Docker 容器存储管理全解析

1. tmpfs 与 Secrets

tmpfs 类型将数据保存到内存中,不会写入主机文件系统。它适用于容器运行时使用的不需要持久化的应用程序或会话数据。tmpfs 挂载可以与容器共享敏感信息,如密钥和密码。Secrets 有两种类型:运行时和构建时。构建时的 Secrets 可通过 Buildah 在 Docker 中使用。

2. 卷与绑定挂载对比

Docker 官方推荐使用 Docker 卷来持久化数据,它相较于绑定挂载有诸多优势:
- 性能提升 :对于 Docker Desktop 用户,Docker 卷是 Docker 原生对象,性能优于绑定挂载。
- 远程主机和云集成 :Docker 卷可使用绑定挂载无法使用的专用、远程和云存储。
- 增强安全性 :插件和驱动程序支持卷加密和访问控制。
- 更好的所有权和权限管理 :Linux 系统(包括在 Windows Subsystem for Linux 下运行的系统)中的绑定挂载需要额外步骤来修复目录所有权,而卷则不需要。
- 备份和共享 :卷更易于备份和迁移,并且在多个容器之间共享更安全。

3. 容器与外部数据的关系

容器与外部数据存在关联,Docker 通过卷能更好地理解这种关系。在简单环境中,这种关系不太重要,但随着复杂度增加,了解数据的使用和存储位置就变得至关重要。例如,在运行 Oracle 数据库容器时,最初使用一个名为 oradata 的目录作为所有容器的根目录,每个容器绑定挂载到自己的子目录,手动管理一切。但随着挂载多个目录到一个容器或在多个容器之间共享目录,跟踪变得困难,删除目录的后果也更严重。

4. 挂载存储的方法
4.1 -v 或 –volume 选项

最终目的是将容器中的目标路径映射到主机上的源路径以写入数据。对于 -v 或 –volume 选项,它们以冒号分隔的有序对形式传递,源在前,目标在后。
- 绑定挂载 :源是一个目录,示例命令如下:

docker run ... \
       -v /oradata/ORA19C:/opt/oracle/oradata \
...
docker run ... \
       --volume /oradata/ORA19C:/opt/oracle/oradata \
...
  • 卷挂载 :源是卷名,示例命令如下:
docker run ... \
       -v oradata_ORA19C:/opt/oracle/oradata \
...
docker run ... \
       --volume oradata_ORA19C:/opt/oracle/oradata \
...
4.2 –mount 选项

–mount 选项将元素分隔为命名的、逗号分隔的列表,元素顺序无关紧要。
- 绑定挂载目录

docker run ... \
       --mount type=bind,source=/oradata/ORA19C,target=/opt/oracle/oradata \
...
  • 挂载卷
docker run ... \
       --mount type=volume,target=/opt/oracle/oradata,source=oradata_ORA19C \
...

–mount 语法中有一个额外的字段 type,用于告诉 Docker 使用的是卷还是绑定挂载。而在 -v 选项中,根据源是 Docker 卷还是目录来判断类型。

5. 未定义的卷

如果尝试将主机目录映射到容器中不存在的路径,Docker 会在容器中创建该目录。例如:

> docker run --rm -it \
>        -v $HOME:/not/a/real/directory alpine
/ # ls -l /not/a/real/directory/
total 8302776
drwx------    7 sean.scott  staff       224 Mar 26 11:44 Applications
drwx------@  88 sean.scott  staff      2816 Apr  8 16:57 Desktop
drwx------+  49 sean.scott  staff      1568 Mar 16 08:31 Documents
...

也可以将目录挂载到容器中的现有路径。如果容器路径包含文件,Docker 会隐藏容器目标目录的内容,将源目录内容投影到上面。但需要注意的是,挂载现有非空目录到容器中,如果源目录缺少容器所需的文件,可能会产生意外和不良结果。

6. 入口点目录

镜像定义了启动命令,类似于物理或虚拟主机上的引导或初始化过程。启动命令在创建和启动容器时运行,指导它们执行预定义的工作。例如,Oracle 数据库容器启动时,会调用一个脚本检查数据库是否存在,如果存在则启动监听器和数据库,否则创建一个新数据库。

入口点目录是一种特殊的空目录,启动命令会扫描该目录中的额外脚本并以编程方式处理它们。目录位置和启动时如何评估内容完全取决于启动命令或脚本。

可以将卷或本地目录挂载到入口点目录,并使用自定义脚本填充它,从而更好地控制容器数据库。但由于该目录存在于容器外部,其内容的更改会影响未来的启动,可能导致意外结果。以下是挂载入口点目录的示例:

# 挂载单个目录
docker run ... \
       --mount type=bind,source=$HOME/dbscripts,target=/docker-entrypoint-initdb.d \
...
# 挂载两个单独的目录
docker run ... \
       --mount type=bind,source=$HOME/dbstartup,target=/opt/oracle/scripts/startup \
       --mount type=bind,source=$HOME/dbsetup,target=/opt/oracle/scripts/setup \
...

当这些目录中有多个脚本时,Oracle 建议添加数字前缀以确保执行顺序,例如:

01_first_step.sql
02_second_step.sh
03_last_step.sql
7. 空间管理
7.1 查看 Docker 对象的空间使用情况

运行 docker system df 命令可以查看系统上 Docker 对象的空间使用情况,示例输出如下:

TYPE           TOTAL  ACTIVE  SIZE     RECLAIMABLE
Images         29     2       157.3GB  143.7GB (91%)
Containers     2      2       540.6MB  0B (0%)
Local Volumes  8      0       15.42GB  15.42GB (100%)
Build Cache    138    0       41.05MB  41.05MB

该输出报告了 Docker 私有 /var/lib/docker 目录中使用空间的对象类型的摘要。

7.2 清理卷

通过 docker volume ls 命令查看卷列表时,可能会混淆本地卷和使用本地驱动的卷。可以通过 docker volume inspect 命令检查卷的元数据来确认。如果“ACTIVE”列下为零,则表示这些卷是孤立的,可以使用 docker volume prune 命令安全地删除它们。示例如下:

> docker volume prune
WARNING! This will remove all local volumes not used by at least one 
container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
84c91dc6a3f0cb354243cb9ee2ca5a79933ca67
oradata_ORCL1_data
oradata_ORCL1_diag
4efabdab067033c973d00e73de9a05121e0cb70
9fc0fdc81eabd71a07f28a8d79fd2c1a5606747
oradata_ORCL1_audit
b26d713b83be8ac8f549f825d19cc2b6e6f19a6
2ac7d7486083a10a4ed313699e06eba017e63ba

运行清理命令后,再次使用 docker system df 命令检查空间使用情况,可以看到本地卷的数量和占用空间变为零。

7.3 清理镜像

docker image prune 命令用于删除“悬空”镜像,即未被任何容器使用且未标记名称的镜像。悬空镜像通常是在重建现有镜像并使用相同标签时产生的。示例如下:

> docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B

如果有容器仍在使用悬空镜像, docker image prune 不会删除它,只有在移除所有依赖后才会删除。

7.4 清理容器

docker container prune 命令用于删除系统中所有停止的容器,但需要谨慎使用,因为停止的容器不一定是无用的。示例如下:

> docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
7.5 清理系统

docker system prune 命令可以一次性清理系统中的停止容器、悬空镜像、未使用的网络和构建缓存。但同样需要谨慎使用,因为它会删除大量数据。示例如下:

> docker system prune
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
8. 哪种卷类型最佳

选择容器存储方式时,有几个标准会影响决策:
| 标准 | 说明 |
| ---- | ---- |
| 访问和导航 | 容器保存到卷的数据是否对本地操作系统用户可见? |
| Docker 管理 | Docker 是否将存储作为对象进行管理,将其与容器关联,并报告卷是否被使用? |
| 目录创建 | Docker 是否会自动在主机上创建不存在的目录路径? |
| 目录所有权 | 对于自动创建的目录,所有权是否设置正确? |
| 备份和保存数据 | 有哪些机制可用于复制和保存数据? |
| 跨平台一致性 | 该解决方案在所有平台(Windows、Mac 和 Linux)上的行为是否相同? |

需要根据这些标准,结合 Oracle 数据库和用户的具体需求,包括创建容器和支持对象所需的步骤以及备份或复制数据库 oradata 卷内容的难易程度,来选择最佳的存储选项。

综上所述,在 Docker 容器的使用过程中,合理选择和管理存储对于数据的持久化、安全性和性能至关重要。通过了解不同的挂载方式、卷类型以及空间管理方法,可以更好地应对各种复杂的应用场景。

Docker 容器存储管理全解析

9. 不同存储方式的操作与分析
9.1 tmpfs 存储操作

tmpfs 存储适用于临时数据,在容器运行时使用,且无需持久化。以下是一个简单的使用 tmpfs 挂载的示例:

docker run -it --tmpfs /tmp:size=100m ubuntu bash

上述命令创建了一个 Ubuntu 容器,并将一个大小为 100MB 的 tmpfs 挂载到容器的 /tmp 目录。在容器内,所有写入 /tmp 目录的数据都将存储在内存中,不会写入主机文件系统。

9.2 卷与绑定挂载的操作对比

我们通过具体操作来更深入地对比卷与绑定挂载。

创建绑定挂载

docker run -v /host/path:/container/path ubuntu ls /container/path

这个命令将主机的 /host/path 目录绑定挂载到容器的 /container/path 目录,并列出容器内该目录的内容。

创建卷挂载

docker volume create myvolume
docker run -v myvolume:/container/path ubuntu ls /container/path

首先创建一个名为 myvolume 的卷,然后将其挂载到容器的 /container/path 目录。

通过这些操作可以发现,绑定挂载直接使用主机上的目录,而卷挂载使用 Docker 管理的卷对象。

10. 存储管理的流程图分析

下面是一个简单的 Docker 存储管理流程图,展示了不同存储方式的选择和清理流程:

graph TD
    A[选择存储方式] --> B{是否需要持久化}
    B -->|是| C[选择卷或绑定挂载]
    B -->|否| D[选择 tmpfs]
    C --> E{是否需要云存储}
    E -->|是| F[选择卷]
    E -->|否| G{是否方便管理所有权}
    G -->|是| H[选择卷]
    G -->|否| I[选择绑定挂载]
    J[空间管理] --> K{是否有未使用资源}
    K -->|是| L{资源类型}
    L -->|卷| M[docker volume prune]
    L -->|镜像| N[docker image prune]
    L -->|容器| O[docker container prune]
    L -->|系统| P[docker system prune]

从这个流程图可以清晰地看到,在选择存储方式时,需要考虑数据是否需要持久化、是否需要云存储以及所有权管理的便利性等因素。在进行空间管理时,根据不同的资源类型选择相应的清理命令。

11. 不同存储方式对 Oracle 数据库的影响

当在容器中运行 Oracle 数据库时,不同的存储方式会产生不同的影响。

11.1 绑定挂载的使用与问题

最初使用绑定挂载时,将 oradata 目录作为根目录,每个容器绑定挂载到自己的子目录。但随着容器和挂载的复杂性增加,管理变得困难。例如,当多个目录挂载到一个容器或多个容器共享目录时,很难跟踪每个目录的使用情况,删除目录可能会导致严重后果。

11.2 卷的优势体现

使用卷可以更好地管理 Oracle 数据库的存储。卷可以与容器建立更清晰的关联,Docker 能够更好地理解容器与外部数据的关系。例如,一个 Oracle 数据库容器可能需要多个卷,分别用于诊断目录、配置文件、共享脚本和数据。使用卷可以更方便地管理这些关联,并且卷的备份和迁移也更加容易。

12. 存储管理的最佳实践总结

为了更好地管理 Docker 容器的存储,以下是一些最佳实践:
- 合理选择存储方式 :根据数据的特性和需求,选择合适的存储方式。对于需要持久化的数据,优先考虑卷;对于临时数据,选择 tmpfs。
- 定期进行空间清理 :定期运行 docker system df 命令检查空间使用情况,并使用相应的清理命令删除未使用的资源,如 docker volume prune docker image prune 等。
- 使用卷管理复杂环境 :在复杂的容器环境中,使用卷来管理外部数据,让 Docker 更好地理解容器与数据的关系,便于跟踪和管理。
- 注意入口点目录的使用 :在使用入口点目录时,谨慎管理自定义脚本,避免因脚本问题导致容器启动异常。

13. 总结展望

通过对 Docker 容器存储管理的深入探讨,我们了解了不同的存储方式(如 tmpfs、卷和绑定挂载)的特点和使用方法,以及如何进行有效的空间管理。在实际应用中,需要根据具体的业务需求和环境特点,合理选择存储方式,并遵循最佳实践进行管理。

随着容器技术的不断发展,未来 Docker 可能会提供更多强大的存储管理功能,进一步简化存储管理的复杂性。对于开发者和运维人员来说,持续学习和掌握这些技术,将有助于更好地构建和管理容器化应用,提高系统的性能和可靠性。

希望本文能够帮助读者更好地理解 Docker 容器存储管理,在实际工作中更加得心应手地运用这些知识。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐