天天看点

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