logo
0
0
WeChat Login
update: 补充Docker存储与网络说明及最佳实践

Docker 存储管理详解

Docker 容器在运行时会产生大量数据,这些数据如何持久化和管理是一个重要的话题。 本节我们将通过一个 Nginx Web 服务器的案例,来深入探讨 Docker 的三种数据管理方式。

Docker 存储基础

Docker 提供了三种主要的数据管理方式:

  1. 默认存储:容器内的数据随容器删除而丢失
  2. Bind Mounts(绑定挂载):将主机上的目录或文件直接挂载到容器中
  3. Volumes(卷):由 Docker 管理的持久化存储空间,完全独立于容器的生命周期

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

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

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

# 运行一个 nginx 容器 docker run -d --name web-default -p 8000:80 nginx # 在容器中创建一个测试页面 docker exec -it web-default /bin/sh echo "<h1>Hello from Default Storage</h1>" > /usr/share/nginx/html/index.html exit # 访问页面验证内容 curl http://localhost:8000 # 容器中文件目录获取 # 背景介绍: # 容器目录是在宿主机目录下的一个子目录,通过chroot的linux技术实现; # 容器的目录对宿主机隔离,一般禁止访问; # 该命令用于获取 Docker 容器在宿主机上的实际存储路径。当容器使用联合文件系统(如 overlay2)时,容器文件系统由多个层组成, MergedDir 就是这些层最终合并后在宿主机上的实际目录路径 docker inspect -f '{{.GraphDriver.Data.MergedDir}}' <container_id> cat 容器目录/usr/share/nginx/html/index.html # 删除容器 docker stop web-default docker rm web-default # 用同样的配置重新运行容器 docker run -d --name web-default-2 -p 8000:80 nginx # 再次访问页面,内容不存在 curl http://localhost:8000

💡 为什么数据会丢失?理解 Docker 的分层存储

Docker 使用 OverlayFS(联合文件系统) 来管理容器的文件系统,它由多个层组成:

┌─────────────────────────────────┐ │ 容器可写层(Container Layer) │ ← 你在容器中创建/修改的文件都在这里 │ 生命周期 = 容器生命周期 │ ← 容器删除时,这一层也被删除! ├─────────────────────────────────┤ │ 镜像只读层 3(Nginx 配置) │ ├─────────────────────────────────┤ │ 镜像只读层 2(Nginx 程序) │ ← 这些层是只读的,多个容器共享 ├─────────────────────────────────┤ │ 镜像只读层 1(基础系统) │ └─────────────────────────────────┘

工作原理:

  • 当你 docker run 时,Docker 在镜像层之上创建一个可写层
  • 容器内的所有写操作(创建文件、修改文件)都发生在这个可写层
  • 当你 docker rm 删除容器时,可写层随之删除,所有修改都丢失
  • 镜像层保持不变,下次创建新容器时又是一个"干净"的状态

这就是为什么:

  • web-default 中创建的 index.html 存储在容器的可写层
  • 删除 web-default 后,可写层被清除
  • web-default-2 是一个全新的容器,有自己的空白可写层,所以看不到之前的文件

Volume 和 Bind Mount 的本质:它们都是绕过可写层,将数据直接存储在容器外部(宿主机),因此不受容器生命周期影响。

场景二:使用 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 Mounts</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

场景三:使用 Volume

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

# 创建一个 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 ls docker volume inspect nginx_data

三种方式的对比

  1. 默认存储

    • 数据随容器删除而丢失
    • 适合存储临时数据
    • 容器间数据隔离
    • 无需额外配置
  2. Bind Mount

    • 数据持久化,存储在主机指定位置
    • 可以直接在主机上修改文件
    • 不足:目录权限不对等,有安全风险
    • 不足:依赖主机文件系统结构
  3. Volume

    • 数据持久化,独立于容器生命周期
    • 数据存储在 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性能好、Docker 管理、便于备份
开发环境代码同步Bind Mount实时修改、IDE 友好
配置文件注入Bind Mount(只读)安全、灵活
临时缓存tmpfs内存存储、容器删除即清理
容器日志默认 + 日志驱动避免存储膨胀

实践案例:使用 Volume 部署 MySQL 数据库

我们将通过一个 MySQL 数据库的例子来演示如何使用 Volume 持久化数据。

创建并管理 Volume

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

运行 MySQL 容器,并挂载卷

# 运行 MySQL 容器并挂载卷 # 备注:MYSQL_ROOT_PASSWORD 是环境变量,用于设置 MySQL 的 root 用户密码 docker run -d \ --name mysql_db \ -e MYSQL_ROOT_PASSWORD=mysecret \ -v mysql_data:/var/lib/mysql \ mysql:8.0 # 进入容器创建测试数据 docker exec -it mysql_db mysql -uroot -pmysecret -h127.0.0.1 # 在 MySQL 中创建测试数据 mysql> CREATE DATABASE test_db; mysql> USE test_db; mysql> CREATE TABLE users (id INT, name VARCHAR(50)); mysql> INSERT INTO users VALUES (1, 'John Doe'); mysql> exit

验证数据持久化

# 删除原容器 docker rm -f mysql_db # 使用同一个卷启动新容器 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;"