Docker 数据管理
Docker镜像是分层设计
如果将正在运行中的容器修改生成了新的数据,或者修改了现有的一个已经存在的文件内容,
那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层也就是容器的工作目录,此即“写时复制(COW) copy on write”机制。(图)
Docker镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层中。
容器的数据分层
-LowerDir: image 镜像层(镜像本身,只读)
-UpperDir: 容器的上层(读写)
-MergedDir:容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和upperdir 合并给容器使用
-WorkDir: 容器在 宿主机的工作目录
查看指定容器数据分层 [root@localhost7B ~]#docker run -it -d --name centosAAA centos:7 [root@localhost7B ~]#docker inspect centosAAA "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3-init/diff:/var/lib/docker/overlay2/d10e88cc4345cdc7fbcf8c590b0753e9bbe2cb82450083ff5885d390efaa9571/diff", "MergedDir": "/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/merged", "UpperDir": "/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/diff", "WorkDir": "/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/work" }, [root@localhost7B ~]# ll /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/ drwxr-xr-x 3 root root 18 10月 19 10:07 diff -rw-r--r-- 1 root root 26 10月 19 10:07 link -rw-r--r-- 1 root root 57 10月 19 10:07 lower drwxr-xr-x 1 root root 18 10月 19 10:07 merged drwx------ 3 root root 18 10月 19 10:07 work [root@localhost7B ~]# docker exec -it centosAAA bash [root@8b4ef7e70316 /]# dd if=/dev/zero of=/root/testfile bs=1M count=10 #容器根目录 [root@localhost7B ~]# ls /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/merged anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var #窗器数据在宿主机的两个文件夹中都有。 [root@localhost7B ~]# ll /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/merged/root/ -rw-r--r-- 1 root root 10485760 10月 19 10:16 test.img [root@localhost7B ~]# ll /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/diff/root -rw-r--r-- 1 root root 10485760 10月 19 10:16 test.img [root@localhost7B ~]# mount overlay on /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/merged type overlay (rw,relatime, lowerdir=/var/lib/docker/overlay2/l/TET36WKJNUBL74DPKRMHUHZ4OH:/var/lib/docker/overlay2/l/GGC2USNVTX35BYIUC5GPE7RF7G, upperdir=/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/diff, workdir=/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3/work) #删除容器后,所有容器数据目录都随之而删除 [root@localhost7B ~]# docker rm -f centosAAA [root@localhost7B ~]# ls /var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3 ls: cannot access '/var/lib/docker/overlay2/26f288313fbfe6efecbd576740058f4a2a74542853491735bb09828c5025c4a3': No such file or directory
容器数据持久化
如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录
哪些数据需要持久化(图)
左侧是无状态的http请求服务,右侧为有状态
下层为不需要存储的服务,上层为需要存储的部分服务
Docker提供了三种数据卷类型:
1.宿主机数据卷(bind mount): 直接在宿主机的文件系统中但是容器可以访问,这种类型是比较常用的一种类型。
2.命名的数据卷: 由Docker管理的数据卷,创建的容器的时候指定数据卷名称,docker数据卷默认目录为:/var/lib/docker/volumes。
2.匿名数据卷: 由Docker管理的数据卷,创建的容器的时候不需要指定数据卷名称,由Docker随机生成,docker数据卷默认目录为:/var/lib/docker/volumes
3.数据卷容器(Data volume container):间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用
一、宿主机数据卷
数据卷(data volume) 数据卷特点和使用 数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount到容器当中使用 实际生成环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性 数据卷使用场景 -日志输出 -静态web页面 -应用配置文件 -多容器间目录或文件共享 数据卷的特点 -数据卷是目录或者文件,并且可以在多个容器之间共同使用 -对数据卷更改数据在所有容器里面会立即更新。 -数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。 -在容器里面的写入数据不会影响到镜像本身 -依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理 数据覆盖问题 如果挂载一个空的数据卷到容器中的一个非空目录中,那么这个目录下的文件会被复制到数据卷中 如果挂载一个非空的数据卷到容器中的一个目录中,那么容器中的目录会显示数据卷中的数据。 如果原来容器中的目录有数据,那么原始数据会被隐藏掉 数据卷使用方法 docker run [选项] [镜像名] [shell命令] [参数] -v , --volume: 绑定一个或多个卷,将宿主机的目录挂载到容器中。常用于配置文件中,如tomcat nginx配置文件。 -v <宿主机目录或文件>:<容器目录或文件>[:ro] :ro 此项为只读,不写此项默认为可读可写, 宿主机目录或文件要先存在。 -v和dockerfile中的VOLUME两者区别在于,VOLUME挂载到宿主机中/var/lib/docker/volumes/ 是固定的, 而-v 是可自定义路径。 docker rm -v 删除容器, 并删除容器挂载的数据卷: docker官网推荐尽量进行目录挂载,不要进行文件挂载,(如果是单个配置文件,可以使用单个文件挂载)。
案例:nginx设置数据卷
#在宿主机创建容器所使用的目录和测试页面 [root@localhost7B ~]# mkdir /datavolume/test/ [root@localhost7B ~]# mkdir /datavolume/test/testfile/ [root@localhost7B ~]# echo nginx test page > /datavolume/test/index.html #下载镜像 [root@localhost7B ~]# docker pull nginx #零时启动容器查看主页面 [root@localhost7B ~]# docker run -it -d --rm --name nginxA nginx:latest 1efcc86bb29a8f0c54f914d2e0b6e7d7e71c6e80170e16895a9cc16da26b25ea [root@localhost7B ~]# docker exec -it nginxA bash root@1efcc86bb29a:/# cat /usr/share/nginx/html/index.html <!DOCTYPE html> .... .... <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> #引用宿主机的数据卷启动容器,容器中原路径的文件消失 [root@localhost7B ~]# docker run -it -d -v /datavolume/test:/usr/share/nginx/html -p 81:80 --name nginxA nginx a5e51da2466b63eae0b184170a059c7392ea2dd3174a807bc6dd1ebff1e45349 [root@localhost7B ~]# docker exec -it nginxA bash root@a5e51da2466b:/# ls /usr/share/nginx/html/ test index.html root@a5e51da2466b:/# cat /usr/share/nginx/html/index.html nginx test page #测试 [root@localhost7A ~]# curl 192.168.80.110:81 nginx test page #再次启动一个容器引用数据卷 [root@localhost7B ~]# docker run -it -d -v /datavolume/test:/usr/share/nginx/html -p 82:80 --name nginxB nginx #测试 root@8005a09b89ec:/# echo nginxB test >> /usr/share/nginx/html/index.html [root@localhost7A ~]# curl 192.168.80.110:82 nginx test page nginxB test #测试 在宿主机修改数据 [root@localhost7B test]# echo nginx test page >> /datavolume/test/index.html [root@localhost7A ~]# curl 192.168.80.110:82 nginx test page nginxB test nginx test page #只读方法挂载数据卷,并不会对其它容器有影响读写。 #默认数据卷为可读可写,加ro选项,可以实现只读挂载,对于不希望容器修改的数据,比如:配置文件,脚本等,可以用此方式挂载 [root@localhost7B ~]# docker run -it -d -v /datavolume/test:/usr/share/nginx/html:ro -p 83:80 --name nginxC nginx 83f4e4d0f54f575ed0fe1ca8c11b92a160f3a57ad7c789fea4dbb4ba57786da4 [root@localhost7B ~]# docker exec -it nginxC bash root@83f4e4d0f54f:/# echo nginxC test >> /usr/share/nginx/html/index.html bash: /usr/share/nginx/html/index.html: Read-only file system #提示只读 #测试 [root@localhost7A ~]# curl 192.168.80.110:82 nginx test page nginxB test nginx test page 删除容器后,宿主机的数据卷还存在,可继续给新的容器使用。 [root@localhost7B test]# docker rm -f nginxC nginxC [root@localhost7B test]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8005a09b89ec nginx "/docker-entrypoint.…" 21 minutes ago Up 21 minutes 0.0.0.0:82->80/tcp nginxB a5e51da2466b nginx "/docker-entrypoint.…" 25 minutes ago Up 25 minutes 0.0.0.0:81->80/tcp nginxA [root@localhost7B test]# ll /datavolume/test/ -rw-r--r-- 1 root root 44 10月 19 14:55 index.html -rw-r--r-- 1 root root 0 10月 19 11:52 testfile
案例:MySQL使用的数据卷
[root@localhost7B ~]# docker pull mysql:5.7.29
#MYSQL_ROOT_PASSWORD=123456:设置 MySQL 服务 root 用户的密码。
[root@localhost7B ~]# docker run -d -p 3306:3306 --name mysqlA -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.29 9f020be63f2ed202b948505a5c99b35a1f2b3176c95ba21ab59b359ff3b2441e [root@localhost7B ~]# docker exec -it mysqlA bash root@9f020be63f2e:/# cat /etc/issue Debian GNU/Linux 10 \n \l root@9f020be63f2e:/# cat /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql #数据库存放路径 #log-error = /var/log/mysql/error.log # By default we only accept connections from localhost #bind-address = 127.0.0.1 # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 #登录数据库 root@9f020be63f2e:/# mysql -uroot -p123456 -h 127.0.0.1 mysql> show databases; mysql> create database dockerdb; mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | dockedb | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) mysql> exit; #删除容器后,再创建新的容器,数据库信息丢失 [root@localhost7B ~]# docker rm -f mysqlA mysqlA #再次创建容器 [root@localhost7B ~]# docker run -d -p 3306:3306 --name mysqlA -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.29 c051ec22750cdc410477de33bc777c24d655a04581bf65fbeed4af8bbc997221 [root@localhost7B ~]# docker exec -it mysqlA bash #登录数据库 root@c051ec22750c:/# mysql -uroot -p123456 -h127.0.0.1 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.01 sec) #利用数据卷创建容器 [root@localhost7B ~]# mkdir /data/mysql -pv mkdir: 已创建目录 "/data/mysql" [root@localhost7B ~]# docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql --name mysqlB -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.29 3c2fbf90262c0fa48d596748bd0607a186e3aaff8b451ad0911214674465068c [root@localhost7B ~]# [root@localhost7B ~]# docker exec -it mysqlB bash root@3c2fbf90262c:/# root@3c2fbf90262c:/# root@3c2fbf90262c:/# mysql -uroot -p123456 -h127.0.0.1 mysql> create database dockerdb; [root@localhost7B ~]# docker rm -f mysqlB #重新创建新容器,之前数据还在 [root@localhost7B ~]# docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql --name mysqlC -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.29 6f446ca7f565615b776ae34fd29fa4658ed4e5a86b0c0d78f65a2904ef56d1d3 [root@localhost7B ~]# docker exec -it mysqlC bash root@6f446ca7f565:/# mysql -uroot -p123456 -h127.0.0.1 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | dockerdb | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) #宿主机查看数据 [root@localhost7B ~]# ls /data/mysql/ auto.cnf client-cert.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem ca-key.pem client-key.pem ibdata1 ibtmp1 private_key.pem server-key.pem ca.pem dockerdb ib_logfile0 mysql public_key.pem sys
二、命名和匿名数据卷
命名和匿名数据卷 特点: 容器内的文件夹挂载在宿主机的目录/var/lib/docker/volumes/ID /_data 容器内的数据保留 由Docker管理的数据卷,创建的容器的时候指定数据卷名称(不需要指定具体的路径),docker数据卷默认目录为:/var/lib/docker/volumes。
docker volumes :
create 创建卷,可以不用创建,直接随机取名
inspect 显示一个或多个卷的详细信息
ls 列出卷
prune 删除所有未使用的本地卷
rm 删除一个或多个卷
#命名数据卷,直接使用,不用使用create创建
[root@localhost7B ~]# docker run -itd --name nginxA -v nginx-volume:/etc/nginx/ -p 80:80 nginx #匿名数据卷 [root@localhost7B ~]# docker run -itd --name nginxB -v /etc/nginx/ -p 81:80 nginx #查看数据卷 [root@localhost7B ~]# docker volume ls DRIVER VOLUME NAME local 2a1ceaebca0b29d6867d6382e95fce97b80bcb905da025bac1fdfb3d43d8a971 local nginx-volume #使用inspect查看数据卷详细信息 [root@localhost7B ~]# docker volume inspect nginx-volume [ { "CreatedAt": "2022-10-19T16:57:20+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/nginx-volume/_data", "Name": "nginx-volume", "Options": null, "Scope": "local" } ] [root@localhost7B ~]# echo nginx test >> /var/lib/docker/volumes/nginx-volume/_data/testfile [root@localhost7B ~]# [root@localhost7B ~]# ls /var/lib/docker/volumes/nginx-volume/_data/ conf.d fastcgi_params mime.types modules nginx.conf scgi_params testfile uwsgi_params [root@localhost7B ~]# docker exec -it nginxA bash #原数据保留 root@dca3e2bb0de4:/# ls /etc/nginx/ conf.d fastcgi_params mime.types modules nginx.conf scgi_params testfile uwsgi_params #删除命名卷 [root@localhost7B ~]# docker rm -fv nginxA nginxA [root@localhost7B ~]# docker volume ls DRIVER VOLUME NAME local 2a1ceaebca0b29d6867d6382e95fce97b80bcb905da025bac1fdfb3d43d8a971 local nginx-volume #清理数据卷:删除上面创建的容器后会,发现数据卷仍然存在,我们就需要去清理它,不然会占用我们的资源 [root@localhost7B ~]# docker volume prune WARNING! This will remove all local volumes not used by at least one container. Are you sure you want to continue? [y/N] y Deleted Volumes: nginx-volume Total reclaimed space: 9.419kB [root@localhost7B ~]# docker volume ls DRIVER VOLUME NAME local 2a1ceaebca0b29d6867d6382e95fce97b80bcb905da025bac1fdfb3d43d8a971 #删除匿名数据卷,-v参数,同数据一起删除。 [root@localhost7B ~]# docker rm -fv nginxB nginxB [root@localhost7B ~]# docker volume ls DRIVER VOLUME NAME
三、数据卷容器
数据卷容器(Data Volume Containers) 如果需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器(Data Volume Containers)。 数据卷容器 也是一个容器,但是它的目的是专门用来提供数据卷供其他容器挂载。 #在当前环境下,即使把提供卷的容器 Server 删除,已经运行的容器 Client 依然 可以使用挂载的卷,因为容器是通过挂载访问数据的, 但是无法创建新的卷容器 客户端,但是再把卷容器 Server 创建后即可正常创建卷容器 Client,此方式可以 用于线上共享数据目录等环境, 因为即使数据卷容器被删除了,其他已经运行的 容器依然可以挂载使用 #数据卷容器可以作为共享的方式为其他容器提供文件共享,类似于 NFS 共享, 可以在生产中启动一个实例挂载本地的目录, 然后其他的容器分别挂载此容器的目录,即可保证各容器之间的数据一致性。 数据卷容器功能是可以让数据在多个 docker 容器之间共享,即可以让 B 容器 访问 A 容器的内容,而容器 C 也可以访问 A 容器的内容, 即先要创建一个后台 运行的容器作为 Server,用于卷提供,这个卷可以为其他容器提供数据存储服务, 其他使用此卷的容器作为 client 端: 基础镜像 [root@localhost7B ~]# docker pullcentos:7 [root@localhost7B ~]# docker pull nginx [root@localhost7B ~]# mkdir /data/volumesA [root@localhost7B ~]# mkdir /data/volumesB [root@localhost7B ~]# touch /data/volumesA/aaa.txt [root@localhost7B ~]# touch /data/volumesB/bbb.txt #构造容器 [root@localhost7B ~]# docker run -it -d -v /data/volumesA:/mnt:ro -v /data/volumesB:/media -p 81:80 --name volume-server centos:7 5b6dcf6c25b0e987311203481710ef82b0fe076791514bc223c80ada6e13e91d
#查看 [root@localhost7B ~]# docker exec -it volume-server bash [root@5b6dcf6c25b0 /]# ls /mnt/ aaa.txt [root@5b6dcf6c25b0 /]# ls /media/ bbb.txt #构造容器 [root@localhost7B ~]# docker run -d --name web1 -p 88:80 --volumes-from volume-server nginx d367c973677ddd4afaa72d44fba5ddc15bbd3586baf4340d1d7bc36cf0241739 [root@localhost7B ~]# docker run -d --name web2 -p 99:80 --volumes-from volume-server nginx 1347088c3d2c475acee4db0d258baee4961154caa93fe45a8ab4797c0b50d3be #测试 重点说明:宿主机的目录/data/volumesA 挂载到容器volume-server下的/mnt目录,最终还是挂载到web1下的/mnt目录(确保web1有这两个文件夹)。 [root@localhost7B ~]# docker exec -it web1 bash root@d367c973677d:/# echo web1 test >> /media/index.html root@d367c973677d:/# echo web1 test >> /mnt/index.html bash: /mnt/index.html: Read-only file system #重点 web1的来源还是"/data/volumesB", 跟volume-server没有关系,佐证了后面说的删除volume-server容器不影响使用,确保web1容器中有/media 和/mnt在。 [root@localhost7B ~]# docker inspect web1 "Mounts": [ { "Type": "bind", "Source": "/data/volumesB", "Destination": "/media", "Mode": "", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/data/volumesA", "Destination": "/mnt", "Mode": "", "RW": false, "Propagation": "rprivate" } # 关闭卷容器 Server 测试能否启动新容器 [root@localhost7B ~]# docker stop volume-server volume-server #使用原容器正常 [root@localhost7B ~]# docker exec -it web1 bash root@d367c973677d:/# echo testfile > /media/testfile.txt root@d367c973677d:/# exit exit [root@localhost7B ~]# ll /data/volumesB/ bbb.txt index.html testfile.txt [root@localhost7B ~]# ls /data/volumesB/ bbb.txt index.html testfile.txt #创建新容器正常 [root@localhost7B ~]# docker run -d --name web3 -p 77:80 --volumes-from volume-server nginx 3bff2be23170e4a501a6ef4747267a0da1ad0400fcd112cee7f80c8b1a511462 删除卷容器 Server 测试能否启动新容器 [root@localhost7B ~]# docker rm -fv volume-server volume-server #不能启动新容器 [root@localhost7B ~]# docker run -d --name web4 -p 66:80 --volumes-from volume-server nginx docker: Error response from daemon: No such container: volume-server. See 'docker run --help'. #使用原容器正常 [root@localhost7B ~]# docker exec -it web2 bash root@1347088c3d2c:/# ls /mnt/ aaa.txt #再次创建卷容器 [root@localhost7B ~]# docker run -it -d -v /data/volumesA:/mnt:ro -v /data/volumesB:/media -p 81:80 --name volume-server centos:7 667f9f4cd32c4370acd3413e0371d8a424c305fe9b0fe455b25876caf567edae #创建新容器正常 [root@localhost7B ~]# docker run -it -d --name web4 -p 66:80 --volumes-from volume-server nginx 6e06ec1e73b6fed599a4aa33844f4db1fff0801c842b13673cf964e746afc2c5 [root@localhost7B ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e06ec1e73b6 nginx "/docker-entrypoint.…" 17 seconds ago Up 16 seconds 0.0.0.0:66->80/tcp web4 667f9f4cd32c centos:7 "/bin/bash" 26 seconds ago Up 25 seconds 0.0.0.0:81->80/tcp volume-server 3bff2be23170 51086ed63d8c "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 0.0.0.0:77->80/tcp web3 1347088c3d2c 51086ed63d8c "/docker-entrypoint.…" 30 minutes ago Up 30 minutes 0.0.0.0:99->80/tcp web2 d367c973677d 51086ed63d8c "/docker-entrypoint.…" 30 minutes ago Up 30 minutes 0.0.0.0:88->80/tcp web1 总结: 关闭容器卷:原容器使用正常和新容器可以创建 删除容器卷:原容器使用正常和新容器不能创建