掘金 后端 ( ) • 2024-04-24 16:32

theme: v-green highlight: a11y-dark

引言

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

什么是数据卷

数据卷(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是开发过程中使用最多的,即使我们容器被删除了,而主机中的数据文件是不会被删除的。