docker在运行时是基于分层镜像得联合挂载机制来实现的,而在这种分层构建镜像联合挂载并在最上层实现读写层的时候会发现一个特点,所有的有容器中的进程所生成的数据都是被最上面的可写层所保存而这个可写层本身并不属于镜像层而属于当前容器,它属于容器内部的组件。
1、生命周期,Data Volume,存储卷
容器的生命周期随着启动而创建,随着停止而结束(比如我们系统上运行ls,我们在系统上运行ls的命令就相当于在系统上创建了一个进程,这个进程从你输入ls回车开始、输出内容结束,这个就是进程的生命周期,从你创建进程开始到进程退出结束任务完成而终止,在比如我们vim打开编辑器输入文本,系统会运行一个vim进程,当我们输入文本wq保存退出时vim进程终止,容器启动时加上--rm选项容器停止就会被直接删除,但是此前如果这个进程创建了数据默认在可写层,如果你一旦把容器删掉,它此前生成的数据将不存在,为了保存该容器的数据,我们可以将容器的可写层的数据挂载到宿主机,当容器删除数据依然存在,可直接使用数据去创建新的容器,这样数据可以脱离于容器的生命周期而存在,这种机制就叫做存储卷)
2、存储卷是什么,为什么要做存储卷
Docker镜像由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件得只读版本以然存在,只是已经被读写层中该文件得副本所隐藏,此即“写时复制(cow)”机制
关闭并重启容器,其数据不受影响;但删除docker容器,则其更改将会全部丢失
存在问题:
a、存储于联合文件系统中,不易于宿主机访问
b、容器间数据共享不便
c、删除容器数据会丢失
解决方案:“卷(volume)”
卷是容器上的一个或多个目录,此类目录可绕过联合文件系统,与宿主机上的某目录“绑定(关联)”
3、存储卷的类型
a、绑定挂载卷Bind mount volume
绑定挂在卷是指在宿主机上的目录是用户指定的,在容器中的目录也是用户指定的
绑定挂载卷例子:
[root@bogon ~]# mkdir /data/volumes/v1 -p //在宿主机上创建一个挂载卷目录
[root@bogon ~]# docker run --name mbbox5 -it -v /data/volumes/v1:/mydata busybox //运行容器时指定存储卷
/ # ls /mydata //查看目录
[root@bogon ~]# cd /data/volumes/v1 //宿主机进入挂载卷目录
[root@bogon v1]# ls
[root@bogon v1]# echo hello > my.txt //写个文件
容器中查看mydata下面的文件
/ # ls
bin dev etc home mydata proc root sys tmp usr var
/ # cat /mydata/my.txt
hello
/ # exit //退出容器
[root@bogon ~]# docker rm mbbox5 //删除容器去查看存储卷
mbbox5
[root@bogon ~]# cd /data/volumes/v1 //进入挂载卷目录
[root@bogon v1]# cat my.txt //查看文件
hello
我们用现有数据去挂载启动一个新的容器
[root@bogon ~]# docker run --name mbbox7 -it -v /data/volumes/v1:/my/data busybox //用存储卷的目录直接运行新的容器
/ # cd /my/data/
/my/data # ls
my.txt
/my/data # cat my.txt //查看宿主机的文件在容器中是否存在
hello
所以说存储卷可以脱离容器的生命周期而存在
b、docker管理卷 docker
管理卷是指容器中的目录是用户指定的,宿主机上的目录是固定目录下自动生成的
docker管理卷例子:
[root@bogon ~]# docker run --name mbbox5 -it -v /mydata busybox //启动容器时创建存储卷
/ # ls
bin dev etc home mydata proc root sys tmp usr var
[root@bogon ~]# docker container inspect mbbox5 //查看mbbox5的详细信息
[
{
"Id": "cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545",
"Created": "2020-08-10T08:04:42.474604567Z",
"Path": "sh",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 45098,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-08-10T08:04:42.80629229Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:018c9d7b792b4be80095d957533667279843acf9a46c973067c8d1dff31ea8b4",
"ResolvConfPath": "/var/lib/docker/containers/cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545/hostname",
"HostsPath": "/var/lib/docker/containers/cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545/hosts",
"LogPath": "/var/lib/docker/containers/cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545/cd837a3cfaa7d0e7b771c6b41215edce8382c18d838e0eafc04d132290043545-json.log",
"Name": "/mbbox5",
"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": true,
"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/5664a3a82d7c9db1e732cd347dd3dcbe1bf182bfd9b84b1a87eb4b0d0f9ec303-init/diff:/var/lib/docker/overlay2/bb945e3d5f5421c019f8022b69070c014e1d842466a3ee2e7a464ef09494f723/diff",
"MergedDir": "/var/lib/docker/overlay2/5664a3a82d7c9db1e732cd347dd3dcbe1bf182bfd9b84b1a87eb4b0d0f9ec303/merged",
"UpperDir": "/var/lib/docker/overlay2/5664a3a82d7c9db1e732cd347dd3dcbe1bf182bfd9b84b1a87eb4b0d0f9ec303/diff",
"WorkDir": "/var/lib/docker/overlay2/5664a3a82d7c9db1e732cd347dd3dcbe1bf182bfd9b84b1a87eb4b0d0f9ec303/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "volume",
"Name": "d79e92f534c136d505a27725a2373795bfe892daf95ad6b9f9b85b2d3708e5f4",
"Source": "/var/lib/docker/volumes/d79e92f534c136d505a27725a2373795bfe892daf95ad6b9f9b85b2d3708e5f4/_data", //宿主机具体目录
"Destination": "/mydata", //容器的具体目录
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
"Config": {
"Hostname": "cd837a3cfaa7",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"sh"
],
"Image": "busybox",
"Volumes": {
"/mydata": {}
},
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "f3037bba96a253e86ab0ddf9fabeaa2207d672b54afba1c486297065618342f3",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/f3037bba96a2",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "3c95f4051dcbd4b5d9b21135689d718482fd11ff20a5fc36097459b3d27cd3bc",
"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": "c0abba7360378d6a7cf08213748f59c6330f3688c8731c0672069926279c5a6a",
"EndpointID": "3c95f4051dcbd4b5d9b21135689d718482fd11ff20a5fc36097459b3d27cd3bc",
"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
}
}
}
}
]
[root@bogon ~]# cd /var/lib/docker/volumes/d79e92f534c136d505a27725a2373795bfe892daf95ad6b9f9b85b2d3708e5f4/_data //我们cd到宿主机的目录
[root@bogon _data]# vi test.txt //在宿主机的目录中编辑一个文件
[root@bogon _data]# cat test.txt //查看文件
aaa
bbb
ccc
ddd
我们切换至容器内部去查看这个文件
/ # ls
bin dev etc home mydata proc root sys tmp usr var
/ # cat mydata/test.txt
aaa
bbb
ccc
ddd
/mydata # exit //退出docker容器
在宿主机上查看卷是否被删除:
[root@bogon ~]# cat /var/lib/docker/volumes /d79e92f534c136d505a27725a2373795bfe892daf95ad6b9f9b85b2d3708e5f4/_data/text.txt
aaa
bbb
ccc
ddd
注意:这种方式相当于打通了一个通道可以让宿主机和容器之间任意传输文件
4、容器之间使用同一个卷去共享数据
可以共享:NET、IPC、UTS
不能共享:Mount、User、Pid
[root@bogon ~]# mkdir /data/volumes/v1 -p //在宿主机上创建一个挂载卷目录
[root@bogon ~]# cd /data/volumes/v1 //宿主机进入挂载卷目录
[root@bogon v1]# ls
[root@bogon v1]# echo hello > my.txt //写个文件
[root@bogon ~]# docker run --name mbbox7 -it -v /data/volumes/v1:/my/data busybox //运行mbbox7容器
/ # cd /my/data/
/my/data # ls
my.txt
/my/data # cat my.txt
hello
[root@bogon v1]# docker run --name mbbox8 -it --rm --volumes-from mbbox7 busybox //运行mbbox8容器去复制mbbox7容器的卷
/ # ls
bin dev etc home my proc root sys tmp usr var
/ # cd my/data/
/my/data # ls
my.txt
/my/data # cat my.txt
hello
因此这个卷中的数据就可以实现容器共享了