<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Docker on 吾爱主机</title><link>https://blog.waihost.com/categories/docker/</link><description>Recent content in Docker on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 21 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/categories/docker/index.xml" rel="self" type="application/rss+xml"/><item><title>Docker 桥接网络与嵌入式 DNS：原理、连通性与工程实践</title><link>https://blog.waihost.com/posts/docker-bridge-network-embedded-dns/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/docker-bridge-network-embedded-dns/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/docker-bridge-network-embedded-dns.svg" alt="Featured image of post Docker 桥接网络与嵌入式 DNS：原理、连通性与工程实践" /&gt;&lt;p&gt;本地 &lt;code&gt;docker run&lt;/code&gt; 能通、Compose 里服务名却解析失败；两个容器“看起来在同一台机器”却互 ping 不通；或者明明没 &lt;code&gt;-p&lt;/code&gt;，却发现同网段容器已经能访问全部端口——这些日常问题，大多不是“Docker 坏了”，而是 &lt;strong&gt;bridge 网络模型 + DNS 解析路径&lt;/strong&gt; 理解不到位。&lt;/p&gt;
&lt;p&gt;本文基于 Docker 官方 &lt;em&gt;Networking overview&lt;/em&gt;、&lt;em&gt;Bridge network driver&lt;/em&gt;、&lt;em&gt;Packet filtering and firewalls&lt;/em&gt;、&lt;em&gt;Networking in Compose&lt;/em&gt; 与 &lt;em&gt;Legacy container links&lt;/em&gt;，把 &lt;strong&gt;默认 bridge 与用户自定义 bridge 的差异、嵌入式 DNS（&lt;code&gt;127.0.0.11&lt;/code&gt;）、端口可达性、防火墙/iptables 协作，以及可复现的连通性实验&lt;/strong&gt; 讲清楚，方便你在排障时按机制定位。&lt;/p&gt;
&lt;h2 id="一问题背景容器眼里只有网卡没有魔法互联"&gt;&lt;a href="#%e4%b8%80%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e5%ae%b9%e5%99%a8%e7%9c%bc%e9%87%8c%e5%8f%aa%e6%9c%89%e7%bd%91%e5%8d%a1%e6%b2%a1%e6%9c%89%e9%ad%94%e6%b3%95%e4%ba%92%e8%81%94" class="header-anchor"&gt;&lt;/a&gt;一、问题背景：容器眼里只有网卡，没有“魔法互联”
&lt;/h2&gt;&lt;p&gt;从容器视角看，网络很朴素：一张网卡、一个 IP、默认网关、路由表和 DNS。容器并不知道对端是不是另一个 Docker 容器，也不知道自己挂在 default bridge 还是自定义网络上。&lt;/p&gt;
&lt;p&gt;因此，工程里真正要掌握的是 &lt;strong&gt;Docker 如何在宿主机上拼出这张网&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用哪种 &lt;strong&gt;network driver&lt;/strong&gt; 创建隔离域；&lt;/li&gt;
&lt;li&gt;容器如何获得 IP，以及如何访问外网（masquerading）；&lt;/li&gt;
&lt;li&gt;同网/跨网时，&lt;strong&gt;哪些端口天然可达，哪些必须 publish&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;名字解析走 &lt;strong&gt;宿主 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 拷贝&lt;/strong&gt;，还是 Docker &lt;strong&gt;嵌入式 DNS&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把这四条对齐后，再看 &lt;code&gt;docker network inspect&lt;/code&gt; 和 &lt;code&gt;cat /etc/resolv.conf&lt;/code&gt;，排障会快很多。&lt;/p&gt;
&lt;h2 id="二核心机制bridge-网络在做什么"&gt;&lt;a href="#%e4%ba%8c%e6%a0%b8%e5%bf%83%e6%9c%ba%e5%88%b6bridge-%e7%bd%91%e7%bb%9c%e5%9c%a8%e5%81%9a%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;二、核心机制：bridge 网络在做什么
&lt;/h2&gt;&lt;h3 id="21-软件桥与隔离边界"&gt;&lt;a href="#21-%e8%bd%af%e4%bb%b6%e6%a1%a5%e4%b8%8e%e9%9a%94%e7%a6%bb%e8%be%b9%e7%95%8c" class="header-anchor"&gt;&lt;/a&gt;2.1 软件桥与隔离边界
&lt;/h3&gt;&lt;p&gt;Bridge 驱动在 Linux 上使用软件网桥：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同一 bridge 网络内的容器可互通；&lt;/li&gt;
&lt;li&gt;默认阻止来自其他 bridge 网络、以及 Docker 宿主机之外的直接访问；&lt;/li&gt;
&lt;li&gt;通过 &lt;strong&gt;masquerading（源地址伪装）&lt;/strong&gt; 让容器出网时，外部只看到宿主机 IP；&lt;/li&gt;
&lt;li&gt;支持 &lt;strong&gt;port publishing&lt;/strong&gt;，把容器端口映射到宿主机地址，供外部或其他网络访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方明确：bridge 面向 &lt;strong&gt;同一 Docker daemon 主机&lt;/strong&gt; 上的容器；跨主机通信需要 OS 级路由或 &lt;strong&gt;overlay&lt;/strong&gt; 等方案。&lt;/p&gt;
&lt;h3 id="22-内置驱动速览选型锚点"&gt;&lt;a href="#22-%e5%86%85%e7%bd%ae%e9%a9%b1%e5%8a%a8%e9%80%9f%e8%a7%88%e9%80%89%e5%9e%8b%e9%94%9a%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;2.2 内置驱动速览（选型锚点）
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Driver&lt;/th&gt;
					&lt;th&gt;典型用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;bridge&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;默认驱动；同机容器互联（本文重点）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;去掉网络隔离，直接用宿主机网络栈&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;完全隔离，无网络&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;overlay&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;多 daemon / Swarm 跨节点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;macvlan&lt;/code&gt; / &lt;code&gt;ipvlan&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;让容器更像“物理网”上的设备 / VLAN 集成&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;没有特殊需求时，应用栈优先用 &lt;strong&gt;用户自定义 bridge&lt;/strong&gt;，而不是长期堆在 default &lt;code&gt;bridge&lt;/code&gt; 上。&lt;/p&gt;
&lt;h3 id="23-默认-bridge-vs-用户自定义-bridge必须分清"&gt;&lt;a href="#23-%e9%bb%98%e8%ae%a4-bridge-vs-%e7%94%a8%e6%88%b7%e8%87%aa%e5%ae%9a%e4%b9%89-bridge%e5%bf%85%e9%a1%bb%e5%88%86%e6%b8%85" class="header-anchor"&gt;&lt;/a&gt;2.3 默认 bridge vs 用户自定义 bridge（必须分清）
&lt;/h3&gt;&lt;p&gt;Docker 首次启动会自动创建名为 &lt;code&gt;bridge&lt;/code&gt; 的 &lt;strong&gt;default bridge&lt;/strong&gt;。未指定 &lt;code&gt;--network&lt;/code&gt; 的容器会连上它。&lt;/p&gt;
&lt;p&gt;官方对用户自定义 bridge 的结论很直接：&lt;strong&gt;User-defined bridge networks are superior to the default bridge network.&lt;/strong&gt; 关键差异如下。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;default bridge&lt;/th&gt;
					&lt;th&gt;用户自定义 bridge&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;名字解析&lt;/td&gt;
					&lt;td&gt;默认只能靠 IP；&lt;code&gt;--link&lt;/code&gt; 为 legacy&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;自动 DNS&lt;/strong&gt;：可用容器名/别名互访&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;隔离&lt;/td&gt;
					&lt;td&gt;未指定网络的容器都挤在一起，无关栈可能互通&lt;/td&gt;
					&lt;td&gt;只有加入该网络的容器互通，作用域清晰&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;热插拔&lt;/td&gt;
					&lt;td&gt;要从 default bridge 换走，通常需停容器重建&lt;/td&gt;
					&lt;td&gt;运行中可用 &lt;code&gt;docker network connect/disconnect&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;配置粒度&lt;/td&gt;
					&lt;td&gt;全局一份（常需改 daemon 并重启）&lt;/td&gt;
					&lt;td&gt;每个网络可独立 &lt;code&gt;docker network create&lt;/code&gt; 配置&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;生产建议&lt;/td&gt;
					&lt;td&gt;官方视为 legacy 细节，&lt;strong&gt;不建议生产依赖&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;推荐作为同机服务互联默认选择&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;同一用户自定义 bridge 内的容器，&lt;strong&gt;彼此相当于暴露全部端口&lt;/strong&gt;；若要对其他网络或宿主机外访问，才需要 &lt;code&gt;-p/--publish&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="24-嵌入式-dns1270011"&gt;&lt;a href="#24-%e5%b5%8c%e5%85%a5%e5%bc%8f-dns1270011" class="header-anchor"&gt;&lt;/a&gt;2.4 嵌入式 DNS：&lt;code&gt;127.0.0.11&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;DNS 是 default bridge 与自定义网络最容易踩坑的分界线：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;default bridge&lt;/strong&gt;：容器通常拿到宿主 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 的&lt;strong&gt;拷贝&lt;/strong&gt;，按宿主配置解析公网名；&lt;strong&gt;默认不能靠容器名互访&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自定义网络（含 Compose 默认项目网络）&lt;/strong&gt;：使用 Docker &lt;strong&gt;embedded DNS&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;地址固定为 &lt;strong&gt;&lt;code&gt;127.0.0.11&lt;/code&gt;&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有 IPv6 等价地址&lt;/strong&gt;，IPv6-only 容器仍用该 IPv4 地址；&lt;/li&gt;
&lt;li&gt;容器名/别名解析在嵌入式 DNS 完成；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部域名查询会转发到宿主上配置的 DNS&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若应用硬编码了 DNS 服务器地址，官方建议写 &lt;strong&gt;&lt;code&gt;127.0.0.11&lt;/code&gt;&lt;/strong&gt;（在自定义网络场景下），而不是宿主上的 &lt;code&gt;8.8.8.8&lt;/code&gt; 之类——后者会绕过容器名解析。&lt;/p&gt;
&lt;p&gt;可用标志（&lt;code&gt;docker run&lt;/code&gt; / &lt;code&gt;docker create&lt;/code&gt;）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;标志&lt;/th&gt;
					&lt;th&gt;作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;--dns&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;指定 DNS 服务器；注意解析发生在&lt;strong&gt;容器网络命名空间&lt;/strong&gt;，&lt;code&gt;--dns=127.0.0.1&lt;/code&gt; 指容器自己的 loopback&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;--dns-search&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;搜索域&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;--dns-opt&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;resolv.conf&lt;/code&gt; 选项&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;--hostname&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;容器主机名（默认多为容器 ID）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;宿主 &lt;code&gt;/etc/hosts&lt;/code&gt; 的自定义条目&lt;strong&gt;不会自动继承&lt;/strong&gt;进容器；额外主机名用 &lt;code&gt;--add-host&lt;/code&gt; / Compose &lt;code&gt;extra_hosts&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="25-端口可达性与防火墙协作"&gt;&lt;a href="#25-%e7%ab%af%e5%8f%a3%e5%8f%af%e8%be%be%e6%80%a7%e4%b8%8e%e9%98%b2%e7%81%ab%e5%a2%99%e5%8d%8f%e4%bd%9c" class="header-anchor"&gt;&lt;/a&gt;2.5 端口可达性与防火墙协作
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;同 bridge 网络：容器端口对&lt;strong&gt;宿主&lt;/strong&gt;及&lt;strong&gt;同网其他容器&lt;/strong&gt;可达；&lt;/li&gt;
&lt;li&gt;默认配置下，&lt;strong&gt;其他 bridge 网络&lt;/strong&gt;或&lt;strong&gt;宿主机外&lt;/strong&gt;访问需要 publish；&lt;/li&gt;
&lt;li&gt;Linux 上 Docker 会创建防火墙规则以实现隔离、端口发布与 NAT；&lt;strong&gt;不要随意改 Docker 生成的规则&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;默认后端多为 &lt;strong&gt;iptables&lt;/strong&gt;，也支持 &lt;strong&gt;nftables&lt;/strong&gt;；bridge 上两者功能等价；&lt;/li&gt;
&lt;li&gt;对 &lt;code&gt;ipvlan&lt;/code&gt; / &lt;code&gt;macvlan&lt;/code&gt; / &lt;code&gt;host&lt;/code&gt;，Docker &lt;strong&gt;不创建&lt;/strong&gt;这套 bridge 规则；&lt;/li&gt;
&lt;li&gt;Docker 常会打开 IP 转发，并可能把转发默认策略设为 drop（防止宿主机无意变成路由器）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker 与 ufw 不兼容的常见根因&lt;/strong&gt;：Docker 在 &lt;code&gt;nat&lt;/code&gt; 表改写流量，可能在 ufw 的 INPUT/OUTPUT 之前分流，导致“ufw 规则像没生效”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常用网络级选项（&lt;code&gt;docker network create --opt&lt;/code&gt;）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Option&lt;/th&gt;
					&lt;th&gt;默认&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;com.docker.network.bridge.enable_ip_masquerade&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;出网伪装&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;com.docker.network.bridge.enable_icc&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;同网容器互通；可关以强制“只经代理/发布口访问”&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;com.docker.network.bridge.host_binding_ipv4&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;全部地址&lt;/td&gt;
					&lt;td&gt;未写主机 IP 时的默认绑定地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;com.docker.network.driver.mtu&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0&lt;/code&gt;（不限制）&lt;/td&gt;
					&lt;td&gt;MTU&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="26-compose-如何落地同一套模型"&gt;&lt;a href="#26-compose-%e5%a6%82%e4%bd%95%e8%90%bd%e5%9c%b0%e5%90%8c%e4%b8%80%e5%a5%97%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;2.6 Compose 如何落地同一套模型
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;docker compose up&lt;/code&gt; 默认会创建名为 &lt;strong&gt;&lt;code&gt;&amp;lt;project-name&amp;gt;_default&lt;/code&gt;&lt;/strong&gt; 的 bridge 网络，服务全部加入，并向内部 DNS 注册&lt;strong&gt;服务名&lt;/strong&gt;。因此 Compose 里写 &lt;code&gt;http://api:8080&lt;/code&gt; 往往能通，而“裸 default bridge 起两个容器却写容器名”会失败——这不是 Compose 魔法，而是 &lt;strong&gt;自定义网络 + 嵌入式 DNS&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;还可声明 &lt;code&gt;internal: true&lt;/code&gt; 的网络（无默认出网网关），把数据库等后端放在“只能被同项目服务访问”的网段上。&lt;/p&gt;
&lt;h2 id="三实践可复现的连通性与-dns-实验"&gt;&lt;a href="#%e4%b8%89%e5%ae%9e%e8%b7%b5%e5%8f%af%e5%a4%8d%e7%8e%b0%e7%9a%84%e8%bf%9e%e9%80%9a%e6%80%a7%e4%b8%8e-dns-%e5%ae%9e%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;三、实践：可复现的连通性与 DNS 实验
&lt;/h2&gt;&lt;p&gt;下面命令假定本机已安装 Docker Engine。若无 &lt;code&gt;alpine&lt;/code&gt; 镜像，会自动拉取。&lt;/p&gt;
&lt;h3 id="31-观察-default-bridge能通-ip不能靠名字"&gt;&lt;a href="#31-%e8%a7%82%e5%af%9f-default-bridge%e8%83%bd%e9%80%9a-ip%e4%b8%8d%e8%83%bd%e9%9d%a0%e5%90%8d%e5%ad%97" class="header-anchor"&gt;&lt;/a&gt;3.1 观察 default bridge：能通 IP，不能靠名字
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 两个容器都挂在 default bridge（未指定 --network）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name dns-a alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name dns-b alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 看 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker inspect -f &lt;span class="s1"&gt;&amp;#39;{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}&amp;#39;&lt;/span&gt; dns-a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IP_A&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;docker inspect -f &lt;span class="s1"&gt;&amp;#39;{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}&amp;#39;&lt;/span&gt; dns-a&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# IP 互通通常成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; dns-b ping -c1 &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$IP_A&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 名字解析：在 default bridge 上通常失败（无嵌入式 DNS 的容器名互访）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; dns-b ping -c1 dns-a &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;name resolve failed as expected on default bridge&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 看 resolv.conf：往往是宿主 DNS 拷贝，而不是 127.0.0.11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; dns-b cat /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;清理：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker rm -f dns-a dns-b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="32-用户自定义-bridge容器名解析--1270011"&gt;&lt;a href="#32-%e7%94%a8%e6%88%b7%e8%87%aa%e5%ae%9a%e4%b9%89-bridge%e5%ae%b9%e5%99%a8%e5%90%8d%e8%a7%a3%e6%9e%90--1270011" class="header-anchor"&gt;&lt;/a&gt;3.2 用户自定义 bridge：容器名解析 + &lt;code&gt;127.0.0.11&lt;/code&gt;
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create demo-net
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name web --network demo-net alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name db --network demo-net alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 名字应可解析&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; web ping -c1 db
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; db ping -c1 web
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 嵌入式 DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; web cat /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 期望 nameserver 为 127.0.0.11&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 同网端口：无需 -p，另一容器即可访问（这里用 nc 示意）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; -d db sh -c &lt;span class="s1"&gt;&amp;#39;echo hi | nc -l -p 9000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; web sh -c &lt;span class="s1"&gt;&amp;#39;nc -w 2 db 9000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行中热挂网络：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name side alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt; &lt;span class="c1"&gt;# 先在 default bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network connect demo-net side
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; side ping -c1 web
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network disconnect demo-net side
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;清理：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker rm -f web db side
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network rm demo-net
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="33-前后端分离网络拓扑多网卡容器"&gt;&lt;a href="#33-%e5%89%8d%e5%90%8e%e7%ab%af%e5%88%86%e7%a6%bb%e7%bd%91%e7%bb%9c%e6%8b%93%e6%89%91%e5%a4%9a%e7%bd%91%e5%8d%a1%e5%ae%b9%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;3.3 “前后端分离网络”拓扑（多网卡容器）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create frontend
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create --internal backend &lt;span class="c1"&gt;# 无默认出网，适合数据库平面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name api --network frontend alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network connect backend api
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name pg --network backend alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# api 能访问 pg（同 backend）；pg 默认不应依赖出网&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; api ping -c1 pg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 可选：限制默认绑定，仅本机可访问已发布端口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --opt com.docker.network.bridge.host_binding_ipv4&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; local-only
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="34-compose-最小可运行示意"&gt;&lt;a href="#34-compose-%e6%9c%80%e5%b0%8f%e5%8f%af%e8%bf%90%e8%a1%8c%e7%a4%ba%e6%84%8f" class="header-anchor"&gt;&lt;/a&gt;3.4 Compose 最小可运行示意
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# compose.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;web&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sleep 3600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;api]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sleep 3600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;default, backend]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sleep 3600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;backend]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; web ping -c1 api &lt;span class="c1"&gt;# 服务名 DNS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; api ping -c1 db
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose down
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="35-关闭-icc强制不能随意横穿"&gt;&lt;a href="#35-%e5%85%b3%e9%97%ad-icc%e5%bc%ba%e5%88%b6%e4%b8%8d%e8%83%bd%e9%9a%8f%e6%84%8f%e6%a8%aa%e7%a9%bf" class="header-anchor"&gt;&lt;/a&gt;3.5 关闭 ICC（强制“不能随意横穿”）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --opt com.docker.network.bridge.enable_icc&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; no-icc-net
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 同网容器在 ICC 关闭后，默认相互连通会被限制&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 常用于强制流量只经反向代理或显式发布路径（结合具体策略验证）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network rm no-icc-net
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="四常见坑与排查清单"&gt;&lt;a href="#%e5%9b%9b%e5%b8%b8%e8%a7%81%e5%9d%91%e4%b8%8e%e6%8e%92%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;四、常见坑与排查清单
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;症状&lt;/th&gt;
					&lt;th&gt;优先检查&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;容器名 ping 不通&lt;/td&gt;
					&lt;td&gt;是否挂在 &lt;strong&gt;default bridge&lt;/strong&gt;？自定义网络里 &lt;code&gt;cat /etc/resolv.conf&lt;/code&gt; 是否为 &lt;code&gt;127.0.0.11&lt;/code&gt;？&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Compose 服务名不通&lt;/td&gt;
					&lt;td&gt;是否在同一 project 网络？服务是否显式挂到不同 network 且未共享？&lt;code&gt;docker network inspect &amp;lt;project&amp;gt;_default&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;外网不能访问容器&lt;/td&gt;
					&lt;td&gt;是否 &lt;code&gt;-p/--publish&lt;/code&gt;？安全组/云防火墙？绑定是否被 &lt;code&gt;host_binding_ipv4=127.0.0.1&lt;/code&gt; 限制在本机？&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;容器不能出网&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;enable_ip_masquerade&lt;/code&gt;？宿主 IP 转发？DNS 是否异常？&lt;code&gt;iptables=false&lt;/code&gt; 是否误关 Docker 防火墙规则？&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ufw “放行了仍异常”&lt;/td&gt;
					&lt;td&gt;已知 Docker 与 ufw 规则路径冲突，publish 流量可能绕过 ufw 链&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;--link&lt;/code&gt; 老项目&lt;/td&gt;
					&lt;td&gt;官方标为 &lt;strong&gt;legacy&lt;/strong&gt;，推荐迁移到 user-defined networks；环境变量共享改用 env/volume/Compose&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;单网容器极多不稳定&lt;/td&gt;
					&lt;td&gt;内核限制：单 bridge 约 &lt;strong&gt;1000+&lt;/strong&gt; 容器时可能不稳定（见 moby#44973）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多网卡默认路由怪&lt;/td&gt;
					&lt;td&gt;多网络时默认网关可能变化；可用 &lt;code&gt;gw-priority&lt;/code&gt; 指定优先网关&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;宿主 hosts 改了容器不生效&lt;/td&gt;
					&lt;td&gt;宿主 &lt;code&gt;/etc/hosts&lt;/code&gt; 不继承；用 &lt;code&gt;--add-host&lt;/code&gt; / &lt;code&gt;extra_hosts&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;推荐排查命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network inspect &amp;lt;net&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker inspect -f &lt;span class="s1"&gt;&amp;#39;{{json .NetworkSettings.Networks}}&amp;#39;&lt;/span&gt; &amp;lt;ctr&amp;gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;ctr&amp;gt; cat /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;ctr&amp;gt; ip route
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;ctr&amp;gt; getent hosts &amp;lt;peer-name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 宿主侧（Linux）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ip link show &lt;span class="nb"&gt;type&lt;/span&gt; bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 注意：不要随手 flush Docker 管理的 iptables 链&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="五工程建议可直接落到规范"&gt;&lt;a href="#%e4%ba%94%e5%b7%a5%e7%a8%8b%e5%bb%ba%e8%ae%ae%e5%8f%af%e7%9b%b4%e6%8e%a5%e8%90%bd%e5%88%b0%e8%a7%84%e8%8c%83" class="header-anchor"&gt;&lt;/a&gt;五、工程建议（可直接落到规范）
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;新项目默认创建用户自定义 bridge&lt;/strong&gt;（或直接用 Compose 项目网络），避免依赖 default bridge 与 &lt;code&gt;--link&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务发现用名字，不写死容器 IP&lt;/strong&gt;；IP 会随重建变化，DNS 是稳定契约。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前后端分层网络&lt;/strong&gt;：对外服务进可出网网络，数据库进 &lt;code&gt;internal&lt;/code&gt; 网络；需要两边都通的 BFF/API 同时 attach。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只 publish 必要端口&lt;/strong&gt;，生产优先绑定具体主机地址，避免 &lt;code&gt;0.0.0.0&lt;/code&gt; 无脑暴露。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要手改 Docker 生成的防火墙规则&lt;/strong&gt;；若必须对接企业防火墙，走官方允许的集成方式，而不是 &lt;code&gt;iptables=false&lt;/code&gt; 一刀切。&lt;/li&gt;
&lt;li&gt;应用若强制配置 DNS，自定义网络场景优先 &lt;strong&gt;&lt;code&gt;127.0.0.11&lt;/code&gt;&lt;/strong&gt;，以免丢掉容器名解析。&lt;/li&gt;
&lt;li&gt;跨主机通信升级到 &lt;strong&gt;overlay / 服务网格 / 云网络&lt;/strong&gt;，不要指望单机 bridge “自动跨机”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="六总结"&gt;&lt;a href="#%e5%85%ad%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;六、总结
&lt;/h2&gt;&lt;p&gt;Docker 单机互联的主干是 &lt;strong&gt;bridge&lt;/strong&gt;：软件桥提供 L2 邻接与隔离，masquerade 解决出网，publish 解决入站，&lt;strong&gt;嵌入式 DNS（&lt;code&gt;127.0.0.11&lt;/code&gt;）&lt;/strong&gt; 解决自定义网络上的名字发现。default bridge 适合“随手跑个容器”，但缺少容器名 DNS、隔离弱、配置粗，官方更推荐 &lt;strong&gt;user-defined bridge&lt;/strong&gt;；Compose 的服务名互通，本质上就是把这套模型产品化了。&lt;/p&gt;
&lt;p&gt;记住一条排障主线：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;先看容器挂在哪张网 → 再看 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 是不是 &lt;code&gt;127.0.0.11&lt;/code&gt; → 再判断该不该有 publish → 最后才怀疑防火墙/宿主路由。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;按这条线做，大部分“容器网络玄学”都会变成可验证的配置问题。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/engine/network/" target="_blank" rel="noopener"
 &gt;Networking overview&lt;/a&gt;（官方；DNS / default bridge / drivers）&lt;/li&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/engine/network/drivers/bridge/" target="_blank" rel="noopener"
 &gt;Bridge network driver&lt;/a&gt;（官方；default vs user-defined、选项表、ICC/masquerade）&lt;/li&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/engine/network/drivers/" target="_blank" rel="noopener"
 &gt;Network drivers&lt;/a&gt;（官方；bridge/host/overlay/macvlan 等选型）&lt;/li&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/engine/network/packet-filtering-firewalls/" target="_blank" rel="noopener"
 &gt;Packet filtering and firewalls&lt;/a&gt;（官方；iptables/nftables、ufw 注意点）&lt;/li&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/compose/how-tos/networking/" target="_blank" rel="noopener"
 &gt;Networking in Compose&lt;/a&gt;（官方；项目默认网络与服务名 DNS）&lt;/li&gt;
&lt;li&gt;Docker Docs — &lt;a class="link" href="https://docs.docker.com/engine/network/links/" target="_blank" rel="noopener"
 &gt;Legacy container links&lt;/a&gt;（官方；&lt;code&gt;--link&lt;/code&gt; legacy 与迁移建议）&lt;/li&gt;
&lt;li&gt;源码文档镜像（核验用）：&lt;a class="link" href="https://github.com/docker/docs/tree/main/content/manuals/engine/network" target="_blank" rel="noopener"
 &gt;docker/docs &lt;code&gt;content/manuals/engine/network/&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Docker Compose 5.3 原生 pre_start 初始化容器实战：让本地编排更接近 Kubernetes Init Container</title><link>https://blog.waihost.com/posts/docker-compose-5-3-pre-start-guide/</link><pubDate>Wed, 08 Jul 2026 09:08:00 +0800</pubDate><guid>https://blog.waihost.com/posts/docker-compose-5-3-pre-start-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/docker-compose-5-3-pre-start-guide.svg" alt="Featured image of post Docker Compose 5.3 原生 pre_start 初始化容器实战：让本地编排更接近 Kubernetes Init Container" /&gt;&lt;p&gt;Docker Compose 在 2026 年 7 月连续发布了 &lt;code&gt;v5.3.0&lt;/code&gt; 和 &lt;code&gt;v5.3.1&lt;/code&gt;。其中 &lt;code&gt;v5.3.1&lt;/code&gt; 主要是内部流程与依赖更新；真正值得一线工程团队关注的变化，是 &lt;code&gt;v5.3.0&lt;/code&gt; 引入了对 Compose Spec &lt;code&gt;pre_start&lt;/code&gt; 生命周期钩子的原生支持。官方发布说明明确写到：这个版本 introduces native support for init containers；对应合并的 PR 说明也进一步确认，&lt;code&gt;pre_start&lt;/code&gt; 会以临时容器形式在服务容器启动前运行，非零退出码会阻止服务启动。&lt;/p&gt;
&lt;p&gt;这意味着，过去很多只能写进镜像入口脚本、Makefile 或 CI/CD 脚本里的初始化动作，现在可以被声明在 &lt;code&gt;compose.yaml&lt;/code&gt; 中：等待依赖、初始化共享卷、生成配置、执行轻量迁移、预热本地开发数据，都可以变成 Compose 编排的一部分。对于把 Docker Compose 当作本地开发、集成测试和小规模部署工具的团队来说，这次更新比普通补丁版本更有工程价值。&lt;/p&gt;
&lt;h2 id="版本与来源核验"&gt;&lt;a href="#%e7%89%88%e6%9c%ac%e4%b8%8e%e6%9d%a5%e6%ba%90%e6%a0%b8%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;版本与来源核验
&lt;/h2&gt;&lt;p&gt;本文基于多源官方资料整理，而不是复制单篇网文：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;版本&lt;/th&gt;
					&lt;th style="text-align: right"&gt;发布时间&lt;/th&gt;
					&lt;th&gt;关键信息&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Docker Compose &lt;code&gt;v5.3.0&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: right"&gt;2026-07-02&lt;/td&gt;
					&lt;td&gt;引入原生 init containers / &lt;code&gt;pre_start&lt;/code&gt; 支持，包含 OCI token transport、&lt;code&gt;run&lt;/code&gt; 事件作用域等修复&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Docker Compose &lt;code&gt;v5.3.1&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: right"&gt;2026-07-07&lt;/td&gt;
					&lt;td&gt;最新补丁版本，主要包含 CI hardening、依赖升级与 Docker CLI &lt;code&gt;29.6.1&lt;/code&gt; 等依赖更新&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Compose Spec&lt;/td&gt;
					&lt;td style="text-align: right"&gt;持续维护&lt;/td&gt;
					&lt;td&gt;定义 &lt;code&gt;pre_start&lt;/code&gt;：在服务容器启动前按顺序运行初始化容器，全部退出 &lt;code&gt;0&lt;/code&gt; 后才启动服务&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;因此，生产或团队模板中如果准备试用 &lt;code&gt;pre_start&lt;/code&gt;，建议直接升级到 &lt;code&gt;v5.3.1&lt;/code&gt; 或后续更高版本，而不是停留在刚发布的 &lt;code&gt;v5.3.0&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="pre_start-到底解决什么问题"&gt;&lt;a href="#pre_start-%e5%88%b0%e5%ba%95%e8%a7%a3%e5%86%b3%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;pre_start 到底解决什么问题
&lt;/h2&gt;&lt;p&gt;传统 Compose 项目里，初始化逻辑通常散落在几个位置：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;写进业务镜像的 &lt;code&gt;entrypoint.sh&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;docker compose up&lt;/code&gt; 前手动执行脚本；&lt;/li&gt;
&lt;li&gt;在 CI 中先跑 &lt;code&gt;docker compose run --rm migrate&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;depends_on.condition: service_healthy&lt;/code&gt; 等待依赖服务健康后再启动主服务。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些做法都能工作，但都有缺点。入口脚本会把“容器启动”与“环境初始化”强耦合，脚本失败时排查困难；CI 前置命令容易和开发者本地流程不一致；&lt;code&gt;depends_on&lt;/code&gt; 更偏向依赖顺序和健康检查，并不负责在主容器启动前执行一次性任务。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pre_start&lt;/code&gt; 的定位更像 Kubernetes 的 Init Container：它不是在主容器内部执行命令，而是创建一个短生命周期的临时容器。这个临时容器可以使用自己的镜像，也可以默认继承服务镜像；它与服务加入相同网络，并共享服务声明的卷挂载。只有这些步骤按声明顺序全部成功退出后，服务容器才会真正启动。&lt;/p&gt;
&lt;h2 id="一个最小可用示例"&gt;&lt;a href="#%e4%b8%80%e4%b8%aa%e6%9c%80%e5%b0%8f%e5%8f%af%e7%94%a8%e7%a4%ba%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;一个最小可用示例
&lt;/h2&gt;&lt;p&gt;假设 Web 服务启动前需要在共享卷里生成配置文件，可以写成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;web&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx:alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;web-conf:/etc/nginx/conf.d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pre_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;-&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; sh -c &amp;#39;cat &amp;gt; /etc/nginx/conf.d/default.conf &amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; server { listen 80; location / { return 200 &amp;#34;ok\\n&amp;#34;; } }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; EOF&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;web-conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的关键点是：&lt;code&gt;pre_start&lt;/code&gt; 容器先把配置写入 &lt;code&gt;web-conf&lt;/code&gt;，主服务随后挂载同一个卷并读取配置。如果初始化命令退出非零，&lt;code&gt;web&lt;/code&gt; 不会继续启动，这比“主容器启动后才发现配置缺失”更早暴露问题。&lt;/p&gt;
&lt;p&gt;如果初始化逻辑需要和业务镜像使用同一套工具链，也可以省略 &lt;code&gt;image&lt;/code&gt;，让 hook 默认使用父服务镜像：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./start-api&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;app-data:/app/data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pre_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./bin/prepare-runtime-cache --output /app/data/cache.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app-data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;官方 PR 中的端到端测试也覆盖了这类场景：当 hook 未指定镜像时，会回退到服务自身构建出的镜像。&lt;/p&gt;
&lt;h2 id="运行语义不要把它当成万能任务调度器"&gt;&lt;a href="#%e8%bf%90%e8%a1%8c%e8%af%ad%e4%b9%89%e4%b8%8d%e8%a6%81%e6%8a%8a%e5%ae%83%e5%bd%93%e6%88%90%e4%b8%87%e8%83%bd%e4%bb%bb%e5%8a%a1%e8%b0%83%e5%ba%a6%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;运行语义：不要把它当成万能任务调度器
&lt;/h2&gt;&lt;p&gt;从 Docker Compose PR 的说明看，当前实现有几个重要边界：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pre_start&lt;/code&gt; 当前按“每个服务一次”运行，而不是每个副本都运行；&lt;/li&gt;
&lt;li&gt;仅当没有副本已经在运行时触发，例如首次 &lt;code&gt;up&lt;/code&gt;、&lt;code&gt;--force-recreate&lt;/code&gt; 或 Compose 规范发生变化；&lt;/li&gt;
&lt;li&gt;普通 scale up 不会重新触发已有服务的 &lt;code&gt;pre_start&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;per_replica: true&lt;/code&gt; 在当前 Compose 实现中会被提前拒绝；&lt;/li&gt;
&lt;li&gt;hook 非零退出会阻止服务以及依赖它的服务继续启动；&lt;/li&gt;
&lt;li&gt;多个 &lt;code&gt;pre_start&lt;/code&gt; 步骤会按声明顺序执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这几个规则决定了它适合“服务级别的一次性准备”，不适合做每个副本都必须执行的注册、预热或本地临时目录初始化。如果你的任务必须随副本扩容逐个执行，现阶段仍应使用应用自身启动逻辑、编排平台能力，或等待 Compose 后续支持完整的 &lt;code&gt;per_replica&lt;/code&gt; 语义。&lt;/p&gt;
&lt;h2 id="适合落地的场景"&gt;&lt;a href="#%e9%80%82%e5%90%88%e8%90%bd%e5%9c%b0%e7%9a%84%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;适合落地的场景
&lt;/h2&gt;&lt;h3 id="1-本地开发数据库初始化"&gt;&lt;a href="#1-%e6%9c%ac%e5%9c%b0%e5%bc%80%e5%8f%91%e6%95%b0%e6%8d%ae%e5%ba%93%e5%88%9d%e5%a7%8b%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;1. 本地开发数据库初始化
&lt;/h3&gt;&lt;p&gt;在开发环境中，很多团队会把数据库、缓存、对象存储模拟器和应用服务放进同一个 Compose 项目。&lt;code&gt;pre_start&lt;/code&gt; 可以用于生成测试数据、写入默认配置，或者确认迁移脚本已经运行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;service_healthy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pre_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sh -c &amp;#39;echo &amp;#34;seed-ready&amp;#34; &amp;gt; /shared/state.txt&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;APP_ENV=local&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;app-shared:/shared&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;postgres:17&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;healthcheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CMD-SHELL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pg_isready -U postgres&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;5s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;3s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app-shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里仍然建议保留数据库健康检查。&lt;code&gt;pre_start&lt;/code&gt; 负责“服务启动前做什么”，&lt;code&gt;depends_on&lt;/code&gt; 和 &lt;code&gt;healthcheck&lt;/code&gt; 负责“依赖是否已经可用”，二者不是替代关系。&lt;/p&gt;
&lt;h3 id="2-集成测试中的夹具准备"&gt;&lt;a href="#2-%e9%9b%86%e6%88%90%e6%b5%8b%e8%af%95%e4%b8%ad%e7%9a%84%e5%a4%b9%e5%85%b7%e5%87%86%e5%a4%87" class="header-anchor"&gt;&lt;/a&gt;2. 集成测试中的夹具准备
&lt;/h3&gt;&lt;p&gt;CI 里经常需要启动一组服务进行端到端测试。把夹具准备放在 &lt;code&gt;pre_start&lt;/code&gt; 中，可以让开发者本地执行和 CI 执行保持一致：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up --build --abort-on-container-exit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果初始化失败，Compose 会在服务启动阶段就返回错误，日志也更集中。相比在 CI YAML 中堆多段 shell，Compose 文件成为更清晰的环境说明书。&lt;/p&gt;
&lt;h3 id="3-共享卷中的配置或证书生成"&gt;&lt;a href="#3-%e5%85%b1%e4%ba%ab%e5%8d%b7%e4%b8%ad%e7%9a%84%e9%85%8d%e7%bd%ae%e6%88%96%e8%af%81%e4%b9%a6%e7%94%9f%e6%88%90" class="header-anchor"&gt;&lt;/a&gt;3. 共享卷中的配置或证书生成
&lt;/h3&gt;&lt;p&gt;对一些内部工具、反向代理或 demo 环境，可以用 &lt;code&gt;pre_start&lt;/code&gt; 生成临时证书、模板配置、静态资源索引等，再让主服务读取。注意这只适合非敏感或临时材料；生产密钥仍应交给 Secret 管理系统，不要把长期敏感信息写进普通卷。&lt;/p&gt;
&lt;h2 id="升级与验证建议"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e4%b8%8e%e9%aa%8c%e8%af%81%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;升级与验证建议
&lt;/h2&gt;&lt;p&gt;升级前先确认当前版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;升级后重点检查三类流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首次 &lt;code&gt;docker compose up&lt;/code&gt;：确认 hook 顺序、日志和失败行为；&lt;/li&gt;
&lt;li&gt;再次 &lt;code&gt;docker compose up&lt;/code&gt;：确认已运行服务不会无意义重复执行初始化；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker compose up --force-recreate&lt;/code&gt; 或修改 &lt;code&gt;compose.yaml&lt;/code&gt;：确认变更后会按预期重新执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;还可以专门构造一个失败用例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sleep 300&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pre_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sh -c &amp;#39;exit 17&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;code&gt;docker compose up demo&lt;/code&gt; 后，预期主服务不会启动。这类“失败路径测试”比只验证成功路径更重要，因为 &lt;code&gt;pre_start&lt;/code&gt; 的核心价值之一正是把启动前置条件变成可失败、可观测的编排步骤。&lt;/p&gt;
&lt;h2 id="注意事项"&gt;&lt;a href="#%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9" class="header-anchor"&gt;&lt;/a&gt;注意事项
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;不要把耗时很长、需要重试队列或人工审批的任务放进 &lt;code&gt;pre_start&lt;/code&gt;，否则会阻塞服务启动；&lt;/li&gt;
&lt;li&gt;不要假设 scale up 会为新增副本重新执行初始化；&lt;/li&gt;
&lt;li&gt;不要把它当作数据库迁移的唯一保护，生产迁移仍应有幂等、锁和回滚策略；&lt;/li&gt;
&lt;li&gt;对共享卷写入要保持幂等，避免 &lt;code&gt;--force-recreate&lt;/code&gt; 后重复生成脏数据；&lt;/li&gt;
&lt;li&gt;团队模板中应固定最低 Compose 版本，否则旧环境会因为不认识字段而失败。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;Docker Compose &lt;code&gt;v5.3.x&lt;/code&gt; 的 &lt;code&gt;pre_start&lt;/code&gt; 支持，让 Compose 在本地编排和集成测试场景中补上了一个长期缺口：服务启动前的初始化任务终于可以以声明式方式进入 &lt;code&gt;compose.yaml&lt;/code&gt;。它不是 Kubernetes 的完整替代品，也不是通用任务调度系统，但非常适合处理服务级别、短生命周期、可幂等的一次性准备动作。&lt;/p&gt;
&lt;p&gt;对工程团队来说，推荐的落地路径是：先升级到 &lt;code&gt;v5.3.1&lt;/code&gt; 或更高版本，在非生产 Compose 项目中选择一个低风险初始化步骤试点；随后把成功路径、失败路径、重复 &lt;code&gt;up&lt;/code&gt;、&lt;code&gt;--force-recreate&lt;/code&gt; 和扩容行为都纳入验证。只要边界理解清楚，&lt;code&gt;pre_start&lt;/code&gt; 会让 Compose 项目更可读、更一致，也更接近现代容器编排的生命周期模型。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Docker Compose GitHub Release：v5.3.1（2026-07-07），https://github.com/docker/compose/releases/tag/v5.3.1&lt;/li&gt;
&lt;li&gt;Docker Compose GitHub Release：v5.3.0（2026-07-02），https://github.com/docker/compose/releases/tag/v5.3.0&lt;/li&gt;
&lt;li&gt;Docker Compose PR #13862：Pre start init containers，https://github.com/docker/compose/pull/13862&lt;/li&gt;
&lt;li&gt;Compose Specification：&lt;code&gt;pre_start&lt;/code&gt; lifecycle hook，https://github.com/compose-spec/compose-spec/blob/main/spec.md#pre_start&lt;/li&gt;
&lt;li&gt;Docker Docs：Compose release notes，https://docs.docker.com/compose/releases/release-notes/&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>