Skip to content
60.Docker»60.CNB下的Docker应用»LV030-存储管理.md

LV030-存储管理

一、为什么容器需要持久化存储

Docker 容器默认使用 Union FS(联合文件系统)作为其存储层,它允许容器在其基础镜像之上添加自己的文件系统层。这种分层结构使得容器可以快速地创建和销毁,但同时也意味着容器内的数据不是持久的,一旦容器被删除,其数据也会丢失。

默认情况下,容器内创建的所有文件都存储在可写容器层上。这意味着:

  • 当该容器不再存在时,数据不会持久存在,并且如果另一个进程需要数据,则可能很难从容器中获取数据。
  • 容器的可写层与运行容器的主机紧密耦合。您无法轻松地将数据移动到其他地方。
  • 写入容器的可写层需要 存储驱动程序 来管理文件系统。存储驱动程序使用 Linux 内核提供联合文件系统。与使用直接写入主机文件系统的 数据卷 相比,这种额外的抽象会降低性能 。

总之,容器文件系统的本质是在镜像层上面创建的读写层,运行中的容器对任何文件的修改都存在于该读写层,当容器被删除时,容器中的读写层也会随之消失。Docker 有两个选项让容器在主机上存储文件,以便即使在容器停止后文件也能保留:卷和绑定挂载。

Tips:可参考:60.Docker/10.文件系统/LV015-Overlay2 文件系统.md

二、volume——数据卷

1. 什么是卷?

在 Docker 的世界里,卷(Volumes)可是实现文件共享与持久化存储的 “神器”。简单来说,卷就是一个 特殊的目录,它专门用于绕过容器的常规文件系统,在容器与宿主机之间搭建起一座稳固的数据桥梁。与容器内部普通的目录相比,卷有着本质的区别。容器内普通目录的数据通常是临时存储的,一旦容器停止或被删除,这些数据就如同泡沫般消散;而卷中的数据却能稳稳扎根于宿主机的文件系统,独立于容器的生命周期,始终保持完好无损。

想象一下,我们在容器里运行一个数据库应用,容器内的数据库文件存储在普通目录下,哪天不小心容器崩溃或者需要重新部署,辛辛苦苦录入的数据瞬间化为乌有,这场景简直是 “灾难现场”。但要是使用卷来存储数据库文件,无论容器怎么折腾,数据都安然无恙地躺在宿主机上,随时等待容器重新挂载使用。

2. 接本原理

Docker 卷的实现原理是在主机的 /var/lib/docker/volumes 目录下,根据卷的名称创建相应的目录,然后在每个卷的目录下创建 _data 目录,在容器启动时如果使用 --mount 参数,Docker 会把主机上的目录直接映射到容器的指定目录下,实现数据持久化。

其实卷的本质是文件或者目录,只是它可以绕过默认的联合文件系统,直接以文件或目录的形式存在于宿主机上。卷的概念不仅解决了数据持久化的问题,还解决了容器间共享数据的问题。使用卷可以将容器内的目录或文件持久化,当容器重启后保证数据不丢失。

3. 有什么优势?

(1)卷具有强大的数据持久化能力。正如前文所提到的,容器内应用产生的数据若存储在卷中,即便容器遭遇关停、删除等变故,数据依旧毫发无损地存于宿主机,这为那些需要长期保存数据的应用,如数据库、日志存储等,提供了坚实可靠的后盾。以 MySQL 数据库容器为例,将数据库文件存放于卷内,无论是日常的容器重启优化,还是紧急情况下的容器重建,数据都能无缝衔接,业务连续性得以保障,有效避免因数据丢失带来的巨大损失。

(2)共享便捷。多个容器可以同时挂载同一个卷,瞬间实现数据的互联互通。在一个微服务架构的电商应用里,前端容器、后端 API 容器以及订单处理容器等,可能都需要共享同一份商品配置信息、用户认证密钥等关键数据,通过卷挂载,这些容器就能像共享 “公共资源库” 一样,轻松获取所需数据,极大地简化了容器间的协作流程,提升整体运行效率。

(3)卷独立于容器生命周期这一特性,赋予了开发与运维工作极大的灵活性。开发阶段,开发人员可以随意启停、修改容器,不用担心数据丢失,随时切换不同版本的应用进行测试;运维人员在进行容器迁移、集群扩展等操作时,也无需顾虑数据迁移的繁琐与风险,只需确保卷的正确挂载,就能快速完成任务。

4. 基本命令

4.1 创建数据卷

shell
docker volume create <volume-name>

【例】

shell
docker volume create nginx-vol

4.2 列出所有卷

shell
docker volume ls

4.3 查看卷信息

docker volume inspect 命令可获取挂载路径、驱动类型等元数据。

shell
docker volume inspect <volume-name>

【例】

shell
docker volume inspect nginx-vol

4.4 挂载到容器

shell
docker run -d --mount src=volume-name,dst=container_dir image
docker run -d -v volume-name:container_dir image

【例】

shell
docker run -d --name=nginx-test1 --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
docker run -d --name=nginx-test2 -v nginx-vol:/usr/share/nginx/html nginx

4.5 查看是否被使用

shell
docker ps -a --filter volume=volume-name

4.6 删除数据卷

  • 删除指定数据卷
bash
docker volume rm <volume_name>
  • 删除未使用的卷
bash
docker volume prune

5. 使用示例

5.1 创建一个数据卷

bash
docker volume create test-vol  # 创建一个自定义容器卷
docker volume ls               # 查看所有容器卷
docker volume inspect test-vol # 查看指定容器卷详情信息

【例】

shell
  /workspace git:(main)  docker volume create test-vol  # 创建一个自定义容器卷
test-vol
  /workspace git:(main)  docker volume ls               # 查看所有容器卷
DRIVER    VOLUME NAME
local     test-vol
  /workspace git:(main)  
  /workspace git:(main)  docker volume inspect test-vol # 查看指定容器卷详情信息
[
    {
        "CreatedAt": "2025-11-01T07:35:25Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/test-vol/_data",
        "Name": "test-vol",
        "Options": null,
        "Scope": "local"
    }
]

volumes:默认位于 /var/lib/docker/volumes 目录中。/var/lib/docker/volumes/test-vol/_data 这整个路径都需要 root 用户才有权限访问,而 cnb 中,我们并不具备这个权限所以这里就没有办法访问,但是要是在 windows 主机或者 linux 主机上装了 docker 再进行操作的话就没问题了。

5.2 使用数据卷

5.2.1 挂载并创建文件
bash
# 方式 1:--mount
# docker run -d --name my_container -v my_volume:/path/in/container my_image
docker run -it --name=my_container -p 8000:8000 --mount src=test-vol,dst=/usr/share/my-vol-data sumumm/docker-demo:1.0.0 /bin/bash

# 方式 2; -v
# docker run -d --name my_container --mount src = my_volume, dst =/path/in/container my_image
docker run -it --name=my_container -p 8000:8000 -v test-vol:/usr/share/my-vol-data sumumm/docker-demo:1.0.0 /bin/bash

无论是使用 -v 还是 --mount 选项,都可以将 Docker 卷挂载到容器中。挂载后,容器就可以读写卷中的数据,实现了数据在容器之间的共享和持久化存储。我们可以使用下面的命令来查看是否有容器使用使用了 test-vol 卷:

bash
docker ps -a --filter volume=test-vol

然后我们在容器中往这个数据卷中写入文件:

bash
cd /usr/share/my-vol-data/
echo "my-vol-data" > README.md
5.2.2 宿主机访问数据卷

但是我在宿主机中访问的时候报错了,好像没有这个目录以及文件:

image-20251101154111824

这里其实已经告诉我们了,没有权限,因为这是 cnb 的云原生开发环境,所以我们没有 root 用户权限。后来我去 VMware 中的安装的 Ubuntu 系统也尝试了一下:

image-20251018101732010

加上 sudo 权限后就可以了。

5.2.3 其他容器访问

其实我们还可以再创建一个容器,挂载这个卷,就可以看到这个文件和内容了:

shell
docker run -it --name=my_container2 -v test-vol:/usr/share/my-vol-data sumumm/docker-demo:1.0.0 /bin/bash

image-20251101154435084

三、Bind Mount 挂载

1. 简介

BindMount 直接将主机目录直接挂载到容器,适合开发调试场景,可直接修改主机文件并实时生效。

2. 基本命令

shell
# 方法 1:-v 参数(推荐)
docker run -v host_dir:container_dir image

# 方法 2:--mount 参数
docker run --mount type=bind,src=host_dir,dst=container_dir image

【例】

shell
# 方法 1:-v 参数(推荐)
docker run -it -v /workspace:/usr/share/workspace sumumm/docker-demo:1.0.0 /bin/bash

# 方法 2:--mount 参数
docker run -it --mount type=bind,src=/workspace,dst=/usr/share/workspace sumumm/docker-demo:1.0.0 /bin/bash

3. 使用示例

3.1 挂载目录

shell
docker run -it --mount type=bind,src=/workspace,dst=/usr/share/workspace sumumm/docker-demo:1.0.0 /bin/bash

3.2 效果测试

原来 settings.json 文件内容如下:

bash
root@4ac5e5124ed5:/usr/share/workspace# cat settings.json
{
    "workbench.colorTheme": "One Dark Pro",
    "workbench.iconTheme": "material-icon-theme",
    "terminal.integrated.suggest.enabled": true
}

我们在宿主机把最后一个选项改为 false:

image-20251016074116855

然后再在容器内查看文件内容:

bash
root@4ac5e5124ed5:/usr/share/workspace# cat settings.json
{
    "workbench.colorTheme": "One Dark Pro",
    "workbench.iconTheme": "material-icon-theme",
    "terminal.integrated.suggest.enabled": false
}

我们在容器中创建一个文件并添加内容:

image-20251016074308766

会发现宿主机中也同步产生了文件。

四、总结

前面已经提到了,Docker 提供了三种主要的数据管理方式:

(1)默认存储:容器内的数据随容器删除而丢失

(2)Volumes(卷):由 Docker 管理的持久化存储空间,完全独立于容器的生命周期

(3)Bind Mounts(绑定挂载):将主机上的目录或文件直接挂载到容器中

让我们通过一个 Nginx Web 服务器的例子来理解这三种方式的区别。我们将在每种方式下执行相同的操作:创建一个 HTML 文件,然后测试数据的持久性。

1. 场景一:默认存储(非持久化)

在这个场景中,我们直接在容器内创建文件,看看数据会发生什么:

shell
# 运行一个 nginx 容器
docker run -d --name web-default -p 8000:80 nginx

# 在容器中创建一个测试页面
docker exec -it web-default sh -c \
  'echo "<h1>Hello from Default Storage</h1>" > /usr/share/nginx/html/index.html'

# 访问页面验证内容
curl http://localhost:8000

# 删除容器
docker rm -f web-default

# 用同样的配置重新运行容器
docker run -d --name web-default -p 8000:80 nginx

# 再次访问页面,会看到默认的 Nginx 欢迎页面,之前的内容已经丢失
curl http://localhost:8000

2. 场景二:使用 Bind Mount

在这个场景中,我们将主机上的目录直接挂载到容器中:

shell
# 创建本地目录
mkdir nginx-content

# 运行 Nginx 容器并挂载本地目录
docker run -d --name web-bind \
   -p 8081:80 \
   -v $(pwd)/nginx-content:/usr/share/nginx/html nginx

# 在容器中创建一个测试页面
docker exec -it web-bind sh -c \
  'echo "<h1>Hello from Bind Mount Storage</h1>" > /usr/share/nginx/html/index.html'

# 访问页面验证内容
curl http://localhost:8081

# 删除容器
docker rm -f web-bind

# 用同样的配置重新运行容器
docker run -d --name web-bind-2 -p 8081:80 \
   -v $(pwd)/nginx-content:/usr/share/nginx/html nginx

# 再次访问页面,内容仍然存在
curl http://localhost:8081

3. 场景三:使用 Volume

在这个场景中,我们使用 Docker 管理的卷来存储数据:

shell
# 创建一个 Docker volume
docker volume create nginx_data

# 运行 Nginx 容器并挂载卷
docker run -d --name web-volume -p 8082:80 -v nginx_data:/usr/share/nginx/html nginx

# 在容器中创建一个测试页面
docker exec -it web-volume sh -c 'echo "<h1>Hello from Volume Storage</h1>" > /usr/share/nginx/html/index.html'

# 访问页面验证内容
curl http://localhost:8082

# 删除容器
docker rm -f web-volume

# 用同样的配置重新运行容器
docker run -d --name web-volume-2 -p 8082:80 \
   -v nginx_data:/usr/share/nginx/html nginx

# 再次访问页面,内容仍然存在
curl http://localhost:8082

# 查看卷的详细信息
docker volume inspect nginx_data
# 查看卷列表
docker volume ls
# 删除卷
docker volume rm nginx_data

4. 三种方式的对比

存储类型描述
默认存储- 数据随容器删除而丢失
- 适合存储临时数据
- 容器间数据隔离
- 无需额外配置
Bind Mount- 数据持久化,存储在主机指定位置
- 可以直接在主机上修改文件,并实时生效
- 将主机目录直接挂载到容器,适合开发调试场景,在开发环境中方便调试和修改
- 依赖主机文件系统结构
Volume- 数据持久化,独立于容器生命周期
- Docker 统一管理,方便备份和迁移
- 可以在多个容器间共享
- 数据存储在 Docker 管理区域,安全性好

5. 清理操作

完成上面的实验后,可以进行清理:

shell
# 清理容器
docker rm -f web-default web-volume web-volume-2 web-bind web-bind-2

# 清理卷
docker volume rm nginx_data

# 清理本地目录
rm -rf nginx-content

参考资料:

还在为 Docker 宿主机与容器文件共享发愁?看这一篇就够了!_docker 共享文件夹-CSDN 博客

(8 封私信) Docker 挂载本地目录和数据卷容器实现文件共享 - 知乎

dcoker 存储 —— One | 明心静性,爱自己

Docker 数据卷(Volume) - deshell - 博客园

莫道桑榆晚 为霞尚满天.