1. 数据卷介绍

Docker将运用与运行的环境打包形成容器运行, Docker 容器产生的数据,如果不通过 docker commit 生成新的镜像,使得数据做为镜像的一部分保存下来, 那么当容器删除后,数据自然也就没有了。 为了能保存数据在 Docker 中我们使用卷。

卷就是目录或文件,存在于一个或多个容器中,由 Docker 挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过联合文件系统提供一些用于持续存储或共享数据的特性:。

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。

数据卷的特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

2. 简单使用

运行容器,指定挂载数据卷命令:

  1. docker run -it -v 主机目录:容器目录
  2. # 通过若干个-v可实现挂载多个目录!
  3. # 这个:冒号读作映射,一般都是主机->容器,下文的端口亦是如此

将主机目录 / home/test 和容器 / home 建立数据卷,首先在容器目录下创建 test.java 文件,再去主机目录下查看是否有该文件。
Docker容器数据卷 - 图1

查看容器对应元数据docker inspect 容器id,可以在 Mounts 节点查看建立的数据卷信息。
Docker容器数据卷 - 图2

即使容器停止运行或者容器删除,仍然可以实现数据同步,本地的数据卷不会丢失。

以后只需要在本地修改文件即可,容器内会自动同步!

3. MySQL 容器建立数据卷同步数据

  1. # 获取镜像
  2. docker pull mysql:5.7

在 Linux 下的 MySQL 默认的数据文档存储目录为 /var/lib/mysql,默认的配置文件的位置 /etc/mysql/conf.d,为了确保 MySQL 镜像或容器删除后,造成的数据丢失,下面建立数据卷保存 MySQL 的数据和文件。

  1. # 运行容器
  2. docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
  3. # -d 后台运行
  4. # -p 端口映射
  5. # -v 卷挂载
  6. # -e 环境配置(mysql密码)
  7. # --name 容器名字

启动成功后,在本地通过Navicat或sqlyog连接测试一下。
本地连接到服务器的3310端口,3310和容器内的3306端口映射。

如果使用配置的密码连接 mysql 服务失败,原因很大可能是本机挂载的配置文件中已有文件, 将容器中的配置给覆盖了, 我们将相应的本机文件中的文件配置删除即可.

4. 常用命令

(1)创建数据卷

  1. docker volume create my-vol

(2)查看所有的数据卷

  1. $ docker volume ls
  2. local my-vol

(3)查看指定数据卷的信息

  1. $ docker volume inspect my-vol
  2. [
  3. {
  4. "Driver": "local",
  5. "Labels": {},
  6. "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
  7. "Name": "my-vol",
  8. "Options": {},
  9. "Scope": "local"
  10. }
  11. ]

(4)删除数据卷 docker volume rm ...

  1. $ docker volume rm my-vol

(5)删除容器之时删除相关的卷

  1. $ docker rm -v ...

数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷 。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

无主的数据卷可能会占据很多空间,要清理请使用以下命令

  1. $ docker volume prune

(5)使用 —mount 创建数据卷
挂载一个主机目录作为数据卷。使用 —mount 标记可以指定挂载一个本地主机的目录到容器中去。

  1. $ docker run -d -P \
  2. --name web \
  3. --mount type=bind,source=/src/webapp,target=/opt/webapp \
  4. training/webapp
  5. python app.py

上面的命令挂载主机的 / src/webapp 目录到容器的 / opt/webapp 目录。用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

Docker 挂载主机目录的默认权限是读写 ,用户也可以通过添加 readonly 参数指定为只读 。

  1. $ docker run -d -P \
  2. --name web \
  3. --mount type=bind,source=/src/webapp,target=/opt/webapp,readonly \
  4. training/webapp \
  5. python app.py
  6. 加了readonly之后,就挂载为只读了。如果你在容器内/src/webapp目录新建文件,会显示如下错误
  7. /src/webapp
  8. touch: new.txt: Read-only file system

5. 具名挂载和匿名挂载

5.1 匿名挂载

匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,即-v 容器内路径。这样对应映射的主机路径就是默认的路径 /var/lib/docker/volumes/ 中自动生成一个随机命名的文件夹。

如下运行并匿名挂载 Nginx 容器:

  1. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -d -P --name nginx01 -v /etc/nginx nginx
  2. d3a27b969d122d5516cac75e99b17dff7aaaf1e0c042385c6b05990053f1259

查看所有的数据卷 volume 的情况, VOLUME NAME 这里的值是真实存在的目录(匿名的)。

  1. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker volume ls
  2. DRIVER VOLUME NAME
  3. local 0cd45ab893fc13971219ac5127f9c0b02491635d76d94183b0261953bdb52d26
  4. local 668a94251e562612880a2fdb03944d67d1acdbbdae6ef7c94bee8685644f2956
  5. local e605f3dc4bf11ab693972592b55fb6911e5bf2083425fd58869c5f574998a09a

5.2 具名挂载

具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在 Docker 指定的默认数据卷路径下的,即-v 卷名:容器内路径。通过docker volume ls命令可以查看当前数据卷的目录情况。

  1. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
  2. 4ceaff19e5275dcd3014a8e7a8af618f7f7ce0da18d605c7c41a8653e78bf912
  3. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker volume ls
  4. DRIVER VOLUME NAME
  5. local 0cd45ab893fc13971219ac5127f9c0b02491635d76d94183b0261953bdb52d26
  6. local 668a94251e562612880a2fdb03944d67d1acdbbdae6ef7c94bee8685644f2956
  7. local e605f3dc4bf11ab693972592b55fb6911e5bf2083425fd58869c5f574998a09a
  8. local juming-nginx # 1行的juming前没有/,说明不是绝对路径

查看指定的数据卷信息的命令:docker volume inspect 数据卷名称

  1. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker volume inspect juming-nginx
  2. [
  3. {
  4. "CreatedAt": "2020-12-29T22:40:25+08:00",
  5. "Driver": "local",
  6. "Labels": null,
  7. "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
  8. "Name": "juming-nginx",
  9. "Options": null,
  10. "Scope": "local"
  11. }
  12. ]

可以看到主机数据卷挂载在 /var/lib/docker/volumes/juming-nginx/_data

Docker 所有的数据卷,没有指定目录的情况下,默认在 /var/lib/docker/volumes/ 目录下

大多数情况下使用具名挂载。


5.3 匿名挂载,具名挂载,指定路径挂载的命令区别

  1. -v 容器内路径 # 匿名挂载
  2. -v 卷名:容器内路径 # 具名挂载
  3. -v /宿主机路径:容器内路径 # 指定路径挂载

通过-v 容器内路径:ro || rw改变读写权限:

  • ro —— readonly 只读。设置了只读则只能操作宿主机的路径,不能操作容器中的对应路径。
  • rw —— readwrite 可读可写。 ```bash docker run -d -P —name nginx02 -v juming-nginx:/etc/nginx:ro nginx

docker run -d -P —name nginx02 -v juming-nginx:/etc/nginx:rw nginx

  1. <a name="da31c778"></a>
  2. ## 6. Dockerfile 中设置数据卷
  3. 我们可以在 Dockerfile 中使用 VOLUME 指令来给镜像添加一个或多个数据卷。
  4. 下面使用 Dockerfile 构建一个新的镜像,dockerfile01 文件的内容,匿名挂载了 volume01 和 volume02 两个目录:
  5. ```bash
  6. # Dockerfile
  7. FROM centos
  8. VOLUME ["volume01","volume02"]
  9. CMD echo "----end----"
  10. CMD /bin/bash

执行构建镜像

  1. [root@iZwz99sm8v95sckz8bd2c4Z docker-test-volume]# docker build -f /home/docker-test-volume/dockerfile01 -t ethan/centos:1.0 .
  2. Sending build context to Docker daemon 2.048kB
  3. Step 1/4 : FROM centos
  4. ---> 300e315adb2f
  5. Step 2/4 : VOLUME ["/volume01","/volume02"]
  6. ---> Running in 215ef28fd5a6
  7. Removing intermediate container 215ef28fd5a6
  8. ---> f506ddf133d2
  9. Step 3/4 : CMD echo "----end----"
  10. ---> Running in 62a1c4b9dc7b
  11. Removing intermediate container 62a1c4b9dc7b
  12. ---> bbea81a6e94e
  13. Step 4/4 : CMD /bin/bash
  14. ---> Running in 245d239f3776
  15. Removing intermediate container 245d239f3776
  16. ---> 1df90e6fd790
  17. Successfully built 1df90e6fd790
  18. Successfully tagged ethan/centos:1.0
  19. [root@iZwz99sm8v95sckz8bd2c4Z docker-test-volume]# docker images
  20. REPOSITORY TAG IMAGE ID CREATED SIZE
  21. ethan/centos 1.0 1df90e6fd790 13 minutes ago 209MB
  22. mytomcat 1.0 f189aac861de 25 hours ago 653MB
  23. mysql 5.7 f07dfa83b528 7 days ago 448MB
  24. tomcat latest feba8d001e3f 11 days ago 649MB
  25. nginx latest ae2feff98a0c 13 days ago 133MB
  26. centos latest 300e315adb2f 3 weeks ago 209MB
  27. portainer/portainer latest 62771b0b9b09 5 months ago 79.1MB
  28. elasticsearch 7.6.2 f29a1ee41030 9 months ago 791MB

完成镜像的生成后,启动自己生成的容器

  1. [root@iZwz99sm8v95sckz8bd2c4Z docker-test-volume]# docker run -it 1df90e6fd790 /bin/bash
  2. [root@828d43dba78e /]# ls -l
  3. total 56
  4. lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
  5. drwxr-xr-x 5 root root 360 Dec 29 15:41 dev
  6. drwxr-xr-x 1 root root 4096 Dec 29 15:41 etc
  7. drwxr-xr-x 2 root root 4096 Nov 3 15:22 home
  8. lrwxrwxrwx 1 root root 7 Nov 3 15:22 lib -> usr/lib
  9. lrwxrwxrwx 1 root root 9 Nov 3 15:22 lib64 -> usr/lib64
  10. drwx------ 2 root root 4096 Dec 4 17:37 lost+found
  11. drwxr-xr-x 2 root root 4096 Nov 3 15:22 media
  12. drwxr-xr-x 2 root root 4096 Nov 3 15:22 mnt
  13. drwxr-xr-x 2 root root 4096 Nov 3 15:22 opt
  14. dr-xr-xr-x 111 root root 0 Dec 29 15:41 proc
  15. dr-xr-x--- 2 root root 4096 Dec 4 17:37 root
  16. drwxr-xr-x 11 root root 4096 Dec 4 17:37 run
  17. lrwxrwxrwx 1 root root 8 Nov 3 15:22 sbin -> usr/sbin
  18. drwxr-xr-x 2 root root 4096 Nov 3 15:22 srv
  19. dr-xr-xr-x 13 root root 0 Dec 29 15:41 sys
  20. drwxrwxrwt 7 root root 4096 Dec 4 17:37 tmp
  21. drwxr-xr-x 12 root root 4096 Dec 4 17:37 usr
  22. drwxr-xr-x 20 root root 4096 Dec 4 17:37 var
  23. drwxr-xr-x 2 root root 4096 Dec 29 15:41 volume01
  24. drwxr-xr-x 2 root root 4096 Dec 29 15:41 volume02

可以看到自动挂载的数据卷目录。下面查看对应宿主机的数据卷目录

  1. [root@iZwz99sm8v95sckz8bd2c4Z docker-test-volume]# docker inspect 828d43dba78e
  2. [
  3. {
  4. "Id": "828d43dba78ecaa10e7e57c1091ee8cf3581dae88187aa04163946dfc86bfd76",
  5. "Created": "2020-12-29T15:41:29.297091075Z",
  6. "Path": "/bin/bash",
  7. "Args": [],
  8. "State": {
  9. "Status": "running",
  10. "Running": true,
  11. "Paused": false,
  12. "Restarting": false,
  13. "OOMKilled": false,
  14. "Dead": false,
  15. "Pid": 1719,
  16. "ExitCode": 0,
  17. "Error": "",
  18. "StartedAt": "2020-12-29T15:41:34.107755138Z",
  19. "FinishedAt": "0001-01-01T00:00:00Z"
  20. },
  21. ...
  22. # 重点
  23. "Mounts": [
  24. {
  25. "Type": "volume", # 数据卷1
  26. "Name": "c51c75b11a69c526a97a07b03ce2ec74d8e77aa150b736291777c1c204a8aecc",
  27. "Source": "/var/lib/docker/volumes/c51c75b11a69c526a97a07b03ce2ec74d8e77aa150b736291777c1c204a8aecc/_data",
  28. "Destination": "volume01",
  29. "Driver": "local",
  30. "Mode": "",
  31. "RW": true,
  32. "Propagation": ""
  33. },
  34. {
  35. "Type": "volume", # 数据卷2
  36. "Name": "1ae2d34c56c4352ba906b5bc261706b235a07ca2b3c9df6c612bb4380db8983f",
  37. "Source": "/var/lib/docker/volumes/1ae2d34c56c4352ba906b5bc261706b235a07ca2b3c9df6c612bb4380db8983f/_data",
  38. "Destination": "volume02",
  39. "Driver": "local",
  40. "Mode": "",
  41. "RW": true,
  42. "Propagation": ""
  43. }
  44. ],

可以看到 Mounts 下有宿主机的挂载目录。因为 dockerfile 中没有指定宿主机目录,所以属于匿名挂载,在 /var/lib/docker/volumes/ 目录下生成了随机命名的路径。

7. 容器数据卷

容器数据卷是指建立数据卷,来同步多个容器间的数据,实现容器间的数据同步。
image.png

首先启动容器 1,volume01、volume02 为挂载目录。

  1. [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it --name cnetos01 ethan/centos:1.0
  2. [root@731d53b8c3d5 /]# ls
  3. bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02

然后启动容器 2,通过参数--volumes-from,设置容器 2 和容器 1 建立数据卷挂载关系。

  1. [root@iZwz99sm8v95sckz8bd2c4Z /]# docker run -it --name centos02 --volumes-from cnetos01 ethan/centos:1.0
  2. [root@7f90d4147511 /]# ls -l
  3. total 56
  4. lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin
  5. drwxr-xr-x 5 root root 360 Dec 30 14:58 dev
  6. drwxr-xr-x 1 root root 4096 Dec 30 14:58 etc
  7. drwxr-xr-x 2 root root 4096 Nov 3 15:22 home
  8. lrwxrwxrwx 1 root root 7 Nov 3 15:22 lib -> usr/lib
  9. lrwxrwxrwx 1 root root 9 Nov 3 15:22 lib64 -> usr/lib64
  10. drwx------ 2 root root 4096 Dec 4 17:37 lost+found
  11. drwxr-xr-x 2 root root 4096 Nov 3 15:22 media
  12. drwxr-xr-x 2 root root 4096 Nov 3 15:22 mnt
  13. drwxr-xr-x 2 root root 4096 Nov 3 15:22 opt
  14. dr-xr-xr-x 108 root root 0 Dec 30 14:58 proc
  15. dr-xr-x--- 2 root root 4096 Dec 4 17:37 root
  16. drwxr-xr-x 11 root root 4096 Dec 4 17:37 run
  17. lrwxrwxrwx 1 root root 8 Nov 3 15:22 sbin -> usr/sbin
  18. drwxr-xr-x 2 root root 4096 Nov 3 15:22 srv
  19. dr-xr-xr-x 13 root root 0 Dec 29 15:41 sys
  20. drwxrwxrwt 7 root root 4096 Dec 4 17:37 tmp
  21. drwxr-xr-x 12 root root 4096 Dec 4 17:37 usr
  22. drwxr-xr-x 20 root root 4096 Dec 4 17:37 var
  23. drwxr-xr-x 2 root root 4096 Dec 30 14:54 volume01
  24. drwxr-xr-x 2 root root 4096 Dec 30 14:54 volume02

首先在容器 2 中的 volume01 中添加文件

  1. [root@7f90d4147511 /]# cd volume01
  2. [root@7f90d4147511 volume01]# touch test.java
  3. [root@7f90d4147511 volume01]# ls
  4. test.java

然后就可以看到容器 1 的文件也会添加上了

下面同步两个 MySQL 的数据库和配置文件,与上面的操作相同,首先建立数据卷,然后给另一个 MySQL 容器建立容器数据卷挂载,示例:

  1. [root@iZwz99sm8v95sckz8bd2c4Z home]# docker run -d -p 6603:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
  2. [root@iZwz99sm8v95sckz8bd2c4Z home]# docker run -d -p 6604:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7

假设docker02和docker03都从docker01同步容器卷,则删除docker01后,02与03中的容器卷依然存在。
数据卷容器的生命周期一直持续到没有容器使用为止!但你一旦持久化到了本地,本地是数据不会被删除。
image.png