Docker 容器:分层、卷与环境定制

1. 容器存储与卷的概念

1.1 联合文件系统与容器分层

容器使用联合文件系统(Union Filesystems)来存储本地数据。联合文件系统允许从同一镜像运行的多个容器使用相同的文件,多个容器不会因共享原始镜像而占用双倍空间,这也是容器启动迅速且行为一致的原因。创建容器时,不会复制或更改镜像的内容,每个容器都使用(并复用)镜像本身的目录结构。

镜像提供联合文件系统的下层,而在其上的一层会保存每个容器所做的更改。通过“向下查看”这些层,每个容器可以看到下层(镜像)中未更改的文件,或者上层中保存的本地更改。

1.2 容器层存储数据的问题

然而,将数据存储在容器层存在一些问题:
- CPU 负载 :计算联合文件系统上下层之间的差异会增加主机 CPU 的负载。
- 存储限制 :容器层位于主机文件系统上,可能不足以容纳不断增长的数据库,也无法满足数据库的性能要求。
- 数据耐久性 :分配给容器的上层就是容器本身,删除容器会移除该层,同时也会删除数据库。

1.3 卷的解决方案

Docker 提供了卷(Volumes)来解决这些问题。容器卷将容器内的目录分配到本地主机上分层文件系统之外的存储位置。当容器将数据保存到挂载目录时,实际上是将其写入本地主机的持久存储中。

删除容器会移除上层和工作层以及其中的所有更改,且这些层一旦删除就无法恢复或重新创建。而卷独立于容器,删除父容器时不受影响。卷还可以像网络文件系统一样,在容器和主机之间甚至容器之间共享文件,并且可以利用高性能存储来存储数据。

1.4 卷在 Oracle 数据库中的应用

以 Oracle 数据库为例,将 oradata 目录挂载为卷可以使 Oracle 的易失性数据文件脱离联合文件系统,避免性能损失。数据库配置文件虽然不常更改,但也有其特殊的存储方式。

在启动 Oracle 数据库容器时,无论数据库是新是旧,都会将配置文件保存到 dbconfig 目录。容器中的 /etc/oratab 文件是复制而非链接到卷,因为 /etc 目录由 root 拥有, oracle 用户没有权限在那里创建链接。而 ORACLE_HOME 下的数据库配置文件实际上是链接,指向 /opt/oracle/oradata/dbconfig 下的文件。

每次启动脚本运行时,会检查数据库是否存在,如果存在,必要时会从预期位置重新创建链接到 /opt/oracle/oradata/dbconfig/$ORACLE_SID 目录中的文件。利用这一特性,可以非常快速地创建数据库克隆。只要在创建容器时传递的 ORACLE_SID 和其他特定于数据库的环境变量与卷上数据库中使用的值匹配,就可以使用 oradata 卷的内容在同一系统上克隆数据库、将数据复制到不同机器,甚至在不同主机操作系统之间共享数据而无需修改。

2. 容器环境定制

2.1 容器启动过程

运行容器类似于调用复杂的 shell 脚本,容器和 shell 脚本都执行一系列特定任务。Docker 提供了一种向 docker run 命令传递变量和值到容器私有环境的选项,这些环境变量就像 shell 脚本参数一样,允许一个镜像动态工作,支持各种需求或应用。

当 Oracle 数据库容器启动时, runOracle.sh 脚本会读取环境中的值,首先查找 ORACLE_SID ORACLE_PDB ,如果未定义,则会分配默认值,然后检查是否存在与该 SID 匹配的数据库。其启动过程流程图如下:

graph TD;
    A[启动容器] --> B[runOracle.sh 读取环境值];
    B --> C{ORACLE_SID 和 ORACLE_PDB 是否定义};
    C -- 否 --> D[分配默认值];
    C -- 是 --> E[跳过默认值分配];
    D --> F{是否存在匹配 SID 的数据库};
    E --> F;
    F -- 是 --> G[执行相应操作];
    F -- 否 --> H[创建数据库];

2.2 查看容器环境值

可以通过以下几种方式查看容器的环境值:
- 使用 env 命令 :登录到容器后,运行 env 命令并使用 grep 过滤出包含 ORACLE 的结果。例如:

docker exec -it ORCL bash
[oracle@8facfd649aa4 ~]$ env | grep ORACLE
ORACLE_BASE=/opt/oracle
ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
  • 使用 docker container inspect 命令 :该命令会输出近 300 行的 JSON 数据,不太友好。可以使用 --format 选项来限制和结构化输出:
docker container inspect --format='{{json .Config.Env}}' ORCL

也可以使用 jq 工具来格式化输出,使其更易读:

docker container inspect --format='{{json .Config.Env}}' ORCL | jq

或者使用扩展格式命令:

docker container inspect --format '{{range .Config.Env}}{{printf "%s\n" .}}{{end}}' ORCL

无论采用哪种格式,在未指定 ORACLE_SID ORACLE_PDB 的情况下,容器环境中都不会显示这两个变量。此外,容器还包含一些在“正常”数据库环境中看不到的变量,如 SLIMMING 以及标识目录、文件和脚本的变量。容器继承了其父镜像的环境设置,如果在创建容器时未提供 ORACLE_SID ORACLE_PDB ,数据库容器会分配默认值。

2.3 定制容器环境的方法

2.3.1 命令行选项

使用 -e 标志或其长形式 --env 标志,后跟单个键值对,例如:

docker run ... -e ORACLE_SID=TEST ...
docker run ... --env ORACLE_PDB=TESTPDB1 ...

如果要传递多个值,可以分别使用 -e 标志:

docker run ... -e ORACLE_SID=TEST -e ORACLE_PDB=TESTPDB1 ...

值也可以是本地环境中的变量,这对于创建多个容器很有用。例如,在 Linux 环境中使用 for 循环部署多个容器:

for dbname in DEV TEST STAGE
  do docker run -d --name $dbname -e ORACLE_SID=$dbname oracle/database:19.3.0-ee
done
2.3.2 从文件获取值

环境文件是文本文件,每行包含一个键值对,使用 --env-file 选项可以将文件中的值传递给 docker run 命令。例如,创建一个名为 db.env 的环境文件:

ORACLE_SID=TEST
ORACLE_PDB=TESTPDB1
ORACLE_EDITION=EE
ENABLE_ARCHIVELOG=true

然后在 docker run 命令中使用:

docker run ... --env-file db.env ...
2.3.3 从主机环境获取值

如果主机上存在与容器中同名的变量,只需提供变量名(无需等号和值)。这适用于命令行和环境文件选项。例如:

for ORACLE_SID in DEV TEST STAGE
  do docker run -d --name $ORACLE_SID -e ORACLE_SID oracle/database:19.3.0-ee
done

在环境文件中使用主机变量时,只需在单独的一行中包含变量名,Docker 会从主机环境中读取该值。需要注意的是,容器中的值在 docker run 执行时设置为主机的值,并写入容器元数据,主机上的值更改不会影响容器内的值。

综上所述,卷和环境定制是 Docker 容器中非常重要的特性,合理使用这些特性可以提高容器的性能、数据的耐久性和可管理性,同时为开发团队提供更灵活的工作方式。

3. 容器环境定制的优势与应用场景

3.1 容器环境定制的优势

  • 灵活性 :通过环境变量的定制,一个镜像可以根据不同的需求创建多个具有不同配置的容器。例如,在开发、测试和生产环境中,可以使用相同的基础镜像,通过传递不同的环境变量来配置不同的数据库实例。
  • 可维护性 :将配置信息以环境变量的形式管理,使得容器的配置更加清晰和易于维护。当需要更改配置时,只需修改环境变量的值,而不需要修改容器内部的代码或配置文件。
  • 可扩展性 :在创建多个容器时,可以通过脚本自动化地传递不同的环境变量,实现容器的快速部署和扩展。例如,使用 for 循环批量创建多个数据库容器。

3.2 应用场景

  • 开发与测试 :在开发和测试阶段,开发人员可以根据需要快速创建不同配置的数据库容器,进行功能测试、性能测试等。例如,通过传递不同的 ORACLE_SID ORACLE_PDB 创建多个测试数据库实例。
  • 生产环境 :在生产环境中,可以根据业务需求配置数据库容器的参数,如数据库版本、字符集、内存分配等。同时,利用卷的特性保证数据的持久性和可恢复性。
  • 故障排查与恢复 :当数据库出现问题时,可以利用卷的快照功能快速恢复数据库到之前的状态。例如,在数据库升级过程中出现问题,可以通过恢复卷的快照并重启容器来快速恢复数据库。

4. 总结与最佳实践

4.1 总结

本文介绍了 Docker 容器中联合文件系统、卷和环境定制的相关概念和操作方法。联合文件系统使得多个容器可以共享同一镜像的文件,提高了资源利用率;卷解决了容器层存储数据的问题,保证了数据的持久性和可恢复性;环境定制则为容器的配置提供了灵活性和可维护性。

4.2 最佳实践

  • 合理使用卷 :对于需要持久化存储的数据,如数据库文件、日志文件等,应使用卷进行存储。同时,定期对卷进行备份,以防止数据丢失。
  • 环境变量管理 :将容器的配置信息以环境变量的形式管理,避免将敏感信息硬编码在容器内部。可以使用环境文件来管理多个环境变量,提高配置的可维护性。
  • 自动化部署 :使用脚本自动化地创建和管理容器,提高部署效率和准确性。例如,使用 for 循环批量创建多个容器,或使用 CI/CD 工具实现容器的自动化部署。

以下是一个总结表格,展示了本文介绍的主要内容:
| 主题 | 内容 |
| ---- | ---- |
| 联合文件系统 | 多个容器可共享同一镜像文件,提高资源利用率 |
| 卷 | 解决容器层存储问题,保证数据持久性和可恢复性 |
| 环境定制 | 通过环境变量灵活配置容器,提高可维护性和扩展性 |
| 应用场景 | 开发测试、生产环境、故障排查与恢复 |
| 最佳实践 | 合理使用卷、环境变量管理、自动化部署 |

通过掌握这些知识和最佳实践,开发人员和运维人员可以更好地使用 Docker 容器,提高工作效率和系统的稳定性。在实际应用中,应根据具体需求选择合适的方法和工具,不断优化容器的配置和管理。

Logo

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

更多推荐