天天看點

Docker學習筆記(三)Image & ContainerImages & layersContainer and layersCopy-on-writeBuild image DemoReferences

Images & layers

Docker image是由一組上下有序的隻讀layer建構出來的,當用Dockerfile建構image時,Dockerfile中每個指令就代表着一個layer。每一個layer都隻包含了相比于下一層的layer不同的内容,最後所有的layer堆疊起來就是一個新的image。

以下面這個Dockerfile為例,它以Ubuntu15.04的image為基礎image,加入一個App後建構出一個新的image:

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
           

如下圖,從image啟動一個container時,docker将會在這些隻讀layer的頂部添加一個writable layer ,也叫作container layer,container運作時的所有修改都會寫到這個writable layer中,包括建立新檔案、修改存在的檔案、删除檔案等。Docker用storage driver負責處理各個layer之間的互動,docker目前支援的storage driver有overlay、aufs、vfs等,這些其實都是一些不同類型的聯合檔案系統。

Docker學習筆記(三)Image & ContainerImages & layersContainer and layersCopy-on-writeBuild image DemoReferences

Container and layers

Container和image之間最大的不同就是頂層的那個writable layer。Container删除後,這個writable  layer也會被删除,底層的image不會發生任何變化。因為所有的修改都寫到了writable layer,是以多個container可以共享部分甚至全部的隻讀layer(底層image),顯然從同一image啟動的container共享所有的隻讀layer,而從Ubuntu15.04這個image啟動的container和從app這個image啟動的container可以共享Ubuntu15.04的所有隻讀layer。

Docker學習筆記(三)Image & ContainerImages & layersContainer and layersCopy-on-writeBuild image DemoReferences

Copy-on-write

寫時複制,顧名思義,在寫資料的時候再對源資料進行拷貝,它将共享和拷貝檔案的效率最大化。在container中,如果要修改一個在隻讀layer中已經存在的檔案,這個檔案會先被拷貝到writable layer,然後将修改寫入writable layer中的檔案副本。在build image的過程中,某一層要layer修改底層layer的檔案,流程一樣如此。

Build image Demo

Build image

.

└── image1

    ├── Dockerfile

    ├── app.py

    └── requirements.txt

Dockerfile

# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]
           

app.py

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)
           

requirements.txt

Flask
Redis
           

開始build這個image(cd到image1這個目錄build)

M310144TCG8WP:image1 hunk.he$ docker build -t friendlyhello .
Sending build context to Docker daemon   5.12kB
Step 1/7 : FROM python:2.7-slim
2.7-slim: Pulling from library/python
802b00ed6f79: Pull complete 
10b2d5f7ed73: Pull complete 
1073a127cf89: Pull complete 
a5e6e29410fb: Pull complete 
Digest: sha256:f798e54ff77950a44ac7b6376f00bb222087e5b84a4b5ada8d79a6d52bb5708e
Status: Downloaded newer image for python:2.7-slim
 ---> 14dad3ead5f4
Step 2/7 : WORKDIR /app
 ---> Running in 5e55b4535fa7
Removing intermediate container 5e55b4535fa7
...
Step 7/7 : CMD ["python", "app.py"]
 ---> Running in 731fe4a7a643
Removing intermediate container 731fe4a7a643
 ---> d00096296a21
Successfully built d00096296a21
Successfully tagged friendlyhello:latest
           

用做好的image運作container

M310144TCG8WP:image1 hunk.he$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
friendlyhello       latest              d00096296a21        14 minutes ago      132MB
python              2.7-slim            14dad3ead5f4        3 hours ago         120MB
hello-world         latest              4ab4c602aa5e        4 weeks ago         1.84kB
ubuntu              latest              cd6d8154f1e1        4 weeks ago         84.1MB
M310144TCG8WP:image1 hunk.he$ docker run -p 4000:80 friendlyhello
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
           

Push image to docker hub

首先在https://hub.docker.com上注冊并建立自己的repository,并login到docker hub。

[[email protected] ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: hebostary
Password: 
Login Succeeded
           

給本地image打上tag

hebostary是docker hub上的username,gohead是repository的name,demo1是tag。

M310144TCG8WP:image1 hunk.he$ docker tag friendlyhello hebostary/gohead:demo1
M310144TCG8WP:image1 hunk.he$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hebostary/gohead    demo1               d00096296a21        34 hours ago        132MB
friendlyhello       latest              d00096296a21        34 hours ago        132MB
           

push到遠端repository

M310144TCG8WP:image1 hunk.he$ docker push hebostary/gohead:demo1
The push refers to repository [docker.io/hebostary/gohead]
69f86992535d: Pushed 
47c126cf49af: Mounted from library/python 
demo1: digest: sha256:969c4f12d7c1d9e3f167498e1779aceefc631a158ec4e18730d16f5602569d03 size: 1787
           

登入到docker hub上檢視image

Docker學習筆記(三)Image &amp; ContainerImages &amp; layersContainer and layersCopy-on-writeBuild image DemoReferences

到另一台docker上使用image

[email protected]:/home/hunk# docker run -p 4000:80 hebostary/gohead:demo1
Unable to find image 'hebostary/gohead:demo1' locally
demo1: Pulling from hebostary/gohead
802b00ed6f79: Pull complete 
...
658889f7b573: Pull complete 
Digest: sha256:969c4f12d7c1d9e3f167498e1779aceefc631a158ec4e18730d16f5602569d03
Status: Downloaded newer image for hebostary/gohead:demo1
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
           

docker的宗旨就是一次build然後可以到處運作,通過這個demo可以體會到其強大了。

相關的指令

docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode
docker container ls                                # List all running containers
docker container ls -a             # List all containers, even those not running
docker container stop <hash>           # Gracefully stop the specified container
docker container kill <hash>         # Force shutdown of the specified container
docker container rm <hash>        # Remove specified container from this machine
docker container rm $(docker container ls -a -q)         # Remove all containers
docker image ls -a                             # List all images on this machine
docker image rm <image id>            # Remove specified image from this machine
docker image rm $(docker image ls -a -q)   # Remove all images from this machine
docker login             # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag  # Tag <image> for upload to registry
docker push username/repository:tag            # Upload tagged image to registry
docker run username/repository:tag                   # Run image from a registry
           

References

https://docs.docker.com/get-started/part2/

https://docs.docker.com/storage/storagedriver/#images-and-layers