0x1 数据卷
数据卷就是在宿主中可以在容器之间进行共享和重用的一系列和文件和文件夹,通过docker run -v命令可以将数据卷挂载到对应的容器目录空间,进行文件读取,容器卷特性如下
数据卷可以在容器之间共享和重用,容器间传递数据将变得高效方便
对数据卷内数据的修改会立马生效,无论是容器内操作还是本地操作
对数据卷的更新不会影响镜像,解耦了应用和数据
卷会一直存在,直到没有容器使用,可以安全地卸载它
添加数据卷的方式有两种,第一种是直接通过命令行挂载,第二种是通过dockerFile添加
首先来说第一种通过命令行挂载的方式,命令如下:
docker run -it -v /宿主机绝对路径目录: /容器内目录 镜像名
[root@localhost share]# docker run -ti -v /share:/data azkaban/custom_centos:dev
[root@723b1c6c0f25 tmp]# cd /data/
[root@723b1c6c0f25 data]# ls
a.txt
[root@723b1c6c0f25 data]# echo "HelloWorld">>a.txt
[root@723b1c6c0f25 data]#
[root@723b1c6c0f25 data]# exit
exit
[root@localhost share]# cd /share/
[root@localhost share]# ls
a.txt
[root@localhost share]# cat a.txt
HelloWorld
HelloWorld
[root@localhost share]#
[root@localhost share]# docker inspect 723b1c6c0f25
[
{
"Id": "723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4",
"Created": "2020-01-23T14:35:55.088049728Z",
"Path": "/bin/sh",
"Args": [
"-c",
"/bin/bash"
],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-01-23T14:35:55.742450742Z",
"FinishedAt": "2020-01-23T14:36:42.981585771Z"
},
"Image": "sha256:10248154fcbd03f95d67e121b79e577ac5bdbb3f62d096ad749d60b068a5eeff",
"ResolvConfPath": "/var/lib/docker/containers/723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4/hostname",
"HostsPath": "/var/lib/docker/containers/723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4/hosts",
"LogPath": "/var/lib/docker/containers/723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4/723b1c6c0f254c4a92ad88a6fb4fa076098421c60f65650a4542a6b2a24572d4-json.log",
"Name": "/nostalgic_black",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/share:/data"
],
"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": false,
"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/05adb969c49381ba6a9f3f77f161776db166e3707b07bcdd3effb9d92f5b4fd3-init/diff:/var/lib/docker/overlay2/2c302ef399e314cb73b7fc149f41960a9a007cfe1c738a9e3a83c583e1e5bfc9/diff:/var/lib/docker/overlay2/d4a0baf19798567e8b48724f2244aac26d7085daed320edb0a554a30e82adf7e/diff:/var/lib/docker/overlay2/5366d9b7b1063ced6d86da7153d7bbbefb1184239658ab00ddadf4f52c8b25b4/diff",
"MergedDir": "/var/lib/docker/overlay2/05adb969c49381ba6a9f3f77f161776db166e3707b07bcdd3effb9d92f5b4fd3/merged",
"UpperDir": "/var/lib/docker/overlay2/05adb969c49381ba6a9f3f77f161776db166e3707b07bcdd3effb9d92f5b4fd3/diff",
"WorkDir": "/var/lib/docker/overlay2/05adb969c49381ba6a9f3f77f161776db166e3707b07bcdd3effb9d92f5b4fd3/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/share",
"Destination": "/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "723b1c6c0f25",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"newpath=/tmp"
],
"Cmd": [
"/bin/sh",
"-c",
"/bin/bash"
],
"Image": "azkaban/custom_centos:dev",
"Volumes": null,
"WorkingDir": "/tmp",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200114",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "919d485b7e7916ccf8390297e7d1ad24f5e75ea7c7ac112f4aad3f393b9f9f0b",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/919d485b7e79",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "91167e5c3554e5d2ce13882d32421cf0c14768bc6809c1cdb6cb67452dc3037e",
"EndpointID": "",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
}
}
]
[root@localhost share]#
这个命令会在宿主机和容器内分别建立两个目录,两个目录是对接的,里面的数据可以共享。如果我们不知道数据卷是否挂载成功时,我们可以通过以下方式来检查数据卷的挂载结果。
docker inspect 容器id
上面的命令可以查看容器的详细情况,命令返回的是JSON格式的字符串,运行命令之后我们在返回的JSON字符串中找到Volumes属性,假如挂载成功的话,Volumes里面显示的绑定结果应该是你在挂载时输入的命令参数 (/宿主机绝对路径目录: /容器内目录 ),如果与你们之前输入的一致的话,证明挂载成功。PS: Volumes里面显示的绑定结果可能有多个,但是只要找到目标结果就可以。挂载之后,当容器停止运行的时候,宿主机上对数据卷做的内容修改是会同步到容器内的。
我们再挂载的时候还可以给数据卷加上权限,假如我们要宿主机只能读取容器的数据卷内容不能修改,我们可以添加只读权限
docker run -it -v /宿主机绝对路径目录 : /容器内目录 :ro 镜像名
实例
[root@localhost share]# docker run -it -v /share:/data:ro azkaban/custom_centos:dev
[root@48d5939954ac tmp]# ls
ks-script-_srt3u3c ks-script-gpqu_kuo
[root@48d5939954ac tmp]# cd /data
[root@48d5939954ac data]# ls
a.txt
[root@48d5939954ac data]# touch b.txt
touch: cannot touch 'b.txt': Read-only file system
[root@48d5939954ac data]#
至于只写的话我们一般不会用到,要么就是读写,要么就是只读,而且我们可以通过docker inspect 来查看容器的volumesRW来查看容器内数据卷的读写权限。
第二种就是利用dockerFile的形式添加
Dockerfile对于docker镜像而言就如同java中某个类的.class文件对应上该类的.java文件。
首先在linux服务器根目录上新建docker文件夹并建立DockerFile文件,使用volume命令(出于可移植可分享的的考虑,用以上 -v /宿主机绝对路径目录 : /容器内目录 的这种方式不能够直接在dockerFile中直接实现,因为宿主机目录是依赖于特定的宿主机的,并不能保证所有的宿主机都存在这样特定的目录)
编写的dockerFile文件如下
FROM 镜像名
VOLUME ["/生成的目录路径"] -- privileged=true
CMD echo "success build"
CMD /bin/bash
相当于命令行: docker run -it -v /宿主机目录路径 : /生成的目录路径
然后我们通过命令行docker build执行我们写好的dockerFile文件(docker build和docker commit两个命令都可以建立docker镜像,docker commit 需要在容器内进行,docker build 不需要)
docker build -f /docker/DockerFile -t 命名空间/镜像名
执行后输入docker images就可以发现自己通过DockerFile所build的镜像,里面有挂载数据卷,那么问题来了宿主机所对应的目录是什么呢?同上,我们可以通过docker inspect来查看当前容器的Volumes,里面会有宿主机的数据卷目录。
0x2 数据卷容器
接数据卷,已经存在一个挂载了数据卷的容器;由于数据卷在容器之前是可以共享的,所以此时如果存在其他容器通过docker run --volumes-from [容器别名]命令挂载到该容器上,则该容器可以被称之为数据卷容器,其主要功能是提供数据卷供其他容器挂载。当数据卷容器宕机后,并不会造成数据卷的回收卸载,数据卷会继续挂载在其他容器中。当全部挂载该数据卷的容器全部宕机后,该数据卷才会卸载。
首先,我们建立父容器
docker run -it - -name parentContainer 镜像名(可以填上面通过dockerFile建立的镜像,里面有挂载容器卷)
然后建立两个子容器继承父容器
docker run -it - -name sonContainer1 --volumes -from parentContainer 镜像名
docker run -it - -name sonContainer2 --volumes -from parentContainer 镜像名
假设我们DockerFile里面定义的容器卷目录为dockerVolume,父容器里面有dockerVolume目录,子容器继承了父容器的dockerVolume,在字容器中的dockerVolume目录作出的修改会同步到父容器的dockerVolume目录上,达到了继承和数据共享的目的。