时髦大叔 發表於 2025-12-22 09:41:31

Docker Compose容器编排深度解析与实战小结

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">第一章 Docker Compose 核心定义与架构分析</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1.1 什么是 Docker Compose</a></li><li><a href="#_lab2_0_1">1.2 为什么引入 Docker Compose</a></li><li><a href="#_lab2_0_2">1.3 Docker Compose 的安装验证</a></li><li><a href="#_lab2_0_3">1.4 Docker Compose 的核心功能与使用场景</a></li><ul class="third_class_ul"><li><a href="#_label3_0_3_0">1.4.1 使用步骤</a></li><li><a href="#_label3_0_3_1">1.4.2 核心管理能力</a></li><li><a href="#_label3_0_3_2">1.4.3 典型使用场景</a></li></ul></ul><li><a href="#_label1">第二章 Docker Compose 配置文件(docker-compose.yml)详析</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_4">2.1 配置文件核心参数</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_5">2.2 核心指令:image</a></li><ul class="third_class_ul"><li><a href="#_label3_1_5_3">实战演练 prj1:基础容器启动</a></li></ul><li><a href="#_lab2_1_6">2.3 核心指令:command</a></li><ul class="third_class_ul"><li><a href="#_label3_1_6_4">实战演练 prj2:命令覆盖对比</a></li></ul><li><a href="#_lab2_1_7">2.4 核心指令:entrypoint</a></li><ul class="third_class_ul"><li><a href="#_label3_1_7_5">实战演练 prj3:入口点覆盖</a></li></ul><li><a href="#_lab2_1_8">2.5 核心指令:environment</a></li><ul class="third_class_ul"><li><a href="#_label3_1_8_6">实战演练:环境变量注入</a></li></ul><li><a href="#_lab2_1_9">2.6 核心指令:networks</a></li><ul class="third_class_ul"><li><a href="#_label3_1_9_7">实战演练 prj4:多网络连接</a></li></ul><li><a href="#_lab2_1_10">2.7 核心指令:volumes</a></li><ul class="third_class_ul"><li><a href="#_label3_1_10_8">实战演练 prj5:存储卷绑定</a></li></ul><li><a href="#_lab2_1_11">2.8 核心指令:ports</a></li><ul class="third_class_ul"><li><a href="#_label3_1_11_9">实战演练 prj6:端口发布</a></li></ul><li><a href="#_lab2_1_12">2.9 核心指令:expose</a></li><ul class="third_class_ul"><li><a href="#_label3_1_12_10">实战演练 prj7:内部暴露</a></li></ul><li><a href="#_lab2_1_13">2.10 核心指令:depends_on</a></li><ul class="third_class_ul"><li><a href="#_label3_1_13_11">实战演练 prj8:健康检查与顺序启动</a></li></ul><li><a href="#_lab2_1_14">2.11 核心指令:env_file</a></li><ul class="third_class_ul"><li><a href="#_label3_1_14_12">实战演练 prj9:多文件引用</a></li></ul></ul><li><a href="#_label2">第三章 Docker Compose 常用命令全集</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_15">3.1 命令行全局选项</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_2_16">3.2up命令深度解析</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_2_17">3.3down命令与数据清理</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_2_18">3.4run与一次性任务</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label3">第四章 综合操作案例:多层级拓扑构建</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_19">4.1 复杂拓扑关系配置</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_3_20">4.2 网络与持久化配置</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label4">第五章 企业级应用实战:部署 WordPress 站点</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_21">5.1 编写部署脚本</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_4_22">5.2 启动与验证</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label5">第六章 常见问题与总结</a></li><ul class="second_class_ul"><li><a href="#_lab2_5_23">6.1 up、run、start 的本质区别</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_5_24">6.2 总结</a></li><ul class="third_class_ul"></ul></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>第一章 Docker Compose 核心定义与架构分析</h2>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1.1 什么是 Docker Compose</h3>
<p>Docker Compose 是 Docker 官方推出的开源项目,其核心代码由 Python 编写。在技术实现层面,它通过直接调用 Docker 服务的 API 来实现对容器的精细化管理和编排。官方将 Docker Compose 定义为&ldquo;用于定义和运行多个 Docker 容器的应用工具&rdquo;。</p>
<p>在 Docker Compose 的体系结构中,存在两个至关重要的核心概念,这决定了其管理容器的逻辑维度:</p>
<ol><li><strong>服务 (Service)</strong>:服务代表一个应用容器。在实际生产中,一个服务可以由若干个运行相同镜像的容器实例组成。它是逻辑上的一个单元,通过镜像名、环境变量、网络配置等属性进行定义。</li><li><strong>项目 (Project)</strong>:项目是由一组互相关联的应用容器构成的完整业务单元。项目的所有属性都在 <code>docker-compose.yml</code> 文件中定义。可以说,一个 <code>docker-compose.yml</code> 文件就代表了一个完整的项目。</li></ol>
<p>Docker Compose 的默认管理对象是项目。通过一套简洁的子命令,开发人员可以对项目中的一组容器进行便捷的生命周期管理,从而极大地简化了多容器应用的协同工作。</p>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>1.2 为什么引入 Docker Compose</h3>
<p>Docker 遵循单一职责原则,其官方推荐在每个 Docker 容器中仅运行一个进程。这种轻量化的设计提高了镜像的复用性,但也给复杂应用的部署带来了挑战。</p>
<p>在实际开发中,一个完整的业务应用往往需要依赖多个环境,例如 Nginx 作为反向代理、MySQL 提供数据持久化、Redis 提供缓存服务。如果采用传统方式,我们需要分别为应用代码、MySQL 数据库和 Nginx 创建独立的容器,并逐一手动启动。</p>
<p>手动管理的弊端显而易见:</p>
<ul><li><strong>繁琐性</strong>:每次启动应用都需要执行多次 <code>docker run</code> 命令,或者编写复杂的 Shell 脚本。</li><li><strong>管理分散</strong>:容器之间是独立且分散的,不利于统一的镜像版本控制和资源分配。</li><li><strong>逻辑脱节</strong>:这些容器本质上是为同一个业务目标服务的,但在管理层面上却缺乏一个有机的整体视角。</li></ul>
<p>Docker Compose 的出现解决了上述问题。它将属于同一个业务逻辑的所有容器整合在一起,通过一个配置文件进行统筹。</p>
<p class="maodian"><a name="_lab2_0_2"></a></p><h3>1.3 Docker Compose 的安装验证</h3>
<p>在现代 Docker 环境中,<code>docker-compose-plugin</code> 通常作为默认组件被安装。这意味着在安装 Docker 时,Compose 功能已经集成在内。</p>
<p>通过执行以下命令可以验证安装情况:</p>
<div class="jb51code"><pre class="brush:bash;">docker compose version
</pre></div>
<p>执行结果如图所示:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209333990.jpg" /></p>
<p>图片显示了当前系统中 Docker Compose 的版本信息,这确认了环境已经准备就绪。</p>
<p class="maodian"><a name="_lab2_0_3"></a></p><h3>1.4 Docker Compose 的核心功能与使用场景</h3>
<p class="maodian"><a name="_label3_0_3_0"></a></p><h4>1.4.1 使用步骤</h4>
<p>使用 Compose 遵循标准的三步走策略:</p>
<ol><li><strong>定义环境</strong>:使用 Dockerfile 定义应用程序环境。</li><li><strong>定义服务</strong>:在 <code>docker-compose.yml</code> 中定义构成应用的服务,使其可以在隔离环境中协同运行。</li><li><strong>启动运行</strong>:执行 <code>docker compose up</code> 命令,一键启动并运行整个应用程序。</li></ol>
<p class="maodian"><a name="_label3_0_3_1"></a></p><h4>1.4.2 核心管理能力</h4>
<p>Compose 涵盖了应用程序全生命周期的命令体系:</p>
<ul><li>服务的启动、停止与自动化重建。</li><li>实时查看运行服务的状态监控。</li><li>流式传输并聚合查看所有运行服务的日志输出。</li><li>在特定服务上执行单次命令。</li></ul>
<p class="maodian"><a name="_label3_0_3_2"></a></p><h4>1.4.3 典型使用场景</h4>
<ul><li><strong>单主机部署</strong>:在开发或测试阶段,快速搭建单节点的完整环境。</li><li><strong>多环境隔离</strong>:通过指定不同的项目名称(Project Name),在同一台主机上运行多套相互隔离的环境(如开发环境、预发布环境)。</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>第二章 Docker Compose 配置文件(docker-compose.yml)详析</h2>
<p><code>docker-compose.yml</code> 是 Docker Compose 的核心,其采用 YAML 格式。YAML 语法的严谨性要求必须正确处理缩进和数据类型。</p>
<p class="maodian"><a name="_lab2_1_4"></a></p><h3>2.1 配置文件核心参数</h3>
<p>以下是一个标准的配置文件参数模板及其含义:</p>
<div class="jb51code"><pre class="brush:plain;">version: "3.8" # 定义版本,表示当前使用的 docker-compose 语法的版本
services: # 服务容器配置块
servicename: # 服务自定义名称,同时作为内部 bridge 网络的 DNS 名称
    image: # 必选,指定使用的镜像名称
    command: # 可选,覆盖镜像默认的 CMD 指令
    environment: # 可选,设置环境变量,等同于 docker run --env
    volumes: # 可选,挂载数据卷,等同于 docker run -v
    networks: # 可选,指定网络,等同于 docker run --network
    ports: # 可选,端口映射,等同于 docker run -p
    expose: # 可选,暴露内部端口,不映射到宿主机
    build: # 可选,指定 Dockerfile 所在的构建目录
    depends_on: # 可选,定义服务间的启动依赖关系
    env_file: # 可选,指定环境变量文件路径
volumes: # 定义全局数据卷
networks: # 定义全局网络</pre></div>
<p class="maodian"><a name="_lab2_1_5"></a></p><h3>2.2 核心指令:image</h3>
<p><code>image</code> 指令指定容器运行的基础镜像。它支持多种格式,包括本地镜像名、带标签的镜像名、带摘要(digest)的镜像名,以及来自私有仓库的镜像。</p>
<p>示例如下:</p>
<div class="jb51code"><pre class="brush:bash;">image: redis
image: redis:5
image: redis@sha256:0ed5d5928d4737458944eb604cc8509e245c3e19d02ad83935398bc4b991aac7
image: my_private.registry:5000/redis
</pre></div>
<p class="maodian"><a name="_label3_1_5_3"></a></p><h4>实战演练 prj1:基础容器启动</h4>
<p>首先创建一个名为 <code>mycompose</code> 的目录,在其下创建子目录 <code>prj1</code>,并编写 <code>docker-compose.yml</code>:</p>
<div class="jb51code"><pre class="brush:bash;">version: "3.8"
services:
web:
    image: nginx:1.23.3
</pre></div>
<p>如图所示,目录结构清晰,仅包含一个简单的 YAML 文件:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209333977.jpg" /></p>
<p>在 <code>prj1</code> 目录下执行启动命令:</p>
<div class="jb51code"><pre class="brush:bash;">docker compose up -d
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209333984.jpg" /></p>
<p>图中信息显示,Docker Compose 自动创建了一个默认网络(prj1_default)并启动了一个容器(prj1-web-1)。</p>
<p>通过 <code>docker ps -a</code> 和 <code>docker network ls</code> 进行查询验证:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334069.jpg" /></p>
<p>查询结果证实,容器和网络已经成功建立。</p>
<p>进一步查看网络详情:</p>
<div class="jb51code"><pre class="brush:bash;">docker inspect prj1_default</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334037.jpg" /></p>
<p>图中 Containers 节点下清晰地展示了该网络已经绑定了 <code>prj1-web-1</code> 容器。</p>
<p>通过 <code>docker compose ps</code> 可以直观查看当前项目下的服务状态:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334029.jpg" /></p>
<p>最后,执行 <code>docker compose down</code> 即可实现一键清理:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334082.jpg" /></p>
<p>图片显示,不仅容器被停止并删除,连同之前自动生成的项目网络也被一并移除,这体现了 Compose 的整洁管理特性。</p>
<p class="maodian"><a name="_lab2_1_6"></a></p><h3>2.3 核心指令:command</h3>
<p><code>command</code> 用于覆盖容器启动时的默认命令。</p>
<p class="maodian"><a name="_label3_1_6_4"></a></p><h4>实战演练 prj2:命令覆盖对比</h4>
<p>在 <code>prj1</code> 的基础上拷贝出 <code>prj2</code> 目录,并修改配置文件。<code>prj1</code> 保持默认启动 Nginx,而 <code>prj2</code> 则指定启动命令:</p>
<div class="jb51code"><pre class="brush:bash;">version: "3.8"
services:
web:
    image: nginx:1.23.3
    command: ["tail","-f","/etc/hosts"]</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334053.jpg" /></p>
<p>如图所示,我们将 <code>web</code> 服务的启动指令改为持续监控 <code>/etc/hosts</code> 文件。</p>
<p>分别启动 prj1 和 prj2 的服务。执行 <code>docker inspect</code> 查看容器详情:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334025.jpg" /></p>
<p>(这是 prj1 的默认 CMD 状态)</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334167.jpg" /></p>
<p>(这是 prj2 启动时的状态信息)</p>
<p>通过 <code>docker inspect prj2-web-1</code> 可以观察到其命令确实变更为我们指定的 <code>tail</code> 命令:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334185.jpg" /></p>
<p><strong>验证功能性差异</strong>:<br />对 <code>prj1-web-1</code> 执行 curl:</p>
<div class="jb51code"><pre class="brush:bash;">docker exec -it prj1-web-1 curl 127.0.0.1</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334112.jpg" /></p>
<p>返回了 Nginx 默认首页,说明 Nginx 进程正在运行。</p>
<p>对 <code>prj2-web-1</code> 执行 curl:</p>
<div class="jb51code"><pre class="brush:bash;">docker exec -it prj2-web-1 curl 127.0.0.1
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334281.jpg" /></p>
<p>系统提示拒绝连接。这是因为 <code>tail</code> 命令覆盖了 Nginx 的启动命令,容器内部并没有 Nginx 监听 80 端口。这有力地证明了 <code>command</code> 参数的生效机制。</p>
<p class="maodian"><a name="_lab2_1_7"></a></p><h3>2.4 核心指令:entrypoint</h3>
<p><code>entrypoint</code> 与 <code>command</code> 类似,但它直接覆盖镜像定义的入口点。</p>
<p class="maodian"><a name="_label3_1_7_5"></a></p><h4>实战演练 prj3:入口点覆盖</h4>
<p>编写 <code>prj3</code> 的配置文件,使用 <code>entrypoint</code> 查看系统发行版信息:</p>
<div class="jb51code"><pre class="brush:bash;">version: "3.8"
services:
web:
    image: nginx:1.23.3
    entrypoint:
      - tail
      - -f
      - /etc/os-release
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334265.jpg" /></p>
<p>启动服务后查看进程列表:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334241.jpg" /></p>
<p>图中 <code>COMMAND</code> 栏目明确显示容器正在执行 <code>tail -f /etc/os-release</code>。</p>
<p>查看 <code>docker inspect</code> 详情:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334383.jpg" /></p>
<p>Entrypoint 节点已经被完全重写。</p>
<p class="maodian"><a name="_lab2_1_8"></a></p><h3>2.5 核心指令:environment</h3>
<p>该参数用于向容器注入环境变量。</p>
<p class="maodian"><a name="_label3_1_8_6"></a></p><h4>实战演练:环境变量注入</h4>
<p>修改 <code>prj1</code> 的配置文件,加入自定义变量:</p>
<div class="jb51code"><pre class="brush:bash;">environment:
Test: 1
</pre></div>
<p>执行 <code>up -d</code> 后,Docker Compose 识别到配置变更,会自动触发容器重建(Recreate):</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334222.jpg" /></p>
<p>通过 <code>docker inspect</code> 验证环境变量:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334318.jpg" /></p>
<p>图中 Env 列表中清晰地出现了 <code>Test=1</code>。</p>
<p class="maodian"><a name="_lab2_1_9"></a></p><h3>2.6 核心指令:networks</h3>
<p>通过该指令可以精细化控制容器的网络连接。</p>
<p class="maodian"><a name="_label3_1_9_7"></a></p><h4>实战演练 prj4:多网络连接</h4>
<p>在 <code>prj4</code> 中定义两个自定义网络,并将 <code>web</code> 服务同时加入:</p>
<div class="jb51code"><pre class="brush:bash;">version: "3.8"
services:
web:
    image: nginx:1.23.3
    networks:
      - mywebnet1
      - mywebnet2
networks:
mywebnet1:
mywebnet2:
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334238.jpg" /></p>
<p>图中可见,Compose 同时创建了两个网络。</p>
<p>执行 <code>docker network ls</code> 可以观察到新生成的网络:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334385.jpg" /></p>
<p>通过 <code>inspect</code> 查看容器网络详情:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334330.jpg" /></p>
<p>容器成功获得了两个不同网段的 IP 地址,具备了跨网络通信的能力。</p>
<p class="maodian"><a name="_lab2_1_10"></a></p><h3>2.7 核心指令:volumes</h3>
<p><code>volumes</code> 实现宿主机与容器间的数据持久化与共享。</p>
<p class="maodian"><a name="_label3_1_10_8"></a></p><h4>实战演练 prj5:存储卷绑定</h4>
<p>在配置文件中将宿主机目录挂载到 Nginx 的网页根目录:</p>
<div class="jb51code"><pre class="brush:bash;">volumes:
- /data/kk/vol1:/usr/share/nginx/html/
</pre></div>
<p>在正式运行前,使用 <code>docker compose config</code> 检查语法:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334365.jpg" /></p>
<p>图中显示配置无误,挂载类型被标识为 <code>bind</code>。</p>
<p>启动服务:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334443.jpg" /></p>
<p>进入容器查看:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334362.jpg" /></p>
<p>在宿主机挂载点 <code>/data/kk/vol1</code> 下创建测试文件:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334451.jpg" /></p>
<p>再次 curl 访问:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334571.jpg" /></p>
<p>返回内容即为我们在宿主机编辑的内容,证明数据同步实时生效。</p>
<p class="maodian"><a name="_lab2_1_11"></a></p><h3>2.8 核心指令:ports</h3>
<p>用于指定容器端口与宿主机端口的映射关系。</p>
<p class="maodian"><a name="_label3_1_11_9"></a></p><h4>实战演练 prj6:端口发布</h4>
<div class="jb51code"><pre class="brush:bash;">ports:
- 8083:80
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334536.jpg" /></p>
<p>启动后,通过 <code>docker compose ps</code> 查看端口映射状态:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334558.jpg" /></p>
<p>或者使用 <code>docker port</code> 命令精确定位:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334512.jpg" /></p>
<p>直接通过宿主机 IP 加 8083 端口进行 curl 测试:</p>
<p>外部访问成功。</p>
<p class="maodian"><a name="_lab2_1_12"></a></p><h3>2.9 核心指令:expose</h3>
<p>与 <code>ports</code> 不同,<code>expose</code> 暴露的端口仅能被同一网络内的其他服务访问,不对外开放。</p>
<p class="maodian"><a name="_label3_1_12_10"></a></p><h4>实战演练 prj7:内部暴露</h4>
<p>在配置文件中设置暴露 3030 端口:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334516.jpg" /></p>
<p>启动服务进入容器内部进行测试:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334699.jpg" /></p>
<p>内部 curl 显示端口已开放,但切换到宿主机尝试 curl 该端口:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334644.jpg" /></p>
<p>宿主机无法连接,这展示了 <code>expose</code> 的安全隔离特性。</p>
<p class="maodian"><a name="_lab2_1_13"></a></p><h3>2.10 核心指令:depends_on</h3>
<p>该参数定义服务间的启动顺序和健康状态依赖。</p>
<p class="maodian"><a name="_label3_1_13_11"></a></p><h4>实战演练 prj8:健康检查与顺序启动</h4>
<p>我们希望 <code>web</code> 服务在 <code>mysql</code> 服务达到&ldquo;健康&rdquo;状态后再启动:</p>
<div class="jb51code"><pre class="brush:bash;">services:
web:
    depends_on:
      mysql:
      condition: service_healthy
mysql:
    image: mysql:5.7
    healthcheck:
      test: mysql -u root -phuyunkai -e 'select 1;'
      interval: 10s
      timeout: 5s
      retries: 10
</pre></div>
<p>执行启动命令:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334610.jpg" /></p>
<p>图中显示 <code>web</code> 服务处于 Waiting 状态,因为它在等待 <code>mysql</code> 完成健康检查。</p>
<p>当 <code>mysql</code> 变为 healthy 后,<code>web</code> 才会启动:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334689.jpg" /></p>
<p>停止服务时,Compose 会遵循逆序:先停止依赖者(web),再停止被依赖者(mysql):</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334773.jpg" /></p>
<p>重新启动也是同理,必须先验证被依赖服务的健康度:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334715.jpg" /></p>
<p class="maodian"><a name="_lab2_1_14"></a></p><h3>2.11 核心指令:env_file</h3>
<p>通过外部文件管理环境变量,提高配置的可维护性。</p>
<p class="maodian"><a name="_label3_1_14_12"></a></p><h4>实战演练 prj9:多文件引用</h4>
<p>在 <code>prj9</code> 目录下创建 <code>commenv</code> 和 <code>webenv</code> 文件:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334761.jpg" /></p>
<p>在 YAML 中引用:</p>
<div class="jb51code"><pre class="brush:yaml;">env_file:
- ./commenv
- ./webenv</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334839.jpg" /></p>
<p>启动服务并进入容器执行 <code>env</code> 命令:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334824.jpg" /></p>
<p>图中显示来自两个不同文件的环境变量都已经生效。</p>
<p class="maodian"><a name="_label2"></a></p><h2>第三章 Docker Compose 常用命令全集</h2>
<table><thead><tr><th>命令</th><th>功能描述</th></tr></thead><tbody><tr><td><code>up</code></td><td>构建、创建并启动所有容器,支持后台运行 <code>-d</code></td></tr><tr><td><code>down</code></td><td>停止并删除容器、网络、镜像以及卷(带 <code>-v</code>)</td></tr><tr><td><code>ps</code></td><td>列出项目中的所有容器状态</td></tr><tr><td><code>config</code></td><td>验证并查看 Compose 文件的实际配置</td></tr><tr><td><code>logs</code></td><td>查看容器的日志输出</td></tr><tr><td><code>exec</code></td><td>在运行中的容器内执行命令</td></tr><tr><td><code>restart</code></td><td>重启服务</td></tr><tr><td><code>stop</code> / <code>start</code></td><td>停止或启动现有的容器实例</td></tr><tr><td><code>run</code></td><td>在服务上运行一次性命令,常用于临时调试</td></tr><tr><td><code>port</code></td><td>打印服务的端口映射信息</td></tr></tbody></table>
<p class="maodian"><a name="_lab2_2_15"></a></p><h3>3.1 命令行全局选项</h3>
<ul><li><code>-f, --file</code>:指定配置文件。默认使用 <code>docker-compose.yml</code>。</li><li><code>-p, --project-name</code>:指定项目名。默认使用所在目录名。</li></ul>
<p>例如,指定一个非默认名称的配置文件启动:</p>
<div class="jb51code"><pre class="brush:bash;">docker compose -f ./prj1/docker-compose.txt up -d
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334899.jpg" /></p>
<p class="maodian"><a name="_lab2_2_16"></a></p><h3>3.2up命令深度解析</h3>
<p><code>up</code> 是最常用的指令。它集成了镜像拉取、容器创建、网络设置等一系列动作。</p>
<p><strong>前台运行</strong>:不加 <code>-d</code> 时,日志直接输出到控制台。按 <code>Ctrl+C</code> 会导致容器停止:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334884.jpg" /></p>
<p><strong>强制重建</strong>:使用 <code>--force-recreate</code>。当配置文件发生逻辑变更(如挂载路径修改)时非常有用:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334893.jpg" /></p>
<p class="maodian"><a name="_lab2_2_17"></a></p><h3>3.3down命令与数据清理</h3>
<p>默认 <code>down</code> 只删除容器和网络。如果需要清理关联的卷映射,必须加 <code>-v</code>:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334966.jpg" /></p>
<p class="maodian"><a name="_lab2_2_18"></a></p><h3>3.4run与一次性任务</h3>
<p><code>run</code> 主要用于在不影响主服务运行的情况下,启动一个新容器执行特定任务。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334897.jpg" /></p>
<p>注意:执行 <code>run</code> 时需要指定服务名而非镜像名。</p>
<p class="maodian"><a name="_label3"></a></p><h2>第四章 综合操作案例:多层级拓扑构建</h2>
<p class="maodian"><a name="_lab2_3_19"></a></p><h3>4.1 复杂拓扑关系配置</h3>
<p>设计一个包含 Nginx、MySQL 和 Redis 的三层架构,并设置严格的依赖与健康检查:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334987.jpg" /></p>
<p>核心配置逻辑:</p>
<ol><li>MySQL 和 Redis 设置 <code>healthcheck</code>。</li><li>Web 服务通过 <code>depends_on</code> 监控前两者的 <code>service_healthy</code> 状态。</li></ol>
<p>启动过程如图:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209334925.jpg" /></p>
<p>系统会精确控制启动时机,确保数据库就绪后应用才上线。</p>
<p class="maodian"><a name="_lab2_3_20"></a></p><h3>4.2 网络与持久化配置</h3>
<p>在拓扑基础上,加入自定义 bridge 网络 <code>mytestnetwork</code>,并挂载存储卷:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335040.jpg" /></p>
<p>通过 <code>docker compose ps</code> 确认所有组件均处于 Healthy 状态:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335049.jpg" /></p>
<p>访问映射端口 8085,可以看到自定义的首页:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335088.jpg" /></p>
<p class="maodian"><a name="_label4"></a></p><h2>第五章 企业级应用实战:部署 WordPress 站点</h2>
<p>WordPress 是一个典型的 Web + DB 的应用。我们需要配置 WordPress 访问数据库所需的四个关键环境变量:主机地址、用户名、密码、数据库名。</p>
<p class="maodian"><a name="_lab2_4_21"></a></p><h3>5.1 编写部署脚本</h3>
<p>在配置文件中,将 <code>db</code> 服务的健康状态作为 <code>wordpress</code> 启动的前置条件:</p>
<div class="jb51code"><pre class="brush:bash;">services:
wordpress:
    depends_on:
      db:
      condition: service_healthy
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: undoom
      WORDPRESS_DB_PASSWORD: huyunkai
db:
    image: mysql:5.7
    healthcheck:
      test: mysql -u root -proot -e "select 1;"
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335028.jpg" /></p>
<p class="maodian"><a name="_lab2_4_22"></a></p><h3>5.2 启动与验证</h3>
<p>执行 <code>up -d</code> 后,系统自动拉取镜像并按顺序创建环境:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335048.jpg" /></p>
<p>通过浏览器访问服务器端口,进入 WordPress 安装引导页面:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335029.jpg" /></p>
<p>配置完成后,一个完整的博客系统就运行起来了:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335018.jpg" /></p>
<p>由于配置了 <code>volumes</code>,即使执行 <code>down</code> 命令清理了容器,宿主机目录中的数据库文件和网页文件依然得以保留:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122209335080.jpg" /></p>
<p class="maodian"><a name="_label5"></a></p><h2>第六章 常见问题与总结</h2>
<p class="maodian"><a name="_lab2_5_23"></a></p><h3>6.1 up、run、start 的本质区别</h3>
<ul><li><strong>up</strong>:全自动化命令。它会检查镜像、网络、容器是否存在。如果不存在则创建,如果配置变动则重建,并将容器拉起到运行状态。</li><li><strong>run</strong>:侧重于执行。它会为指定的服务临时创建一个新容器来执行命令,执行完毕后容器通常处于停止状态。</li><li><strong>start</strong>:侧重于恢复。它只能启动已经存在且处于停止状态的容器,不会处理配置变更或重建逻辑。</li></ul>
<p class="maodian"><a name="_lab2_5_24"></a></p><h3>6.2 总结</h3>
<p>Docker Compose 通过声明式的 YAML 文件,将复杂的多容器协作关系简化为单一的配置文件管理。它不仅控制了服务的生命周期,还通过 <code>depends_on</code> 和 <code>healthcheck</code> 解决了服务启动的时序性问题。无论是对于本地开发环境的快速复现,还是中小型生产环境的快速部署,Docker Compose 都是不可或缺的利器。</p>
<p>到此这篇关于Docker Compose容器编排深度解析与实战小结的文章就介绍到这了,更多相关Docker Compose容器编排内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
頁: [1]
查看完整版本: Docker Compose容器编排深度解析与实战小结