logo
0
0
WeChat Login
fix(storage): 修复文档错误

Docker 存储管理

Docker 容器在运行时会产生大量数据,如何对这些数据进行持久化和管理是一个重要话题。本节将以 Nginx Web 服务器为例,深入介绍 Docker 的三种数据管理方式。

Docker 存储基础

Docker 中常见的三种数据管理方式是:

  1. 容器层(默认存储):数据写在容器内部文件系统中,容器删除后数据也随之丢失
  2. Volumes(卷):由 Docker 管理的持久化存储,与容器生命周期解耦
  3. Bind Mounts(绑定挂载):将宿主机上的目录或文件直接挂载到容器内部使用

下面以 Nginx Web 服务器为例,通过在三种方式下都执行同样的操作 ---- 创建一个 HTML 文件并测试其在容器删除后的可用性,来对比它们在数据持久型方面的差异。

默认存储(非持久化)

我们直接在容器内创建文件,看看数据会发生什么。

首先,运行一个 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 stop web-default docker rm web-default

用同样的配置重新运行容器

docker run -d --name web-default -p 8000:80 nginx

再次访问页面,会看到什么?

curl http://localhost:8000

使用 Bind Mount

在这个场景中,我们将主机上的目录直接挂载到容器中,看看会发生什么。

首先在宿主机本地创建一个目录:

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 stop web-bind docker rm web-bind

用同样的配置再次运行一个新的容器,名称为 web-bind-2

docker run -d --name web-bind-2 -p 8081:80 \ -v $(pwd)/nginx-content:/usr/share/nginx/html nginx

再次验证页面内容:

curl http://localhost:8081

使用 Volume

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

首先创建一个 Docker Volume(名称为 nginx_data

docker volume create nginx_data

接着运行 Nginx 容器,并挂在刚刚创建好的卷。卷被挂在到容器中的 /usr/share/nginx/html 目录下:

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 stop web-volume docker rm 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 查看卷的详细信息,会有什么发现?

三种方式的对比

  1. 默认存储

    • 数据保存在容器可写层,容器删掉数据就会消失
    • 适合缓存、临时文件等不需持久化的数据
    • 不同容器之间默认互相隔离
    • 开箱即用,无需任何配置
  2. Bind Mounts(绑定主机目录)

    • 数据持久化,直接存放在主机指定目录
    • 可以在宿主机上直接查看、编辑文件
    • 非常适合开发环境的实时调试和热更新
    • 强依赖宿主机目录结构和路径,不够“可移植”
  3. Volume(卷)

    • 数据持久化,生命周期独立于容器
    • 由 Docker 统一管理,便于备份、迁移和清理
    • 容易在多个容器之间共享同一份数据
    • 存储在 Docker 管理区域,相对更安全、更解耦

清理操作

完成实验后,进行清理:

# 清理容器 docker rm -f web-default web-volume web-volume-2 web-bind web-bind-2 # 清理卷 docker volume rm nginx_data # 清理本地目录 rm -rf nginx-content

使用 Volume 部署 MySQL 数据库✏️

MySQL 是最常见的需要数据持久化的服务之一。 在这个案例中,我们将创建一个 Volume 来存储 MySQL 的数据目录, 并验证在容器被删除重建后,数据库中的数据依然完好。

创建 Volume

# 创建一个卷,名称为 mysql_data docker volume create mysql_data # 查看卷信息 docker volume inspect mysql_data # 列出所有卷 docker volume ls

运行 MySQL 并写入测试数据

首先,运行 MySQL 容器,并挂载 Volume mysql_data

docker run -d --name mysql_db -e MYSQL_ROOT_PASSWORD=mysecret -v mysql_data:/var/lib/mysql mysql:8.0

接下来进入到容器中,启动 MySQL 客户端,连接到 MySQL 服务:

docker exec -it mysql_db mysql -uroot -pmysecret -h127.0.0.1

在 MySQL 中创建测试数据:

create database test_db; use test_db create table users(id int, name varchar(50)); insert into users values(1, 'Mike'); select * from users\G exit

验证数据持久化

现在来验证 Volume 的持久化能力:删除当前的 MySQL 容器,再用同一个 Volume 启动一个全新的容器,看数据是否还在。

首先,停止并删除原容器:

docker stop mysql_db docker rm mysql_db

然后,挂载同一个 Volume mysql_data,运行一个新的 MySQL 容器:

docker run -d --name mysql_db2 -e MYSQL_ROOT_PASSWORD=mysecret -v mysql_data:/var/lib/mysql mysql:8.0

查询之前写入的数据,看看是否还在:

docker exec -it mysql_db2 mysql -uroot -pmysecret -e "USE test_db; SELECT * FROM users;"

如果能看到之前插入的记录,说明数据确实存储在 Volume 中,不会因为容器的删除而丢失。

🤔这里的 -e 选项是什么含义?与上一个命令中的 -e 是否相同?

使用 Bind Mounts 运行 Jupyter Notebook

Jupyter Notebook 是一个非常适合用 Bind Mounts 的场景:笔记文件(.ipynb)存放在代码仓库中,通过 Bind Mounts 挂载到容器内,就能在 Jupyter 中直接打开和编辑这些文件。更重要的是,由于 Bind Mounts 是双向同步的,在容器内的修改会实时反映到宿主机,反之亦然。

构建镜像

构建一个 Jupyter Notebook 镜像:

cd jupyter docker build -t jupyter-notebook .

运行容器

先查看本地的 .ipynb 文件是否存在:

ls -l notebooks

运行 Jupyter Notebook 容器:

docker run --name jupyter-notebook -p 8888:8888 -d -v $(pwd)/notebooks:/notebooks jupyter-notebook

其中 -v $(pwd)/notebooks:/notebooks 将宿主机当前目录下的 notebooks 文件夹挂载到容器内的 /notebooks 目录(即 Jupyter 的工作目录)。这样容器内对笔记文件的修改会实时同步到宿主机,反之亦然。

查看容器状态:

docker ps

查看容器日志,确认 Jupyter 服务已正常启动:

docker logs jupyter-notebook

在 CNB 上,我们可以通过添加一个端口映射来实现外网访问:

port_forward

验证 Bind Mounts 双向同步

在浏览器中访问 Jupyter Notebook,你应该能看到 notebooks 目录下已有的 .ipynb 文件。

接下来我们验证 Bind Mounts 的双向同步特性:

容器 → 宿主机:在 Jupyter Notebook 界面中编辑并保存一个笔记文件,然后在宿主机上查看:

ls -l notebooks

你会发现文件已经实时同步到宿主机上。

宿主机 → 容器:反过来,在宿主机上直接修改笔记文件,刷新 Jupyter Notebook 页面后也能看到最新内容。

这就是 Bind Mounts 在开发场景下的优势:代码仓库中的文件和容器内的文件始终保持一致,非常适合本地开发和调试。