本文主要討論的是Docker容器(container)的數據共享和傳輸,包括容器之間和容器與宿主機(host)之間兩種情況。以下實驗參考自Sébastien Goasguen撰寫的Docker Cookbook (ISBN 978-1-491-91971-2)第一章Page32~Page36。

具體章節為 * 1.18 Sharing Data in Your Docker Host with Containers * 1.19 Sharing Data Between Containers * 1.20 Copying Data to and from Containers

本文使用的Docker image是alpine,使用命令docker pull alpine即可下載。

Preparation

安裝Docker engine並啓動Daemon(守護進程),具體安裝過程可參考本人撰寫的 Docker Instroduction and Installation on CentOS 7

停止並移除所有容器(container)

成功下載image alpine

[flying@lemp ~]$ docker ps -qa
[flying@lemp ~]$ docker images | grep alpine
alpine                 latest              d7a513a663c1        3 days ago          4.794 MB
[flying@lemp ~]$

Stop and Remove Containers

  • Docker中使用docker ps查看容器信息
    • -a, --all: Show all containers (default shows just running)
    • -q, --quiet: Only display numeric IDs
  • Docker中使用docker stop停止正在運行的容器
  • Docker中使用docker rm移除已經停止運行的容器(一個或多個)
    • -v, --volumes: Remove the volumes associated with the container

根據以上命令 * 停止所有運行的容器: docker stop $(docker ps -qa) * 移除所有容器: docker rm $(docker ps -qa)

Sharing Data Between Docker Host and Containers

Docker Container(容器)和Docker Host(宿主機)之間通過volume進行數據共享。類似於在虛擬機中掛載宿主機磁盤分區,Docker在運行docker run時通過指定選項-vhost volume(主機卷)掛載到容器(container)中,默認是read-write mode(讀寫模式)。

  • docker run: Run a command in a new container
    • -v, --volume=[]: Bind mount a volume

此處假定宿主機的host volume/tmp/data,映射到容器中的/data目錄。

以下按掛載模式,分爲read-write moderead-only mode兩種。

read-write mode

讀寫模式 掛載host volume(主機卷),可在其中創建或刪除文件。

操作過程

#在宿主機查看目錄
[flying@lemp ~]$ cd /tmp/data
bash: cd: /tmp/data: No such file or directory

#創建容器,並掛載主機目錄/tmp/data,映射爲/data
[flying@lemp ~]$ docker run -ti -v /tmp/data:/data alpine /bin/sh
WARNING: IPv4 forwarding is disabled. Networking will not work.
/ # cd /data
/data # ls

#在容器中創建測試文件
/data # echo 'This is created in container' > inside.txt
/data # ls
inside.txt
/data # ls -lh
total 4
-rw-r--r--    1 root     root          29 Apr  5 13:33 inside.txt

#退出容器
/data # exit

#在宿主機切換到/tmp/data/
[flying@lemp ~]$ cd /tmp/data/

#列出目錄中文件
[flying@lemp data]$ ls
inside.txt

#查看文件內容
[flying@lemp data]$ cat inside.txt
This is created in container

#當前容器ID號
[flying@lemp data]$ docker ps -qa
377caea9cb0f
[flying@lemp data]$

read-only mode

只讀模式 掛載host volume(主機卷),無法創建或刪除文件,設置方式爲在映射目錄後添加ro,以冒號:分隔,形如-v /tmp/data:/data:ro

操作過程

#創建新容器,以只讀模式掛載主機目錄/tmp/data,映射爲/data
[flying@lemp ~]$ docker run -ti -v /tmp/data:/data:ro alpine /bin/sh
WARNING: IPv4 forwarding is disabled. Networking will not work.
/ # cd /data
/data # ls
inside.txt

#在容器中嘗試刪除文件
/data # rm inside.txt
rm: remove 'inside.txt'? y
rm: can't remove 'inside.txt': Read-only file system

#在容器中嘗試創建文件
/data # touch newinside.txt
touch: newinside.txt: Read-only file system

#在容器中嘗試創建目錄
/data # mkdir testinside
mkdir: can't create directory 'testinside': Read-only file system
/data # exit

#在宿主機切換到/tmp/data/,並列出文件
[flying@lemp ~]$ cd /tmp/data/ && ls
inside.txt

#所有容器ID號
[flying@lemp data]$ docker ps -qa
96cb78003fca
377caea9cb0f
[flying@lemp data]$

刪除所有容器

[flying@lemp data]$ docker stop $(docker ps -qa)
96cb78003fca
377caea9cb0f
[flying@lemp data]$ docker rm $(docker ps -qa)
96cb78003fca
377caea9cb0f
[flying@lemp data]$

Sharing Data Between Containers

容器間的數據共享是通過命令docker run中的選項--volumes-from實現

  • docker run: Run a command in a new container
    • --volumes-from=[]: Mount volumes from the specified container(s)

當離開容器後,可使用docker inspect查看volume在宿主機的具體位置,通常在目錄/var/lib/docker/volumes/下。對於宿主機,該目錄默認是可讀寫的,且重啓容器後仍存在。

如果刪除容器的同時想刪除volume,可使用docker rm -v * -v, --volumes: Remove the volumes associated with the container

在運行中的容器中運行命令,可通過 * docker exec: Run a command in a running container

操作過程

#創建名爲container1的容器,其volume爲/tmp/data
[flying@lemp ~]$ docker run -ti -v /tmp/data --name container1 alpine /bin/sh
WARNING: IPv4 forwarding is disabled. Networking will not work.
/ # cd /tmp/data

#創建文件container1.txt
/tmp/data # echo 'This is created from container1.' > container1.txt
/tmp/data # ls
container1.txt

#退出容器container1
/tmp/data # exit

#查看當前所有容器信息
[flying@lemp ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
5bfb88721e9a        alpine              "/bin/sh"           53 seconds ago      Exited (0) 13 seconds ago                       container1

#查看容器ID
[flying@lemp ~]$ docker ps -qa
5bfb88721e9a

#通過容器名稱查看容器container1的volume在宿主機的位置
[flying@lemp ~]$ docker inspect -f {{.Mounts}} container1
[{e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76 /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data /tmp/data local  true }]

#通過容器ID查看容器container1的volume在宿主機的位置
[flying@lemp ~]$ docker inspect -f {{.Mounts}} 5bfb88721e9a
[{e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76 /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data /tmp/data local  true }]

#列出目錄中內容
[flying@lemp ~]$ sudo ls /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data
container1.txt

#使用宿主機在該目錄中創建新文件
[flying@lemp ~]$ sudo touch /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data/outside.txt

#啓動容器container1
[flying@lemp ~]$ docker start container1
container1

#以交互模式登錄容器container1
[flying@lemp ~]$ docker exec -ti container1 /bin/sh

#列出目錄/tmp/data/中文件,有外部創建的outside.txt
/ # ls /tmp/data/
container1.txt  outside.txt
/ # exit

#查看當前所有容器ID
[flying@lemp ~]$ docker ps -qa
5bfb88721e9a


#創建名爲container2的容器,其volume來自容器container1
[flying@lemp ~]$ docker run -ti --volumes-from container1 --name container2 alpine /bin/sh
WARNING: IPv4 forwarding is disabled. Networking will not work.
/ # cd /tmp/data/

#列出目錄下文件
/tmp/data # ls
container1.txt  outside.txt

#創建新文件container2.txt
/tmp/data # echo 'This is created from container2.' > container2.txt
/tmp/data # ls
container1.txt  container2.txt  outside.txt
/tmp/data # exit

#當前所有容器信息
[flying@lemp ~]$ docker ps -qa
4cdba963a62a
5bfb88721e9a

#查看容器container1的volume在宿主機中的位置
[flying@lemp ~]$ docker inspect -f {{.Mounts}} container1
[{e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76 /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data /tmp/data local  true }]

#列出目錄下文件
[flying@lemp ~]$ sudo ls /var/lib/docker/volumes/e11abbd34f2608187a68717f2e12fc53311f57d6a6bafe6331c7f97ae72c5c76/_data
container1.txt	container2.txt	outside.txt
[flying@lemp ~]$

刪除所有容器

[flying@lemp ~]$ docker stop $(docker ps -qa)
4cdba963a62a
5bfb88721e9a
[flying@lemp ~]$ docker rm -v $(docker ps -qa)
4cdba963a62a
5bfb88721e9a
[flying@lemp ~]$

Copying Data to and from Containers

宿主機和容器間的數據共享可通過docker run -v掛載並映射volume,也可使用命令docker cp從運行中的容器中複製文件到宿主機,或將宿主機中的文件複製到容器中。

Usage:	docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
	docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Copy files/folders between a container and the local filesystem
Use '-' as the source to read a tar archive from stdin
and extract it to a directory destination in a container.
Use '-' as the destination to stream a tar archive of a
container source to stdout.

  --help               Print usage
  -L, --follow-link    Always follow symbol link in SRC_PATH

操作過程

#創建名爲copysrc的容器
[flying@lemp ~]$ docker run -d --name copysrc alpine sleep 360
WARNING: IPv4 forwarding is disabled. Networking will not work.
2e20d30b6e4176406a746dd91f97aa28e5396e61c6e43a4d9c93c1cdc7490552

#以交互模式登錄容器copysrc
[flying@lemp ~]$ docker exec -ti copysrc /bin/sh
/ # cd /root

#創建文件srcfile.txt
~ # echo 'I love you, my lover.' > srcfile.txt
~ # exit

#退出容器後,使用docker cp將文件srcfile.txt從容器中複製到宿主機/tmp目錄中
[flying@lemp ~]$ docker cp copysrc:/root/srcfile.txt /tmp

#在宿主機/tmp目錄中查看文件
[flying@lemp ~]$ cat /tmp/srcfile.txt
I love you, my lover.

#在宿主機中創建文件/tmp/host.txt
[flying@lemp ~]$ echo 'This is created from host.' > /tmp/host.txt

#將文件/tmp/host.txt複製到容器copysrc中
[flying@lemp ~]$ docker cp /tmp/host.txt copysrc:/root/hosts.txt

#以交互模式登錄容器copysrc,並查看文件
[flying@lemp ~]$ docker exec -ti copysrc /bin/sh
/ # cd /root
~ # ls
hosts.txt    srcfile.txt
~ # cat hosts.txt
This is created from host.
~ # exit

#創建名爲copydest的容器
[flying@lemp ~]$ docker run -d --name copydest alpine sleep 360
WARNING: IPv4 forwarding is disabled. Networking will not work.
5f0649af1bd8b352288971af9b16f03c0f1ea8e30372a376fe0cb026325bf76c

#將文件/tmp/host.txt複製到容器copydest中
[flying@lemp ~]$ docker cp /tmp/host.txt copydest:/root/host.txt
[flying@lemp ~]$ docker exec -ti copydest /bin/sh
/ # cd /root
~ # ls
host.txt

#查看文件內容
~ # cat host.txt
This is created from host.
~ # exit
[flying@lemp ~]$

上文中用到的 Mountssleep 360暫不知其含義

Solved Problems

Mounts該模板來自於docker inspect,json格式數據,具有層級結構,顯示容器底層信息

[flying@lemp ~]$ docker inspect nginx
[
    {
        "Id": "94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b",
        "Created": "2016-04-16T02:27:51.092033965Z",
        "Path": "nginx",
        "Args": [
            "-g",
            "daemon off;"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 3990,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2016-04-16T02:27:53.403871144Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:eb4a127a1188a3e274eb38f079c5e11680b942927956a38b9b6b6c6736e14fd2",
        "ResolvConfPath": "/var/lib/docker/containers/94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b/hostname",
        "HostsPath": "/var/lib/docker/containers/94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b/hosts",
        "LogPath": "/var/lib/docker/containers/94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b/94d76a093e410e4f7ff6458c0614e01708bf74f09edcff08525734c8fe7bf73b-json.log",
        "Name": "/nginx",
        "RestartCount": 0,
        "Driver": "devicemapper",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "ShmSize": 67108864,
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "KernelMemory": 0,
            "Memory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": -1,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null
        },
        "GraphDriver": {
            "Name": "devicemapper",
            "Data": {
                "DeviceId": "558",
                "DeviceName": "docker-8:5-541360073-8d62ebf58d95951f90adad8e40100cd3e42d6636604fcd3e3708487569acae1a",
                "DeviceSize": "10737418240"
            }
        },
        "Mounts": [],
        "Config": {
            "Hostname": "94d76a093e41",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "443/tcp": {},
                "80/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.9.14-1~jessie"
            ],
            "Cmd": [
                "nginx",
                "-g",
                "daemon off;"
            ],
            "Image": "nginx:latest",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {},
            "StopSignal": "SIGTERM"
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "8db15ca1c6ad60da40a4601813b63c9da54faf99cddf30505a59828c5af1c568",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "443/tcp": null,
                "80/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/8db15ca1c6ad",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "b0ba7f666e974388778f2b61883736b52e131fab4e15cd9e049d9b18db1a8abd",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "f3106e0554ade2aaaa3cf60ccc705fdb5752905ef6c4f6aaf3d9c40ba0c1525d",
                    "EndpointID": "b0ba7f666e974388778f2b61883736b52e131fab4e15cd9e049d9b18db1a8abd",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02"
                }
            }
        }
    }
]
[flying@lemp ~]$

獲取具體數據

[flying@lemp ~]$ docker inspect -f '{{ .State.StartedAt }}' nginx
2016-04-16T02:27:53.403871144Z
[flying@lemp ~]$ docker inspect -f '{{ .NetworkSettings.SandboxKey }}' nginx
/var/run/docker/netns/8db15ca1c6ad
[flying@lemp ~]$ docker inspect -f {{.NetworkSettings.SandboxKey}} nginx
/var/run/docker/netns/8db15ca1c6ad
[flying@lemp ~]$

Bibliography

以下參考來自Docker Cookbook


Change Log

  • 2016.04.05 22:57 Tue
    • 初稿完成
  • 2016.04.16 10:34 Sat
    • 添加Solved Problems