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。
[root@izuf682lz6444cynn96up0z ~]# 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
root@n209-h246:/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