Docker入门到精通《数据卷》

210 阅读9分钟

引言

数据卷是一种持久化的解决方案,用于存储容器内应用程序的数据。数据卷允许容器之间共享数据,并提供了可靠的数据存储备份机制。由于容器是隔离环境,容器内程序的文件、配置等都在容器的内部,要读写容器内的文件非常不方便。因此容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器进行解耦。

Docker默认将卷存储在 /var/lib/docker/volumes/ 这是Docker管理卷的内部路径,通常由Docker自动创建和管理。不建议直接操作这个目录,除非你非常了解Docker的内部机制。

在实际使用中,开发者通常会根据需要将容器卷挂载到特定的目录,常见的挂载路径包括:

  1. 适用于存储应用程序的持久化数据,如数据库文件、日志文件等。/opt/<app-name>/data
  2. 适用于挂载应用程序的配置文件。/etc/<app-name>/config
  3. 适用于存储应用程序的日志文件。/var/log/<app-name>
  4. 适用于存储用户相关的数据。/home/<user>/<app-name>/data

什么是数据卷

数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。
以Nginx为例,我们知道Nginx中有两个关键的目录:

  • html:放置一些静态资源;
  • conf:放置配置文件;

如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件。

从DockerHub的nginx说明处,我们可以看到nginx静态文件的位置: image.png

不过,容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图: image.png

在上图中:

  • 创建了两个数据卷:confhtml
  • Nginx容器内部的conf目录和html目录分别与两个数据卷关联
  • 而数据卷conf和html分别指向了宿主机的/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录

这样,容器内的confhtml目录就与宿主机的confhtml目录关联起来,被称为挂载。此时,操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html/_data目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。

数据卷命令

数据卷的相关命令有:

命令说明文档地址
docker volume create创建数据卷docker volume create
docker volume ls查看所有数据卷docs.docker.com
docker volume rm删除指定数据卷docs.docker.com
docker volume inspect查看某个数据卷的详情docs.docker.com
docker volume prune清除数据卷docker volume prune

注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建

数据卷的三种挂载方式

方式一:匿名挂载

匿名挂载是指没有指定特定名称的挂载方式。当您在创建容器时没有明确指定数据卷的路径名称时,Docker会在该目录/var/lib/docker/volumes下为该数据卷创建一个匿名的、临时的卷。匿名挂载适用于临时存储或一次性使用的场景,因为它们在容器被删除后将被自动清理。

1.运行nginx容器

//以后台方式运行容器,并使用-v 匿名挂载容器数据卷 (推荐)
docker run --rm -d -p 80:80 --name nginx_volume -v /usr/share/nginx/html nginx:latest

//--rm:退出容器以后,这个容器就被删除了
//-d:表示该容器在后台运行
//-p 80:80:将容器的80接口与宿主机的80端口进行映射
//--name nginx_volume:容器名称
//-v /usr/share/nginx/html:将nginx目录中/usr/share/nginx/html挂在到宿主机/var/lib/docker/volumes下。

//注意:如果出现Docker挂载宿主机目录显示cannot open directory .:Permission denied
解决办法:在挂载目录后面 多加一个--privileged=true参数即可。

2.访问Nginx image.png

3.查看Nginx容器详细信息

// 查看Nginx容器详细信息
docker inspect nginx_volume

// 执行结果
  ...
"Mounts": [
            {
                "Type": "volume",
                "Name": "eb169266220c48dd13dee21dd41700e00f19da8d757de91b127cdda09d70e2b9",
                //挂载到宿主机目录
                "Source": "/var/lib/docker/volumes/eb169266220c48dd13dee21dd41700e00f19da8d757de91b127cdda09d70e2b9/_data",              
                //将nginx下的文件
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
   ...
 
// 进入宿主机的目录下
cd /var/lib/docker/volumes/eb169266220c48dd13dee21dd41700e00f19da8d757de91b127cdda09d70e2b9/_data

// 列表展示
ll

//执行结果
-rw-r--r-- 1 root root 497 Dec 28  2021 50x.html
-rw-r--r-- 1 root root 615 Dec 28  2021 index.html

//将index.html中的所有东西替换为'Hello Docker'
echo 'Hello Docker' > index.html

4.再次访问Nginx image.png

5.进入Nginx容器内部

//进入Nginx容器内部
docker exec -it nginx_volume /bin/bash

//查看 Nginx 容器的 index.html
cat /usr/share/nginx/html/index.html

//执行结果
Hello Docker

//我们在Nginx容器中创建一个test.html并修改index.html
echo 'Test' > test.html
echo 'Hello Container' > index.html

//进入宿主机的Nginx文件目录中
cd /var/lib/docker/volumes/eb169266220c48dd13dee21dd41700e00f19da8d757de91b127cdda09d70e2b9/_data

// 查看文件列表
ls

//执行结果
50x.html  index.html  test.html

//查看文件
cat index.html

//执行结果
Hello Container

6.再次访问Nginx

  • 默认访问的是index.html

image.png

  • 访问test.html

image.png

  1. 停止容器
// 停止容器
docker stop nginx 

//查看数据卷
docker volume ls

//因为我们启动容器使用`docker run --rm -d -p 80:80 --name nginx_volume -v /usr/share/nginx/html nginx:latest`进行启动,rm表示退出容器以后,这个容器就被删除了。但是数据卷也是随之删除。

由以上案例可知,在使用匿名卷时,会在宿主机目录/var/lib/docker/volumes路径下自动生成目录(或文件),当我们修改宿主机的文件时,容器中的的文件也会被修改。当我们修改容器中的文件时,宿主机的文件也会被修改。且匿名绑定的volume在容器被删除的时候,数据卷也会被删除。

方式二:具名挂载

具名挂载是指通过指定一个唯一的名称来挂载数据卷的方式。使用具名挂载,您可以为数据卷提供一个明确的、可预测的名称,以便在多个容器之间共享和重用。具名挂载的数据卷在容器删除后仍然存在,除非您明确删除该卷。

1.运行nginx容器

// 1.以后台方式运行容器
docker run --rm -d -p 80:80 --name nginx_volume -v nginx-volume:/usr/share/nginx/html nginx:latest

//--rm:退出容器以后,这个容器就被删除了
//-d:表示该容器在后台运行
//-p 80:80:将容器的80接口与宿主机的80端口进行映射
//--name nginx_volume:容器名称
//-v nginx-volume:/usr/share/nginx/html :将nginx目录中/usr/share/nginx/html挂在到宿主机/var/lib/docker/volumes/nginx-volume/_data下。并将数据卷名称改为nginx-volume

// 2.查看已启动的容器信息
docker ps
//执行结果
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
a50508c919bc   nginx:latest   "/docker-entrypoint.…"   4 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp                      nginx_volume

// 3.查看数据卷列表
docker volume ls
//执行结果
DRIVER    VOLUME NAME
local     nginx-volume

// 4.查看容器详细信息
docker inspect nginx_volume
//执行结果
...
"Mounts": [
            {
                "Type": "volume",
                "Name": "nginx-volume",
                //将容器中该路径/usr/share/nginx/html下的文件挂载到"/var/lib/docker/volumes/nginx-volume/_data"下
                "Source": "/var/lib/docker/volumes/nginx-volume/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
...

//进入该路径/var/lib/docker/volumes/nginx-volume/_data下
cd /var/lib/docker/volumes/nginx-volume/_data
//查看该路径下的文件列表
ls
//执行结果
50x.html  index.html

//echo "Hello Host" > test.html
//echo "Hello Index" > index.html

访问Nginx页面

  • 访问 index.html

image.png

  • 访问 test.html

image.png

3.进入容器修改文件

//进入Nginx容器中
docker exec -it  nginx_volume /bin/bash

//进入该目录下
cd /usr/share/nginx/html

//将"Hello Container Test"写入 test.html 中
echo "Hello Container Test" > test.html

//将"Hello Container Index"写入 index.html 中
echo "Hello Container Index" > index.html

4.访问Nginx页面

  • 访问 index.html

image.png

  • 访问 test.html

image.png

5.删除容器

//强制删除数据卷时,不会删除数据卷,而是会删除这个容器本身的东西
docker rm -f nginx_volume

//查看已启动容器,查询不到nginx_volume容器
docker ps

//数据卷列表
docker volume ls
//执行结果:
DRIVER    VOLUME NAME
local     nginx-volume

//删除数据卷
docker volume rm nginx-volume
docker volume ls//查询不到数据卷

//执行一下命令重新运行这个容器,然后停止这个容器,查询数据卷时发现数据卷还在
docker run --rm -d -p 80:80 --name nginx_volume -v nginx-volume:/usr/share/nginx/html nginx:latest
docker stop nginx_volume
docker ps
docker volume ls

由以上案例可知,在使用具名卷时,会在宿主机目录/var/lib/docker/volumes路径下自动生成目录(或文件),当我们修改宿主机的文件时,容器中的的文件也会被修改。当我们修改容器中的文件时,宿主机的文件也会被修改。且具名绑定的volume在容器被删除的时候,数据卷不会被删除,除非指定docker volume rm 数据卷名称删除数据卷。

方式三:Bind Mount(推荐)

绑定并加载主机的某一个文件目录到容器中,这种方式是最平常最常用的。这种绑定方式与前面两种一样。


//将"Hello Host"写入到/wuke/wolfcode/index.html下面
echo "Hello Host" > /wuke/wolfcode/index.html

//以后台方式运行容器,并将/usr/share/nginx/html的所有目录文件替换为/wuke/wolfcode下的index.html
docker run --rm -d -p 80:80 --name nginx_volume -v /wuke/wolfcode:/usr/share/nginx/html nginx:latest

//--rm:退出容器以后,这个容器就被删除了
//-d:表示该容器在后台运行
//-p 80:80:将容器的80接口与宿主机的80端口进行映射
//--name nginx_volume:容器名称
//-v /wuke/wolfcode:/usr/share/nginx/html:将宿主机/wuke/wolfcode下的目录文件映射到容器/usr/share/nginx/html下。

访问Nginx页面 image.png

//停掉nginx_volume容器
docker stop nginx_volume

//停掉nginx_volume容器,但是主机/wuke/wolfcode/index.html下的文件是不会被删除的。
docker ps
cd /wuke/wolfcode
ls

该Bind Mount是开发过程中使用最多的,即使我们容器被删除了,而主机中的数据文件是不会被删除的。