<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pre_start on 吾爱主机</title><link>https://blog.waihost.com/tags/pre_start/</link><description>Recent content in Pre_start on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Wed, 08 Jul 2026 09:08:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/pre_start/index.xml" rel="self" type="application/rss+xml"/><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>