目录
前面将讲最重要的就是 volume数据卷,即docker -v参数
一、volume数据卷介绍
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有
用的特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 数据卷默认会一直存在,即使容器被删除
*注意:
数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的数据卷。
二、volume数据卷相关操作
2.1 建立一个数据卷
在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。
例如:
docker run -d -P --name web -v /webapp training/webapp python app.py
从docker --help语法
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
套上面的格式得知:
-P:为随便端口
-v:表示建立一个/webapp,没有冒号,则表示在容器直接创建,如果没有填写主机则表示宿主目录使用docker默认目录/var/lib/docker/volumes ,后面将介绍
python app.py:表示命令
[root@vm82 ~]# docker run -d -P --name web -v /webapp training/webapp python app.py
Unable to find image 'training/webapp:latest' locally
latest: Pulling from training/webapp
Image docker.io/training/webapp:latest uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
e190868d63f8: Pull complete
909cd34c6fd7: Pull complete
0b9bfabab7c1: Pull complete
a3ed95caeb02: Pull complete
10bbbc0fc0ff: Pull complete
fca59b508e9f: Pull complete
e7ae2541b15b: Pull complete
9dd97ef58ce9: Pull complete
a4c1b0cb7af7: Pull complete
Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d
Status: Downloaded newer image for training/webapp:latest
[root@vm82 ~]#
[root@vm82 ~]#
[root@vm82 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7ffe357586c4 training/webapp "python app.py" About a minute ago Up About a minute 0.0.0.0:32768->5000/tcp web
上面生成了32768端口,我试下用浏览器访问看下
*注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。
2.2 volume数据卷删除操作
数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker不会在容器被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。无主的数据卷可能会占据很多空间,要清理会很麻烦。Docker官方正在试图解决这个问题,相关工作的进度可以查看这个PR。
2.3 挂载一个主机目录作为数据卷
使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。如在nginx上映宿主目录到容器中
docker run -p 80:80 --name nginx18 -v /disk1/www/hualinux.com:/www/hualinux.com -d nginx:1.18
上面的命令是加载主机的/disk1/www/hualinux.com目录到容器的/www/hualinux.com目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker会自动为你创建它。
*注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。
Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。
docker run -p 80:80 --name nginx18 -v /disk1/www/hualinux.com:/www/hualinux.com:ro -d nginx:1.18
2.4 查看数据卷的具体信息
[root@vm82 ~]# docker inspect --help
Usage: docker inspect [OPTIONS] NAME|ID [NAME|ID...]
Return low-level information on Docker objects
Options:
-f, --format string Format the output using the given Go template
-s, --size Display total file sizes if the type is container
--type string Return JSON for specified type
如查看上第那个python的信息
[root@vm82 ~]# docker inspect web
[
{
"Id": "7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a",
"Created": "2020-07-06T13:24:10.322685191Z",
"Path": "python",
"Args": [
"app.py"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 6165,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-07-06T13:24:14.351456332Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:6fae60ef344644649a39240b94d73b8ba9c67f898ede85cf8e947a887b3e6557",
"ResolvConfPath": "/var/lib/docker/containers/7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a/hostname",
"HostsPath": "/var/lib/docker/containers/7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a/hosts",
"LogPath": "/var/lib/docker/containers/7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a/7ffe357586c40db522f27d5194f880cbddbb891873c1d4abc62a4a1d1bc3bc8a-json.log",
"Name": "/web",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": true,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/8a768871773ada9b038358e4f829bf249dfdc3798908cec4e08b5c50013fdc7e-init/diff:/var/lib/docker/overlay2/14f3187f70c981778dc81068d1541255a9cadece3486ad64faa69057bbb6eae3/diff:/var/lib/docker/overlay2/78f4d01cfe870a2a8019fdec6419dbf51f4fd46de7226b19f59c6fcc23593183/diff:/var/lib/docker/overlay2/27e213f0140115bf6cd80be0bd25ff9191f60f4ce8365607b47c73ce441ce33f/diff:/var/lib/docker/overlay2/d7a7735484c126efa3f6ea0584f5839dc4657d82439dff42027c85dea626619d/diff:/var/lib/docker/overlay2/6a230adea32f0d9c25f872f6de6cdb327e95fddb1d3f077a13ade56a8d730a98/diff:/var/lib/docker/overlay2/cc2fe0423b2d12b6c5cf8b55efc4112afcc60c184862a29c8dcbe6042f77ce1d/diff:/var/lib/docker/overlay2/7602cfb2526dfb82811e5cb5e031c2e2771fb56448e84bd67fe7c769244985c4/diff:/var/lib/docker/overlay2/19ea19c0c8a266d437461cf74def4caaabf4deac32755e43312d95db84a8f283/diff:/var/lib/docker/overlay2/a7bb25e8db56b94f7ebe1d73d61f56c0785c520389348a527c1c9066eccbcd8a/diff:/var/lib/docker/overlay2/bf5a3c16b23f2edcfde9d8635617cca18d2692fd0850d8c1f7cda111b8ee6ea4/diff:/var/lib/docker/overlay2/ec4610b550b22d2114b300b9563fb252376d04d67d9f2abf9aeab5b39089d420/diff:/var/lib/docker/overlay2/8f402e208eafd1021b5c88158134a50039d1b19e7c5bcbadfca8df306bac4760/diff:/var/lib/docker/overlay2/b044d5eb9306c16afbc87cfea27936baeb649c71f76f039a9f98917da6d92c76/diff",
"MergedDir": "/var/lib/docker/overlay2/8a768871773ada9b038358e4f829bf249dfdc3798908cec4e08b5c50013fdc7e/merged",
"UpperDir": "/var/lib/docker/overlay2/8a768871773ada9b038358e4f829bf249dfdc3798908cec4e08b5c50013fdc7e/diff",
"WorkDir": "/var/lib/docker/overlay2/8a768871773ada9b038358e4f829bf249dfdc3798908cec4e08b5c50013fdc7e/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "volume",
"Name": "ea6482dd6a35a517f9b99ff94fd5f2c7df36a77a86a9ce5013436c29cf6f7939",
"Source": "/var/lib/docker/volumes/ea6482dd6a35a517f9b99ff94fd5f2c7df36a77a86a9ce5013436c29cf6f7939/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
"Config": {
"Hostname": "7ffe357586c4",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"5000/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"python",
"app.py"
],
"Image": "training/webapp",
"Volumes": {
"/webapp": {}
},
"WorkingDir": "/opt/webapp",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "2cdd16ae8bcc0cfe6589fcd8dc06bcd3af731219a0143c8e1f5ed8514990e75b",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"5000/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "32768"
}
]
},
"SandboxKey": "/var/run/docker/netns/2cdd16ae8bcc",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "139020958d68a9869b70055a4bf216095d47fc7fa440078d3877a89337b00a9a",
"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": "86c1cd603f1de999938c9dd2f9605291a183e2c6880054137c55f29d1e026559",
"EndpointID": "139020958d68a9869b70055a4bf216095d47fc7fa440078d3877a89337b00a9a",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
在输出的内容中找到其中和数据卷相关的部分,可以看到所有的数据卷都是创建在主机的 /var/lib/docker/volumes/下面的
"Mounts": [
{
"Type": "volume",
"Name": "ea6482dd6a35a517f9b99ff94fd5f2c7df36a77a86a9ce5013436c29cf6f7939",
"Source": "/var/lib/docker/volumes/ea6482dd6a35a517f9b99ff94fd5f2c7df36a77a86a9ce5013436c29cf6f7939/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
2.5 挂载一个本地主机文件作为数据卷
-v 标记也可以从主机挂载单个文件到容器中
docker run --name nginx --rm -it -v ~/.bash_history:/.bash_history nginx /bin/bash
这样就可以记录在容器输入过的命令了
*注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed --inplace,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。
三、数据卷容器
如果你有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。数据卷容器,其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载。
首先,创建一个名为 dbdata 的数据卷容器:
#该容器运行一下就会自动停止,这是正常的
docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres
然后,在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷
docker run -d --volumes-from dbdata --name db1 training/postgres
docker run -d --volumes-from dbdata --name db2 training/postgres
设置完之后你,你可以进入bd1的容器在/dbdata里面建立一个文件,再去db2查看结果是一样的就像NFS挂载一样。
[root@vm82 ~]# docker exec -it db1 /bin/bash
root@988d51ca3c89:/# cd /dbdata/
root@988d51ca3c89:/dbdata# echo 'this is db1'>db1.txt
root@988d51ca3c89:/dbdata# exit
exit
[root@vm82 ~]# docker exec -it db2 cat /dbdata/db1.txt
this is db1
可以使用超过一个的 --volumes-from 参数来指定从多个容器挂载不同的数据卷。 也可以从其他已经挂载了数据卷的容器来级联挂载数据卷。
docker run -d --name db3 --volumes-from db1 training/postgres
*注意:使用 --volumes-from 参数所挂载数据卷的容器自己并不需要保持在运行状态。
如果删除了挂载的容器(包括 dbdata、db1 和 db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用 docker rm -v 命令来指定同时删除关联的容器。 这可以让用户在容器之间升级和移动数据卷。
四、利用数据卷容器来备份、恢复、迁移数据卷
4.1 备份
首先使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器,并从主机挂载当前目录到容器的 /backup 目录。命令如下:
[root@vm82 ~]# docker run --volumes-from dbdata -v $(pwd):/backup centos:6.8 tar cvf /backup/backup.tar /dbdata
tar: Removing leading `/' from member names
/dbdata/
/dbdata/db1.txt
[root@vm82 ~]# tar -xf backup.tar
[root@vm82 ~]# cat dbdata/db1.txt
this is db1
容器启动后,使用了tar命令来将dbdata卷备份为容器中/backup/backup.tar文件,也就是主机当前目录下的名为 backup.tar 的文件
4.2 恢复
如果要恢复数据到一个容器,首先创建一个带有空数据卷的容器 dbdata2。
docker run -v /dbdata --name dbdata2 centos:7 /bin/bash
然后创建另一个容器,挂载 dbdata2 容器卷中的数据卷,并使用 untar 解压备份文件到挂载的容器卷中。
[root@vm82 ~]# docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
27144aa8f1b9: Pull complete
Digest: sha256:be3c11fdba7cfe299214e46edc642e09514dbb9bbefcd0d3836c05a1e0cd0642
Status: Downloaded newer image for busybox:latest
dbdata/
dbdata/db1.txt
为了查看/验证恢复的数据,可以再启动一个容器挂载同样的容器卷来查看
[root@vm82 ~]# docker run --volumes-from dbdata2 busybox /bin/ls /dbdata
db1.txt