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等,這些其實都是一些不同類型的聯合檔案系統。
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。
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
[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