<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>升级指南 on 吾爱主机</title><link>https://blog.waihost.com/tags/%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97/</link><description>Recent content in 升级指南 on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 11 Jul 2026 09:03:43 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes 1.36.2 补丁升级实战：DRA、CSI 与控制面稳定性检查清单</title><link>https://blog.waihost.com/posts/kubernetes-1-36-2-upgrade-checklist/</link><pubDate>Sat, 11 Jul 2026 09:03:43 +0800</pubDate><guid>https://blog.waihost.com/posts/kubernetes-1-36-2-upgrade-checklist/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/kubernetes-1-36-2-upgrade-checklist.svg" alt="Featured image of post Kubernetes 1.36.2 补丁升级实战：DRA、CSI 与控制面稳定性检查清单" /&gt;&lt;p&gt;Kubernetes 1.36.2 是 1.36 分支的补丁版本，官方 GitHub Release 发布于 2026-06-12。它不是一个“炫技型”的大版本，而是非常适合平台团队纳入日常升级窗口的稳定性修复版本：其中包含 Dynamic Resource Allocation（DRA）调度路径、CSI 卷重新发布、Endpoint Controller、Secret 环境变量处理以及 kubeadm 证书 dry-run 等多个容易在生产环境放大的边界问题。&lt;/p&gt;
&lt;p&gt;本文基于 Kubernetes 官方 Release、1.36 分支 CHANGELOG 与官方版本支持说明整理，面向正在运行 1.36.x、准备从 1.35/1.34 规划升级，或正在试点 GPU、DPU、RDMA 等设备资源编排能力的团队，给出一份可执行的升级与验证清单。&lt;/p&gt;
&lt;h2 id="为什么这个补丁版本值得关注"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e4%b8%aa%e8%a1%a5%e4%b8%81%e7%89%88%e6%9c%ac%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8" class="header-anchor"&gt;&lt;/a&gt;为什么这个补丁版本值得关注
&lt;/h2&gt;&lt;p&gt;Kubernetes 补丁版本通常不会引入破坏性 API 变化，但它会修复控制面和节点侧的真实缺陷。1.36.2 的重点可以概括为三类：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;调度正确性&lt;/strong&gt;：DRA 相关修复覆盖设备分区、共享计数器、多 allocatable 设备以及 ResourceClaim &lt;code&gt;allocationMode: All&lt;/code&gt; 等场景。对于使用新资源模型管理 GPU、加速卡或其他专用设备的集群，这类问题可能导致 Pod 错误分配、长时间 Pending，甚至设备冲突。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储与节点稳定性&lt;/strong&gt;：kubelet 在 CSI &lt;code&gt;requiresRepublish=true&lt;/code&gt; 周期性 &lt;code&gt;NodePublishVolume&lt;/code&gt; 失败时，曾可能删除挂载目录并让 Pod 继续看到陈旧卷内容。该类问题不一定马上表现为 CrashLoop，却可能带来数据一致性和排障难度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制面兼容性&lt;/strong&gt;：Endpoint Controller 处理早期未更新过 &lt;code&gt;ipFamilies&lt;/code&gt; 字段的 Service 时可能 panic；另外还修复了 Secret 二进制非 UTF-8 数据作为环境变量来源时的回归问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;与此同时，官方发布说明显示 Kubernetes 1.36.2 构建使用 Go 1.26.4。对自建发行包、二次编译或维护私有镜像仓库的团队来说，这意味着需要同步确认构建链、制品校验与 SBOM 记录。&lt;/p&gt;
&lt;h2 id="核心修复解读"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e4%bf%ae%e5%a4%8d%e8%a7%a3%e8%af%bb" class="header-anchor"&gt;&lt;/a&gt;核心修复解读
&lt;/h2&gt;&lt;h3 id="1-dra-调度从能调度到调度正确"&gt;&lt;a href="#1-dra-%e8%b0%83%e5%ba%a6%e4%bb%8e%e8%83%bd%e8%b0%83%e5%ba%a6%e5%88%b0%e8%b0%83%e5%ba%a6%e6%ad%a3%e7%a1%ae" class="header-anchor"&gt;&lt;/a&gt;1. DRA 调度：从“能调度”到“调度正确”
&lt;/h3&gt;&lt;p&gt;Dynamic Resource Allocation 是 Kubernetes 近几个版本持续增强的资源建模能力，用于表达比传统 CPU/Memory 更复杂的设备资源。1.36.2 中有多项 DRA 修复：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修复在 &lt;code&gt;SharedCounters&lt;/code&gt; 与多 allocatable 设备组合下，调度器可能把互斥设备分区分配给多个 Pod 的问题。&lt;/li&gt;
&lt;li&gt;修复同时使用多节点 claim 与 per-node claim 的 Pod 可能卡在 Pending 的问题。&lt;/li&gt;
&lt;li&gt;修复 ResourceClaim 使用 &lt;code&gt;allocationMode: All&lt;/code&gt; 且选择消耗 shared counters 的设备时，kube-scheduler 可能 panic 的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的集群还没有启用 DRA，这些修复可能暂时没有直接影响；但如果你在做 AI 训练、推理平台、边缘设备管理或高性能网络设备调度，建议把 1.36.2 视为 1.36 分支的最低生产基线之一。&lt;/p&gt;
&lt;h3 id="2-csi-republish不要忽略陈旧卷内容"&gt;&lt;a href="#2-csi-republish%e4%b8%8d%e8%a6%81%e5%bf%bd%e7%95%a5%e9%99%88%e6%97%a7%e5%8d%b7%e5%86%85%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;2. CSI republish：不要忽略“陈旧卷内容”
&lt;/h3&gt;&lt;p&gt;CHANGELOG 提到，kubelet 在处理 &lt;code&gt;CSIDriver.spec.requiresRepublish=true&lt;/code&gt; 的周期性 &lt;code&gt;NodePublishVolume&lt;/code&gt; 调用时，如果 republish 返回错误，曾可能删除 CSI mount directory，导致 Pod 继续持有陈旧卷内容，后续成功 republish 也无法自动修复。&lt;/p&gt;
&lt;p&gt;这类问题的危险之处在于：应用容器未必马上退出，监控也未必能通过简单的 Pod 状态发现异常。对依赖 CSI 动态刷新凭据、配置或挂载状态的系统来说，升级后应该重点观察：&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;kubectl get csidriver -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{range .items[*]}{.metadata.name}{&amp;#34;\t&amp;#34;}{.spec.requiresRepublish}{&amp;#34;\n&amp;#34;}{end}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get events -A --field-selector involvedObject.kind&lt;span class="o"&gt;=&lt;/span&gt;Pod &lt;span class="p"&gt;|&lt;/span&gt; grep -i &lt;span class="s1"&gt;&amp;#39;NodePublishVolume\|MountVolume&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果存在 &lt;code&gt;requiresRepublish=true&lt;/code&gt; 的驱动，建议在灰度节点上执行挂载异常注入或至少复盘历史 kubelet 日志，确认升级前是否出现过 republish 失败后应用读到旧数据的迹象。&lt;/p&gt;
&lt;h3 id="3-endpoint-controller-与旧-service-兼容性"&gt;&lt;a href="#3-endpoint-controller-%e4%b8%8e%e6%97%a7-service-%e5%85%bc%e5%ae%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;3. Endpoint Controller 与旧 Service 兼容性
&lt;/h3&gt;&lt;p&gt;1.36.2 修复了 Endpoint Controller 在处理 &lt;code&gt;ipFamilies&lt;/code&gt; 为空的 Service 时可能 panic 的问题。官方说明将其描述为 pre-dual-stack services that were never spec-updated，也就是双栈能力引入前创建、之后长期未触碰规格字段的老 Service。&lt;/p&gt;
&lt;p&gt;这对老集群很重要：很多生产集群中存在“创建多年但业务仍在使用”的 Service。升级前可以先做一次巡检：&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;kubectl get svc -A -o json &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; .items[] | select((.spec.ipFamilies // []) | length == 0) |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; [.metadata.namespace, .metadata.name, .spec.clusterIP] | @tsv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果发现命中项，先在测试环境复现或通过无害字段更新触发默认值补齐，再安排控制面升级，会比升级窗口里临时定位 controller panic 更稳妥。&lt;/p&gt;
&lt;h3 id="4-secret-二进制环境变量回归"&gt;&lt;a href="#4-secret-%e4%ba%8c%e8%bf%9b%e5%88%b6%e7%8e%af%e5%a2%83%e5%8f%98%e9%87%8f%e5%9b%9e%e5%bd%92" class="header-anchor"&gt;&lt;/a&gt;4. Secret 二进制环境变量回归
&lt;/h3&gt;&lt;p&gt;官方 CHANGELOG 还提到修复了 1.34+ 中一个回归：容器环境变量值来自 Secret API 对象且包含 binary non-UTF8 data 时的处理问题。虽然“二进制 Secret 直接进环境变量”不是推荐实践，但历史系统、第三方 Chart 或迁移遗留配置中并不少见。&lt;/p&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;kubectl get deploy,statefulset,daemonset -A -o yaml &lt;span class="p"&gt;|&lt;/span&gt; grep -n &lt;span class="s2"&gt;&amp;#34;secretKeyRef&amp;#34;&lt;/span&gt; -C &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对命中的工作负载，优先确认 Secret 内容是否应改为文件挂载、是否存在非 UTF-8 值，以及应用侧是否真的需要以环境变量方式读取。升级补丁可以修复回归，但配置治理仍应同步推进。&lt;/p&gt;
&lt;h2 id="升级前检查清单"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d%e6%a3%80%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;升级前检查清单
&lt;/h2&gt;&lt;h3 id="确认版本与支持窗口"&gt;&lt;a href="#%e7%a1%ae%e8%ae%a4%e7%89%88%e6%9c%ac%e4%b8%8e%e6%94%af%e6%8c%81%e7%aa%97%e5%8f%a3" class="header-anchor"&gt;&lt;/a&gt;确认版本与支持窗口
&lt;/h3&gt;&lt;p&gt;Kubernetes 官方 Releases 页面说明，项目维护最近三个 minor release 分支；当前页面描述为 1.36、1.35、1.34。Kubernetes 1.19 及之后版本大约提供 1 年补丁支持。因此，1.36.2 对 1.36 用户是常规补丁升级，对更老分支则应结合版本偏斜策略规划跨 minor 升级。&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;kubectl version --short
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes -o wide
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get --raw /version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果控制面、kubelet、kubectl 或外部组件版本跨度较大，先阅读官方 version skew policy，再决定是否直接升级到 1.36.x。&lt;/p&gt;
&lt;h3 id="备份-etcd-与关键配置"&gt;&lt;a href="#%e5%a4%87%e4%bb%bd-etcd-%e4%b8%8e%e5%85%b3%e9%94%ae%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;备份 etcd 与关键配置
&lt;/h3&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;&lt;span class="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl snapshot save /backup/etcd-&lt;span class="k"&gt;$(&lt;/span&gt;date +%F-%H%M&lt;span class="k"&gt;)&lt;/span&gt;.db &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --endpoints&lt;span class="o"&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cacert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --key&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key
&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;kubectl get all,cm,secret,ingress,pvc -A -o yaml &amp;gt; /backup/k8s-resources-&lt;span class="k"&gt;$(&lt;/span&gt;date +%F&lt;span class="k"&gt;)&lt;/span&gt;.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;托管 Kubernetes 也应在云厂商控制台确认升级回滚策略、节点池灰度能力与控制面维护窗口。&lt;/p&gt;
&lt;h3 id="先升级测试集群或单个节点池"&gt;&lt;a href="#%e5%85%88%e5%8d%87%e7%ba%a7%e6%b5%8b%e8%af%95%e9%9b%86%e7%be%a4%e6%88%96%e5%8d%95%e4%b8%aa%e8%8a%82%e7%82%b9%e6%b1%a0" class="header-anchor"&gt;&lt;/a&gt;先升级测试集群或单个节点池
&lt;/h3&gt;&lt;p&gt;推荐顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在测试集群升级控制面到 1.36.2。&lt;/li&gt;
&lt;li&gt;升级一组低风险节点池。&lt;/li&gt;
&lt;li&gt;验证 DRA、CSI、Service、Job、Secret 相关用例。&lt;/li&gt;
&lt;li&gt;扩大到生产控制面与生产节点池。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对 kubeadm 集群，还应关注 1.36.2 修复的 &lt;code&gt;kubeadm init phase certs --dry-run&lt;/code&gt; 复制现有 CA 文件问题。如果你的自动化流程依赖 dry-run 生成或校验证书，应在升级后重新跑一遍流水线。&lt;/p&gt;
&lt;h2 id="升级后验证清单"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%90%8e%e9%aa%8c%e8%af%81%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;升级后验证清单
&lt;/h2&gt;&lt;p&gt;升级完成后，不要只看 &lt;code&gt;kubectl get nodes&lt;/code&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;kubectl get componentstatuses 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get --raw&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/readyz?verbose&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -A --field-selector&lt;span class="o"&gt;=&lt;/span&gt;status.phase!&lt;span class="o"&gt;=&lt;/span&gt;Running,status.phase!&lt;span class="o"&gt;=&lt;/span&gt;Succeeded
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get events -A --sort-by&lt;span class="o"&gt;=&lt;/span&gt;.lastTimestamp &lt;span class="p"&gt;|&lt;/span&gt; tail -n &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n kube-system logs -l &lt;span class="nv"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-scheduler --tail&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;200&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;ul&gt;
&lt;li&gt;kube-scheduler 是否仍出现 DRA ResourceClaim 相关 panic 或调度失败。&lt;/li&gt;
&lt;li&gt;CSI 插件与 kubelet 日志中是否还有 &lt;code&gt;NodePublishVolume&lt;/code&gt; 反复失败。&lt;/li&gt;
&lt;li&gt;老 Service 是否触发 Endpoint/EndpointSlice 控制器异常。&lt;/li&gt;
&lt;li&gt;使用 Secret 环境变量的 Pod 是否能正常启动。&lt;/li&gt;
&lt;li&gt;suspended Job 修改 &lt;code&gt;nodeSelector&lt;/code&gt;、tolerations、node affinity 等 scheduling directives 是否符合预期。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实践建议"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;实践建议
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;未使用 DRA 的普通集群&lt;/strong&gt;：可以按常规补丁窗口升级，但仍需完成 etcd 备份、控制面健康检查与节点池灰度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 DRA 或设备插件的集群&lt;/strong&gt;：建议优先升级测试环境，并围绕 ResourceClaim、共享计数器、多 allocatable 设备构造回归用例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSI 驱动复杂的集群&lt;/strong&gt;：重点确认 &lt;code&gt;requiresRepublish=true&lt;/code&gt; 驱动，升级后观察挂载内容刷新是否正常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;历史包袱较重的老集群&lt;/strong&gt;：先巡检空 &lt;code&gt;ipFamilies&lt;/code&gt; Service、Secret 环境变量与长期未更新的 workload spec，避免把老问题带进升级窗口。&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;Kubernetes 1.36.2 的价值不在于新功能数量，而在于把 1.36 分支中几个高风险边界问题向前修掉：DRA 调度正确性、CSI republish 行为、Endpoint Controller 兼容性、Secret 二进制数据回归以及 kubeadm dry-run 证书流程。对于平台团队来说，最稳妥的做法是把它纳入一次小而完整的补丁升级：先备份，再灰度，最后用针对性清单验证关键路径。&lt;/p&gt;
&lt;p&gt;如果你的集群已经在 1.36.x，建议优先评估 1.36.2；如果仍在 1.35/1.34，则应结合官方版本支持与版本偏斜策略制定 minor 升级计划，而不是只追补丁号。&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;Kubernetes GitHub Release：&lt;a class="link" href="https://github.com/kubernetes/kubernetes/releases/tag/v1.36.2" target="_blank" rel="noopener"
 &gt;https://github.com/kubernetes/kubernetes/releases/tag/v1.36.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kubernetes 1.36 CHANGELOG：&lt;a class="link" href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md" target="_blank" rel="noopener"
 &gt;https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.36.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kubernetes 官方 Releases 与支持分支说明：&lt;a class="link" href="https://kubernetes.io/releases/" target="_blank" rel="noopener"
 &gt;https://kubernetes.io/releases/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Node.js 26.5.0 Current 发布：Web Streams、TLS 可观测性与权限模型修复实践指南</title><link>https://blog.waihost.com/posts/nodejs-26-5-current-upgrade-guide/</link><pubDate>Fri, 10 Jul 2026 09:02:00 +0800</pubDate><guid>https://blog.waihost.com/posts/nodejs-26-5-current-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/nodejs-26-5-current-upgrade-guide.svg" alt="Featured image of post Node.js 26.5.0 Current 发布：Web Streams、TLS 可观测性与权限模型修复实践指南" /&gt;&lt;p&gt;Node.js 26.5.0 已于 2026-07-08 发布，属于 Current 版本线的一次常规功能与维护更新。它不是安全发布，但包含了几类对后端服务、网关、边缘函数和内部平台都值得关注的变化：&lt;code&gt;Blob&lt;/code&gt; 新增面向流式读取的能力，TLS 协商信息更容易被观测，权限模型在 &lt;code&gt;NODE_OPTIONS&lt;/code&gt; 传播场景下修复了行为一致性问题，同时依赖组件继续更新到较新的版本。&lt;/p&gt;
&lt;p&gt;如果你的生产环境仍以 LTS 为主，不需要因为 Current 版本线的每次发布立即升级线上服务；但如果团队正在验证 Node.js 26、维护基础镜像、做运行时平台适配，26.5.0 很适合作为一次“小步升级 + 回归验证”的窗口。&lt;/p&gt;
&lt;h2 id="这次发布的定位"&gt;&lt;a href="#%e8%bf%99%e6%ac%a1%e5%8f%91%e5%b8%83%e7%9a%84%e5%ae%9a%e4%bd%8d" class="header-anchor"&gt;&lt;/a&gt;这次发布的定位
&lt;/h2&gt;&lt;p&gt;从官方 release index 可以看到，Node.js 26.5.0 的发布日期为 2026-07-08，随附组件版本包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npm：&lt;code&gt;11.17.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;V8：&lt;code&gt;14.6.202.34&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;libuv：&lt;code&gt;1.52.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;zlib：&lt;code&gt;1.3.2.1-motley&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;OpenSSL：&lt;code&gt;3.5.7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ABI modules：&lt;code&gt;147&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;security: false&lt;/code&gt;，即这不是一次安全专版发布&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着它更适合被理解为 Current 线的功能增强和维护版本，而不是“必须立刻全量升级”的安全补丁。不过，Current 线通常会提前暴露未来 LTS 可能影响应用的运行时变化，平台团队应该尽早把它纳入 CI 验证。&lt;/p&gt;
&lt;h2 id="关键变化一blobtextstream-让文本读取更贴近流式处理"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%8f%98%e5%8c%96%e4%b8%80blobtextstream-%e8%ae%a9%e6%96%87%e6%9c%ac%e8%af%bb%e5%8f%96%e6%9b%b4%e8%b4%b4%e8%bf%91%e6%b5%81%e5%bc%8f%e5%a4%84%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;关键变化一：&lt;code&gt;Blob.textStream()&lt;/code&gt; 让文本读取更贴近流式处理
&lt;/h2&gt;&lt;p&gt;官方发布说明将 &lt;code&gt;blob.textStream()&lt;/code&gt; 列为 notable change。过去我们处理 &lt;code&gt;Blob&lt;/code&gt; 文本内容时，常见方式是一次性调用 &lt;code&gt;blob.text()&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种写法简单，但它会把文本内容整体读入内存。对于普通接口请求体、配置片段或小文件来说问题不大；但在日志分析、对象存储网关、上传文件预处理等场景中，一次性读取会放大内存峰值，也不利于和 Web Streams 管线组合。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Blob.textStream()&lt;/code&gt; 的价值在于：它把“文本内容”暴露为可流式消费的接口，应用可以更自然地接入 &lt;code&gt;ReadableStream&lt;/code&gt;、&lt;code&gt;TextDecoderStream&lt;/code&gt;、分块处理、背压控制等模式。实践中建议关注三类用法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&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;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textStream&lt;/span&gt;&lt;span class="p"&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="k"&gt;for&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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; &lt;span class="nx"&gt;processChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;升级验证时不要只跑“能否启动”的 smoke test，应补充大文件和高并发场景，观察 RSS、GC 暂停、吞吐量是否更稳定。&lt;/p&gt;
&lt;h2 id="关键变化二tls-协商组信息更容易排查"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%8f%98%e5%8c%96%e4%ba%8ctls-%e5%8d%8f%e5%95%86%e7%bb%84%e4%bf%a1%e6%81%af%e6%9b%b4%e5%ae%b9%e6%98%93%e6%8e%92%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;关键变化二：TLS 协商组信息更容易排查
&lt;/h2&gt;&lt;p&gt;26.5.0 还提到 “report negotiated TLS groups”。这类变化不一定会改变业务逻辑，但对运维和安全排障很有价值。&lt;/p&gt;
&lt;p&gt;在实际生产中，TLS 问题经常表现为“某些客户端能连、某些客户端失败”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端运行时、OpenSSL、系统证书库版本不同；&lt;/li&gt;
&lt;li&gt;负载均衡、反向代理、服务端 Node.js 的 TLS 配置不一致；&lt;/li&gt;
&lt;li&gt;安全基线升级后，曲线/密钥交换组被禁用或协商失败；&lt;/li&gt;
&lt;li&gt;FIPS、国密或企业代理环境引入额外限制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果运行时能报告协商到的 TLS groups，平台团队就能把“握手失败/降级/兼容性差”的问题从黑盒日志变成可观测事实。建议在升级验证阶段做一次完整 TLS 回归：&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;node -p &lt;span class="s2"&gt;&amp;#34;process.versions&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect example.com:443 -tls1_3 &amp;lt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时检查服务端访问日志、网关日志和 APM 标签，确认 TLS 版本、加密套件、协商组等信息是否能被统一记录。对于金融、企业内网、老旧 Android/WebView 客户端，这一步尤其重要。&lt;/p&gt;
&lt;h2 id="关键变化三权限模型与-node_options-的一致性修复"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%8f%98%e5%8c%96%e4%b8%89%e6%9d%83%e9%99%90%e6%a8%a1%e5%9e%8b%e4%b8%8e-node_options-%e7%9a%84%e4%b8%80%e8%87%b4%e6%80%a7%e4%bf%ae%e5%a4%8d" class="header-anchor"&gt;&lt;/a&gt;关键变化三：权限模型与 &lt;code&gt;NODE_OPTIONS&lt;/code&gt; 的一致性修复
&lt;/h2&gt;&lt;p&gt;Node.js 的 Permission Model 仍处于演进阶段，但已经是很多团队评估“最小权限运行 JavaScript 服务”的重要方向。26.5.0 的提交列表包含 “fix permission model propagation via NODE_OPTIONS”。&lt;/p&gt;
&lt;p&gt;这类修复值得平台团队认真看待，因为 &lt;code&gt;NODE_OPTIONS&lt;/code&gt; 在容器镜像、PaaS 平台、CI/CD、serverless 运行时里非常常见。例如：&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;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--experimental-permission --allow-fs-read=/app/config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node server.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果权限相关选项在子进程、worker、工具链包装脚本中传播不一致，可能会出现两种风险：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;开发/测试环境以为权限限制已生效，但生产包装脚本绕过了限制；&lt;/li&gt;
&lt;li&gt;本该允许的读取、网络或子进程行为被误拦截，导致线上难以复现的故障。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，升级到 26.5.0 后建议新增一组“权限模型回归用例”，覆盖：主进程、&lt;code&gt;child_process&lt;/code&gt;、worker、测试框架、构建脚本以及容器入口脚本。不要只验证应用入口文件。&lt;/p&gt;
&lt;h2 id="关键变化四依赖组件更新带来的隐性影响"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%8f%98%e5%8c%96%e5%9b%9b%e4%be%9d%e8%b5%96%e7%bb%84%e4%bb%b6%e6%9b%b4%e6%96%b0%e5%b8%a6%e6%9d%a5%e7%9a%84%e9%9a%90%e6%80%a7%e5%bd%b1%e5%93%8d" class="header-anchor"&gt;&lt;/a&gt;关键变化四：依赖组件更新带来的隐性影响
&lt;/h2&gt;&lt;p&gt;本次版本还包含若干依赖更新，例如 release notes 中列出的 Undici、nghttp3、SQLite 等更新；release index 也显示 OpenSSL、libuv、zlib 等底层组件处在较新的版本组合上。对大多数业务而言，这些更新不会直接要求改代码，但它们可能影响：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 客户端行为：连接复用、代理、重定向、超时和 header 处理；&lt;/li&gt;
&lt;li&gt;HTTP/2 / HTTP/3 相关边界行为；&lt;/li&gt;
&lt;li&gt;内置 SQLite 场景的兼容性和性能；&lt;/li&gt;
&lt;li&gt;原生插件编译、ABI 兼容和镜像体积；&lt;/li&gt;
&lt;li&gt;TLS/加密能力与企业安全基线的匹配。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果项目依赖 &lt;code&gt;node-gyp&lt;/code&gt;、原生 addon、Electron、Playwright、Puppeteer 或自定义 OpenSSL 行为，建议在升级前后分别输出环境信息：&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;node -p &lt;span class="s2"&gt;&amp;#34;process.version&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node -p &lt;span class="s2"&gt;&amp;#34;process.versions&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ls --depth&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;并在 CI 中保留构建日志，方便定位 ABI 或编译链变化。&lt;/p&gt;
&lt;h2 id="推荐升级流程"&gt;&lt;a href="#%e6%8e%a8%e8%8d%90%e5%8d%87%e7%ba%a7%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;推荐升级流程
&lt;/h2&gt;&lt;p&gt;对于生产团队，建议把 Node.js 26.5.0 当作 Current 线验证版本，而不是直接替换 LTS：&lt;/p&gt;
&lt;h3 id="1-先确认版本线策略"&gt;&lt;a href="#1-%e5%85%88%e7%a1%ae%e8%ae%a4%e7%89%88%e6%9c%ac%e7%ba%bf%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;1. 先确认版本线策略
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;线上核心服务：优先使用当前 LTS；&lt;/li&gt;
&lt;li&gt;内部平台、SDK、CLI、基础镜像：可以提前验证 Current；&lt;/li&gt;
&lt;li&gt;新特性探索：用隔离环境验证，不直接影响生产流量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-建立最小回归清单"&gt;&lt;a href="#2-%e5%bb%ba%e7%ab%8b%e6%9c%80%e5%b0%8f%e5%9b%9e%e5%bd%92%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;2. 建立最小回归清单
&lt;/h3&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;node -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node -p &lt;span class="s2"&gt;&amp;#34;process.versions&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ci
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm run build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Web 服务还应补充接口 smoke test、TLS 连接测试、代理场景测试和大文件上传/下载测试。&lt;/p&gt;
&lt;h3 id="3-对-web-streams-和-blob-场景做专项验证"&gt;&lt;a href="#3-%e5%af%b9-web-streams-%e5%92%8c-blob-%e5%9c%ba%e6%99%af%e5%81%9a%e4%b8%93%e9%a1%b9%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;3. 对 Web Streams 和 Blob 场景做专项验证
&lt;/h3&gt;&lt;p&gt;如果项目处理上传文件、对象存储、日志、CSV/JSONL 或边缘函数请求体，可以新增流式处理测试，避免把大对象一次性读入内存。&lt;/p&gt;
&lt;h3 id="4-对权限模型做入口链路测试"&gt;&lt;a href="#4-%e5%af%b9%e6%9d%83%e9%99%90%e6%a8%a1%e5%9e%8b%e5%81%9a%e5%85%a5%e5%8f%a3%e9%93%be%e8%b7%af%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;4. 对权限模型做“入口链路”测试
&lt;/h3&gt;&lt;p&gt;如果你正在使用或评估 &lt;code&gt;--experimental-permission&lt;/code&gt;，不要只测 &lt;code&gt;node app.js&lt;/code&gt;。要覆盖 npm script、PM2/systemd、Docker ENTRYPOINT、测试框架和子进程。&lt;/p&gt;
&lt;h3 id="5-保留快速回滚方案"&gt;&lt;a href="#5-%e4%bf%9d%e7%95%99%e5%bf%ab%e9%80%9f%e5%9b%9e%e6%bb%9a%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;5. 保留快速回滚方案
&lt;/h3&gt;&lt;p&gt;升级运行时比升级普通依赖更底层。建议镜像 tag 明确区分：&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;node:26.4.0-bookworm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node:26.5.0-bookworm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&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;ol&gt;
&lt;li&gt;26.5.0 是 Current，不等同于 LTS；生产环境是否采用要看团队的运行时策略。&lt;/li&gt;
&lt;li&gt;这不是安全发布，不应把它包装成“紧急安全升级”。&lt;/li&gt;
&lt;li&gt;权限模型仍要关注实验性接口和未来变更，不建议仅凭一次修复就假设所有沙箱场景都已稳定。&lt;/li&gt;
&lt;li&gt;原生 addon 和构建链要在目标架构上验证，尤其是 Alpine、ARM64、CI runner 与生产镜像不一致时。&lt;/li&gt;
&lt;li&gt;如果线上仍在 Node.js 22/24 LTS，优先处理对应 LTS 安全更新，再安排 Node.js 26 的兼容性验证。&lt;/li&gt;
&lt;/ol&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;Node.js 26.5.0 的重点不在“惊天动地的新功能”，而在几个对工程实践很实用的方向：更流式的 &lt;code&gt;Blob&lt;/code&gt; 文本读取、更好的 TLS 可观测性、权限模型传播修复，以及底层依赖维护更新。对平台团队来说，最合理的姿势是：把它纳入 CI 和预发环境，围绕 Web Streams、TLS、权限模型、原生 addon 建立回归清单；对业务团队来说，则应继续遵循 LTS 优先策略，等验证充分后再决定是否升级运行时基线。&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;官方发布说明：&lt;a class="link" href="https://nodejs.org/en/blog/release/v26.5.0" target="_blank" rel="noopener"
 &gt;https://nodejs.org/en/blog/release/v26.5.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;官方 release index：&lt;a class="link" href="https://nodejs.org/download/release/index.json" target="_blank" rel="noopener"
 &gt;https://nodejs.org/download/release/index.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Release：&lt;a class="link" href="https://github.com/nodejs/node/releases/tag/v26.5.0" target="_blank" rel="noopener"
 &gt;https://github.com/nodejs/node/releases/tag/v26.5.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Node.js Permission Model 文档：&lt;a class="link" href="https://nodejs.org/api/permissions.html" target="_blank" rel="noopener"
 &gt;https://nodejs.org/api/permissions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Node.js Web Streams 文档：&lt;a class="link" href="https://nodejs.org/api/webstreams.html" target="_blank" rel="noopener"
 &gt;https://nodejs.org/api/webstreams.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>etcd 3.7.0 正式发布：Kubernetes 控制面的升级、备份与兼容性检查清单</title><link>https://blog.waihost.com/posts/etcd-3-7-upgrade-guide/</link><pubDate>Thu, 09 Jul 2026 09:06:19 +0800</pubDate><guid>https://blog.waihost.com/posts/etcd-3-7-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/etcd-3-7-upgrade-guide.svg" alt="Featured image of post etcd 3.7.0 正式发布：Kubernetes 控制面的升级、备份与兼容性检查清单" /&gt;&lt;p&gt;etcd 是 Kubernetes 控制面的关键依赖：API Server 的对象状态、Leader 选举、租约与 watch 通知，最终都要落到这个分布式键值存储上。2026 年 7 月 8 日，etcd 项目发布了 v3.7.0。它不是一个只改版本号的小更新：官方发布说明明确要求升级前阅读 v3.7 升级指南，CHANGELOG 中同时包含安全修复、依赖升级、v2 相关能力移除、clientv3 行为变化以及运维工具改动。&lt;/p&gt;
&lt;p&gt;这篇文章基于 etcd 官方 Release、CHANGELOG、v3.7 升级文档，以及 Kubernetes 官方 etcd 运维文档整理成一份实践清单。它不是复制某一篇公告，而是面向真实生产环境回答三个问题：是否应该升级、升级前要检查什么、如果你运行的是 Kubernetes 集群该如何降低风险。&lt;/p&gt;
&lt;h2 id="这次发布最值得关注的变化"&gt;&lt;a href="#%e8%bf%99%e6%ac%a1%e5%8f%91%e5%b8%83%e6%9c%80%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8%e7%9a%84%e5%8f%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;这次发布最值得关注的变化
&lt;/h2&gt;&lt;h3 id="1-安全修复与依赖升级"&gt;&lt;a href="#1-%e5%ae%89%e5%85%a8%e4%bf%ae%e5%a4%8d%e4%b8%8e%e4%be%9d%e8%b5%96%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;1. 安全修复与依赖升级
&lt;/h3&gt;&lt;p&gt;v3.7.0 的正式版 CHANGELOG 提到两个服务端修复：当配置 &lt;code&gt;--listen-client-http-urls&lt;/code&gt; 时，gRPC listener 上的 CRL enforcement bypass 问题得到修复；同时修复了带 &lt;code&gt;bearer&lt;/code&gt; 前缀 token 的 websocket 认证问题。依赖层面，官方二进制使用 Go 1.26.5 编译，并将 &lt;code&gt;golang.org/x/crypto&lt;/code&gt; 升级到 v0.52.0，以处理多项 CVE。&lt;/p&gt;
&lt;p&gt;对运维团队来说，这意味着 v3.7.0 不应只被看成“新功能版本”。如果你的 etcd 暴露在严格证书校验、客户端证书吊销列表、或经过网关/WebSocket 访问的场景中，安全修复本身就值得排期评估。&lt;/p&gt;
&lt;h3 id="2-v2-时代彻底退出"&gt;&lt;a href="#2-v2-%e6%97%b6%e4%bb%a3%e5%bd%bb%e5%ba%95%e9%80%80%e5%87%ba" class="header-anchor"&gt;&lt;/a&gt;2. v2 时代彻底退出
&lt;/h3&gt;&lt;p&gt;v3.7 升级文档说明，v2 store 已在 v3.7 中完全移除：&lt;code&gt;--enable-v2&lt;/code&gt;、&lt;code&gt;--experimental-enable-v2v3&lt;/code&gt;、v2 discovery service、&lt;code&gt;client/v2&lt;/code&gt; 包以及 v2 snapshot 文件加载都不再存在。CHANGELOG 也把移除 v2 discovery、client/v2、v2 request/apply 等列为 v3.7.0-rc.0 的 breaking changes。&lt;/p&gt;
&lt;p&gt;如果你的集群已经稳定运行在 3.6，并且历史上没有保留 v2 数据，这通常不是阻塞项；但如果某些遗留脚本仍依赖 v2 API，或者你还保留了旧版本快照恢复流程，就必须在升级前完成迁移与演练。&lt;/p&gt;
&lt;h3 id="3-clientv3-创建连接不再阻塞"&gt;&lt;a href="#3-clientv3-%e5%88%9b%e5%bb%ba%e8%bf%9e%e6%8e%a5%e4%b8%8d%e5%86%8d%e9%98%bb%e5%a1%9e" class="header-anchor"&gt;&lt;/a&gt;3. clientv3 创建连接不再阻塞
&lt;/h3&gt;&lt;p&gt;CHANGELOG 写明：&lt;code&gt;clientv3&lt;/code&gt; 创建 etcd client 变为 non-blocking，etcd 不再遵循已废弃的 &lt;code&gt;grpc.WithBlock&lt;/code&gt; dial option。过去一些程序会假设 &lt;code&gt;clientv3.New(...)&lt;/code&gt; 返回成功就代表连接已建立；升级依赖后，这类程序需要改为显式执行健康检查、超时控制或首次请求重试。&lt;/p&gt;
&lt;p&gt;一个更稳妥的模式是：创建 client 后立即用带超时的上下文调用 &lt;code&gt;Status&lt;/code&gt; 或 &lt;code&gt;EndpointHealth&lt;/code&gt;，把“创建对象成功”和“后端可达”拆开处理。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&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="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&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="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://127.0.0.1:2379&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="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;etcd endpoint not ready: %w&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&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="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;h3 id="4-命令行工具与可观测性变化"&gt;&lt;a href="#4-%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%b7%a5%e5%85%b7%e4%b8%8e%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7%e5%8f%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;4. 命令行工具与可观测性变化
&lt;/h3&gt;&lt;p&gt;v3.7 系列整理了 &lt;code&gt;etcdctl&lt;/code&gt; 命令，使帮助输出更简洁；&lt;code&gt;etcdutl&lt;/code&gt; 为所有命令增加 timeout flag，用于等待数据库文件锁时避免无限等待。监控方面，CHANGELOG 提到新增 &lt;code&gt;etcd_server_request_duration_seconds&lt;/code&gt;，以及多项 watch send loop 相关指标。对于依赖 Prometheus 告警的集群，升级后应同步检查 dashboard 和 recording rules，避免因为指标新增、命名差异或告警阈值不适配导致误报或漏报。&lt;/p&gt;
&lt;h2 id="生产升级前的硬性检查"&gt;&lt;a href="#%e7%94%9f%e4%ba%a7%e5%8d%87%e7%ba%a7%e5%89%8d%e7%9a%84%e7%a1%ac%e6%80%a7%e6%a3%80%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;生产升级前的硬性检查
&lt;/h2&gt;&lt;p&gt;官方 v3.7 升级文档给出了两个基础前提：运行中的集群必须已经是 v3.6.11 或更高版本；etcd 只支持一次升级一个 minor 版本。如果你还在 3.5 或更早版本，应先升级到 3.6，再从 3.6 升级到 3.7。&lt;/p&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;&lt;span class="c1"&gt;# 1. 检查所有成员版本与健康状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --endpoints&lt;span class="o"&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cacert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --key&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; endpoint status --cluster -w table
&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="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --endpoints&lt;span class="o"&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cacert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --key&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; endpoint health --cluster
&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;&lt;span class="c1"&gt;# 2. 创建快照，保留到升级窗口结束后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --endpoints&lt;span class="o"&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cacert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --cert&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --key&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; snapshot save /backup/etcd-&lt;span class="k"&gt;$(&lt;/span&gt;date +%F-%H%M&lt;span class="k"&gt;)&lt;/span&gt;.db
&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;# 3. 校验快照状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;etcdutl snapshot status /backup/etcd-YYYY-MM-DD-HHMM.db -w table
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Kubernetes 官方文档也强调了 &lt;code&gt;etcdctl&lt;/code&gt; 与 &lt;code&gt;etcdutl&lt;/code&gt; 的分工：&lt;code&gt;etcdctl&lt;/code&gt; 主要用于通过网络管理集群、键值和健康状态；&lt;code&gt;etcdutl&lt;/code&gt; 更偏向直接操作数据文件，例如快照恢复、碎片整理、迁移与一致性验证。不要把两者混用成一个“万能命令”。&lt;/p&gt;
&lt;h2 id="推荐的滚动升级节奏"&gt;&lt;a href="#%e6%8e%a8%e8%8d%90%e7%9a%84%e6%bb%9a%e5%8a%a8%e5%8d%87%e7%ba%a7%e8%8a%82%e5%a5%8f" class="header-anchor"&gt;&lt;/a&gt;推荐的滚动升级节奏
&lt;/h2&gt;&lt;p&gt;v3.7 升级文档说明，常规情况下从 v3.6 到 v3.7 可以通过零停机滚动升级完成：一次停止并替换一个成员，所有成员升级完成后，集群才被视为真正升级到 v3.7，并启用新版本能力。升级期间，混合版本集群会按最低共同版本协议运行。&lt;/p&gt;
&lt;p&gt;一个三节点集群可以按下面节奏执行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在预发环境复刻生产拓扑，确认 kube-apiserver、控制器、调度器和自研组件能正常读写。&lt;/li&gt;
&lt;li&gt;对生产集群做快照，并确认快照可读、可复制到独立存储。&lt;/li&gt;
&lt;li&gt;逐个成员停止服务、替换二进制或镜像、启动服务。&lt;/li&gt;
&lt;li&gt;每升级一个成员后检查 &lt;code&gt;endpoint health --cluster&lt;/code&gt;、leader 是否稳定、API Server 错误率是否异常。&lt;/li&gt;
&lt;li&gt;所有成员升级完成后再观察至少一个业务高峰周期。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果是 kubeadm 管理的控制面，不建议绕过发行版或 kubeadm 文档直接替换宿主机上的 etcd；应先确认当前 Kubernetes 版本支持的 etcd 版本范围，并在维护窗口执行。&lt;/p&gt;
&lt;h2 id="日常维护也要一起复盘"&gt;&lt;a href="#%e6%97%a5%e5%b8%b8%e7%bb%b4%e6%8a%a4%e4%b9%9f%e8%a6%81%e4%b8%80%e8%b5%b7%e5%a4%8d%e7%9b%98" class="header-anchor"&gt;&lt;/a&gt;日常维护也要一起复盘
&lt;/h2&gt;&lt;p&gt;升级窗口是复盘 etcd 维护策略的好时机。官方维护文档指出，etcd 需要周期性维护以保持可靠性；如果 keyspace 历史没有及时压缩，后端数据库没有碎片整理，空间配额触发后集群会进入只接受读取和删除的受限维护模式。&lt;/p&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;&lt;span class="c1"&gt;# 自动压缩：保留最近 1 小时历史（示例，需按业务 watch/回放需求调整）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;etcd --auto-compaction-mode&lt;span class="o"&gt;=&lt;/span&gt;periodic --auto-compaction-retention&lt;span class="o"&gt;=&lt;/span&gt;1h
&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;&lt;span class="c1"&gt;# 手动压缩到某个 revision 后，再对成员做 defrag&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl compact &amp;lt;revision&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ETCDCTL_API&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; etcdctl defrag --cluster
&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;&lt;span class="c1"&gt;# 关注 DB Size、leader 切换、请求延迟、watch 延迟等指标&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 结合新增的 etcd_server_request_duration_seconds 更新 Prometheus 告警&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="#%e5%8d%87%e7%ba%a7%e9%a3%8e%e9%99%a9%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;升级风险清单
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;遗留 v2 依赖&lt;/strong&gt;：检查脚本、SDK、旧控制器和快照恢复流程，不要等升级后才发现 &lt;code&gt;client/v2&lt;/code&gt; 或 v2 snapshot 不可用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;客户端连接假设&lt;/strong&gt;：如果业务代码使用 &lt;code&gt;clientv3&lt;/code&gt;，确认是否依赖 &lt;code&gt;grpc.WithBlock&lt;/code&gt; 带来的阻塞语义。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快照不可恢复&lt;/strong&gt;：只保存快照不够，至少要在隔离环境验证一次恢复流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;混合版本时间过长&lt;/strong&gt;：滚动升级可以混合版本运行，但不应长期停留在混合状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes 兼容性&lt;/strong&gt;：控制面组件、发行版、kubeadm 与 etcd 版本必须一起评估。&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;etcd 3.7.0 的关键词是“安全修复、v2 清理、客户端语义调整、运维工具增强”。对于新集群，它让技术债更少；对于老集群，它会暴露历史遗留依赖。真正稳妥的升级不是简单替换二进制，而是先确认版本链路、备份可恢复、客户端行为可接受、监控和维护策略同步更新。只要把这些检查前置，v3.6 到 v3.7 的滚动升级可以成为一次相对可控的控制面基础设施升级。&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;etcd 官方 Release：&lt;a class="link" href="https://github.com/etcd-io/etcd/releases/tag/v3.7.0" target="_blank" rel="noopener"
 &gt;https://github.com/etcd-io/etcd/releases/tag/v3.7.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;etcd 官方 CHANGELOG 3.7：&lt;a class="link" href="https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.7.md" target="_blank" rel="noopener"
 &gt;https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.7.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;etcd 官方 v3.7 升级指南：&lt;a class="link" href="https://etcd.io/docs/v3.7/upgrades/upgrade_3_7/" target="_blank" rel="noopener"
 &gt;https://etcd.io/docs/v3.7/upgrades/upgrade_3_7/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;etcd 官方维护文档：&lt;a class="link" href="https://etcd.io/docs/v3.7/op-guide/maintenance/" target="_blank" rel="noopener"
 &gt;https://etcd.io/docs/v3.7/op-guide/maintenance/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kubernetes 官方 etcd 管理文档：&lt;a class="link" href="https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/" target="_blank" rel="noopener"
 &gt;https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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><item><title>Vite 8.1 升级实战：Rolldown、Node 版本与前端构建链检查清单</title><link>https://blog.waihost.com/posts/vite-8-1-upgrade-guide/</link><pubDate>Mon, 06 Jul 2026 22:28:04 +0800</pubDate><guid>https://blog.waihost.com/posts/vite-8-1-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/vite-8-1-upgrade-guide.svg" alt="Featured image of post Vite 8.1 升级实战：Rolldown、Node 版本与前端构建链检查清单" /&gt;&lt;p&gt;Vite 8 已经进入可用于日常升级评估的 8.1.x 分支。根据 Vite 官方发布记录，&lt;code&gt;v8.1.0&lt;/code&gt; 于 2026-06-23 发布，随后 &lt;code&gt;v8.1.1&lt;/code&gt;、&lt;code&gt;v8.1.2&lt;/code&gt; 与 &lt;code&gt;v8.1.3&lt;/code&gt; 在 6 月底到 7 月初持续修复构建、CSS、SSR、依赖扫描等问题；npm registry 中当前 &lt;code&gt;vite@latest&lt;/code&gt; 指向 &lt;code&gt;8.1.3&lt;/code&gt;，并明确要求 Node.js 版本满足 &lt;code&gt;^20.19.0 || &amp;gt;=22.12.0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这篇文章不是简单翻译 changelog，而是把官方迁移文档、GitHub Release/CHANGELOG 与 npm 包元数据合并成一份适合团队落地的升级指南：哪些变化真正会影响项目，哪些配置应该提前检查，以及如何用最小风险把 Vite 7 或早期 Vite 8 项目推进到 8.1.x。&lt;/p&gt;
&lt;h2 id="为什么-vite-81-值得关注"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-vite-81-%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8" class="header-anchor"&gt;&lt;/a&gt;为什么 Vite 8.1 值得关注
&lt;/h2&gt;&lt;p&gt;Vite 8 的核心变化不是“又一个小版本”，而是构建链底层发生了明显转向：官方迁移文档写明，Vite 8 使用 Rolldown 与 Oxc 相关工具替代过去更常见的 esbuild/Rollup 组合。对普通业务项目来说，这通常意味着更快的依赖预构建与生产构建潜力；对有复杂插件、SSR、自定义 Rollup 配置或 monorepo 的项目来说，也意味着需要更认真地验证边界行为。&lt;/p&gt;
&lt;p&gt;Vite 官方的版本策略也说明，当前常规补丁发布集中在 &lt;code&gt;vite@8.1&lt;/code&gt;；重要修复和安全补丁会回补到 &lt;code&gt;vite@7.3&lt;/code&gt; 与 &lt;code&gt;vite@8.0&lt;/code&gt;，安全补丁还会回补到 &lt;code&gt;vite@6.4&lt;/code&gt;。如果项目仍停留在更早版本，虽然短期内可能还能运行，但已经不适合作为长期维护基线。&lt;/p&gt;
&lt;h2 id="升级前先确认-nodejs-版本"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d%e5%85%88%e7%a1%ae%e8%ae%a4-nodejs-%e7%89%88%e6%9c%ac" class="header-anchor"&gt;&lt;/a&gt;升级前先确认 Node.js 版本
&lt;/h2&gt;&lt;p&gt;npm 元数据给出的 &lt;code&gt;vite@8.1.3&lt;/code&gt; engines 为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;^20.19.0 || &amp;gt;=22.12.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这意味着团队不要只写“Node 20+”这么粗略的要求。CI、Dockerfile、开发机版本管理工具都应该精确到小版本，至少满足 Node 20.19.0，或者升级到 22.12.0 及以上。&lt;/p&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;node -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ls vite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm view vite version engines
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果项目使用 pnpm 或 yarn，也建议同步检查 lockfile 与 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;pnpm why vite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm env use --global &lt;span class="m"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 或在 CI 镜像中固定：node:22.12-bookworm / node:22-bookworm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对生产构建来说，Node 版本不一致是最常见的“本地能过、CI 失败”来源之一。升级 Vite 前先统一 Node，能减少很多无效排查。&lt;/p&gt;
&lt;h2 id="核心变化一rolldown-成为默认构建基础"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%b8%80rolldown-%e6%88%90%e4%b8%ba%e9%bb%98%e8%ae%a4%e6%9e%84%e5%bb%ba%e5%9f%ba%e7%a1%80" class="header-anchor"&gt;&lt;/a&gt;核心变化一：Rolldown 成为默认构建基础
&lt;/h2&gt;&lt;p&gt;官方迁移文档提到，Vite 8 使用 Rolldown 和 Oxc based tools。对于多数应用项目，&lt;code&gt;vite build&lt;/code&gt; 命令仍然是同一个命令，但底层打包器行为可能影响以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;依赖预构建、动态导入、chunk 拆分结果；&lt;/li&gt;
&lt;li&gt;自定义插件中对 Rollup hook 返回值、sourcemap、虚拟模块 ID 的假设；&lt;/li&gt;
&lt;li&gt;SSR 构建时的错误堆栈、external 模块处理；&lt;/li&gt;
&lt;li&gt;monorepo 中 pnpm workspace、软链接、&lt;code&gt;.modules.yaml&lt;/code&gt; 解析。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vite 8.1.x 的 changelog 正好体现了这些修复重点：&lt;code&gt;8.1.1&lt;/code&gt; 修复了 bundled dev 下 HMR/lazy compile 产物服务、&lt;code&gt;import.meta.hot.invalidate()&lt;/code&gt; 栈溢出、CSS/Sass &lt;code&gt;@import&lt;/code&gt; 中 tsconfig paths 解析、pnpm workspace 根目录解析等问题；&lt;code&gt;8.1.3&lt;/code&gt; 又修复了嵌套动态导入的 CSS preload、SSR 首行 stacktrace 列位置等问题。&lt;/p&gt;
&lt;p&gt;因此，如果你的项目有复杂插件或 SSR，不建议直接从旧版本跳到 8.1 后立即上线，而应该先建立一组构建回归用例。&lt;/p&gt;
&lt;h2 id="核心变化二默认浏览器目标更新"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%ba%8c%e9%bb%98%e8%ae%a4%e6%b5%8f%e8%a7%88%e5%99%a8%e7%9b%ae%e6%a0%87%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;核心变化二：默认浏览器目标更新
&lt;/h2&gt;&lt;p&gt;Vite 迁移文档说明，&lt;code&gt;build.target&lt;/code&gt; 的默认值 &lt;code&gt;baseline-widely-available&lt;/code&gt; 对应的浏览器版本更新为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Chrome 111
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Edge 111
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Firefox 114
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Safari 16.4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这组版本对应 2026-01-01 的 Baseline Widely Available 特性集合。实际影响是：如果你的用户仍包含更老的浏览器，尤其是企业内网老 Safari、嵌入式 WebView 或长期不更新的 Chromium 内核，就不能完全依赖默认值。&lt;/p&gt;
&lt;p&gt;保守项目可以显式设置构建目标：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// vite.config.ts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vite&amp;#39;&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="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;chrome107&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;edge107&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;firefox104&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;safari16&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但从长期维护角度看，建议先用访问日志或前端监控确认真实用户浏览器分布。如果老版本浏览器占比极低，跟随默认 baseline 通常能换来更少 polyfill、更现代的输出和更简单的调试体验。&lt;/p&gt;
&lt;h2 id="核心变化三依赖优化与-css-处理更需要回归"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e4%b8%89%e4%be%9d%e8%b5%96%e4%bc%98%e5%8c%96%e4%b8%8e-css-%e5%a4%84%e7%90%86%e6%9b%b4%e9%9c%80%e8%a6%81%e5%9b%9e%e5%bd%92" class="header-anchor"&gt;&lt;/a&gt;核心变化三：依赖优化与 CSS 处理更需要回归
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;vite@8.1.3&lt;/code&gt; 的依赖列表包含 &lt;code&gt;rolldown&lt;/code&gt;、&lt;code&gt;lightningcss&lt;/code&gt;、&lt;code&gt;postcss&lt;/code&gt;、&lt;code&gt;picomatch&lt;/code&gt;、&lt;code&gt;tinyglobby&lt;/code&gt; 等。CHANGELOG 中也有多项与 CSS、依赖扫描相关的修复，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inlined CSS 注入时避开 shebang 行；&lt;/li&gt;
&lt;li&gt;嵌套动态导入场景下预加载 CSS；&lt;/li&gt;
&lt;li&gt;使用 lightningcss 时保留 external &lt;code&gt;@import&lt;/code&gt; URL 中的 &lt;code&gt;$&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;CSS 与 Sass &lt;code&gt;@import&lt;/code&gt; 支持解析 tsconfig paths；&lt;/li&gt;
&lt;li&gt;scanner 忽略 &lt;code&gt;ERR_CLOSED_SERVER&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;glob/HMR 匹配支持 &lt;code&gt;caseSensitive&lt;/code&gt; 选项。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这提示我们，升级验证不能只看首页能不能打开，而要覆盖动态路由、懒加载组件、CSS Modules、Sass/Less、别名路径、SSR 页面和生产构建产物。&lt;/p&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;&lt;span class="c1"&gt;# 1. 清理旧缓存，避免被 node_modules/.vite 误导&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf node_modules/.vite dist
&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;# 2. 安装并锁定 Vite 版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add -D vite@8.1.3
&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;# 3. 类型检查、单测、构建&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm typecheck
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm build
&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;# 4. 本地预览生产产物&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm vite preview --host 0.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果是 monorepo，建议在根目录和子包目录分别验证一次，重点看 workspace 依赖是否被正确解析。&lt;/p&gt;
&lt;h2 id="插件与配置迁移建议"&gt;&lt;a href="#%e6%8f%92%e4%bb%b6%e4%b8%8e%e9%85%8d%e7%bd%ae%e8%bf%81%e7%a7%bb%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;插件与配置迁移建议
&lt;/h2&gt;&lt;p&gt;从 Vite 7 升级时，优先检查下面几类配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/src&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 检查 hmr/ws 相关配置是否仍符合迁移文档
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sourcemap&lt;/span&gt;: &lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rollupOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 如果这里依赖 Rollup 特定行为，需要重点回归
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;rolldown-vite&lt;/code&gt;，官方迁移文档建议可把它作为过渡经验参考；迁移到 Vite 8 时，需要撤销 package.json 中对 &lt;code&gt;npm:rolldown-vite@...&lt;/code&gt; 的别名，改回正式的 &lt;code&gt;vite&lt;/code&gt; 依赖。&lt;/p&gt;
&lt;p&gt;插件方面，内部插件或低频维护插件最容易踩坑。建议临时打开 sourcemap，构建后抽查：&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;pnpm build --debug
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果发现插件 hook 返回值、虚拟模块 ID、动态导入分析异常，应先升级插件版本；仍无法解决时，再考虑在 Vite issue 或插件仓库中查找是否已有兼容性修复。&lt;/p&gt;
&lt;h2 id="推荐的团队升级流程"&gt;&lt;a href="#%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%a2%e9%98%9f%e5%8d%87%e7%ba%a7%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;推荐的团队升级流程
&lt;/h2&gt;&lt;p&gt;对于生产项目，推荐按下面节奏推进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;新建升级分支，只做 Node 与 Vite 相关变更；&lt;/li&gt;
&lt;li&gt;固定 Node.js 到 &lt;code&gt;20.19.0+&lt;/code&gt; 或 &lt;code&gt;22.12.0+&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;升级 Vite 到 &lt;code&gt;8.1.3&lt;/code&gt;，同步升级官方框架插件；&lt;/li&gt;
&lt;li&gt;清理缓存后执行类型检查、单测、生产构建；&lt;/li&gt;
&lt;li&gt;对动态导入、CSS、SSR、monorepo 子包做专项回归；&lt;/li&gt;
&lt;li&gt;在测试环境跑一轮真实流量或关键页面巡检；&lt;/li&gt;
&lt;li&gt;合并前保留回滚方案：锁回旧 Vite 版本与旧 lockfile。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果项目规模较大，可以先升级到官方仍支持补丁的 Vite 7.3，再评估 Vite 8.1。这样可以把“安全维护”和“构建链迁移”分成两个风险更小的步骤。&lt;/p&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;Vite 8.1.x 的重点不是语法糖，而是前端构建底座切换后的稳定化：Rolldown/Oxc 成为核心、默认浏览器目标更新、Node.js 最低版本抬高，同时围绕 CSS、SSR、动态导入和 monorepo 持续修复。对新项目来说，直接从 &lt;code&gt;vite@8.1.3&lt;/code&gt; 起步是合理选择；对存量项目来说，升级前先统一 Node 版本，再用构建、预览、SSR、CSS 与插件回归清单逐项验证，才是更稳妥的做法。&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;Vite GitHub Release：&lt;a class="link" href="https://github.com/vitejs/vite/releases/tag/v8.1.3" target="_blank" rel="noopener"
 &gt;https://github.com/vitejs/vite/releases/tag/v8.1.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite CHANGELOG：&lt;a class="link" href="https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md" target="_blank" rel="noopener"
 &gt;https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite 官方迁移文档：&lt;a class="link" href="https://vite.dev/guide/migration" target="_blank" rel="noopener"
 &gt;https://vite.dev/guide/migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vite 官方版本策略：&lt;a class="link" href="https://vite.dev/releases" target="_blank" rel="noopener"
 &gt;https://vite.dev/releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;npm registry &lt;code&gt;vite@latest&lt;/code&gt; 元数据：&lt;a class="link" href="https://registry.npmjs.org/vite/latest" target="_blank" rel="noopener"
 &gt;https://registry.npmjs.org/vite/latest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Spring Data 2025.0.13 收官发布：3.5.x 项目升级与迁移检查清单</title><link>https://blog.waihost.com/posts/spring-data-2025-0-13-upgrade-guide/</link><pubDate>Wed, 01 Jul 2026 01:15:51 +0000</pubDate><guid>https://blog.waihost.com/posts/spring-data-2025-0-13-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/spring-data-2025-0-13-upgrade-guide.svg" alt="Featured image of post Spring Data 2025.0.13 收官发布：3.5.x 项目升级与迁移检查清单" /&gt;&lt;p&gt;Spring Data 2025.0.13 已经发布。对还在 Spring Boot 3.5.x / Spring Data 3.5.x 线上分支上的团队来说，这不是一个“追新功能”的版本，而更像一次收尾性质的维护窗口：官方发布说明明确提到，2025.0.13 是 Spring Data 3.5.x generation 预期的最后一个开源服务版本，并且只包含回归修复。换句话说，如果你的系统还停留在这一代，短期内应该尽快吸收这个补丁；中期则应该把迁移到 2025.1.x / 4.x 系列纳入排期。&lt;/p&gt;
&lt;p&gt;本文用工程实践的角度梳理：这次发布意味着什么、哪些模块值得重点回归、Maven/Gradle 应该怎么升级，以及团队应该怎样规划下一阶段迁移。&lt;/p&gt;
&lt;h2 id="这次发布的关键信息"&gt;&lt;a href="#%e8%bf%99%e6%ac%a1%e5%8f%91%e5%b8%83%e7%9a%84%e5%85%b3%e9%94%ae%e4%bf%a1%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;这次发布的关键信息
&lt;/h2&gt;&lt;p&gt;先把核心事实讲清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Data BOM 版本：&lt;code&gt;2025.0.13&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;发布时间：GitHub Release 显示为 &lt;code&gt;2026-06-24T07:40:08Z&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;发布性质：服务版本，主要面向回归修复。&lt;/li&gt;
&lt;li&gt;官方博客说明：它预期是 Spring Data 3.5.x generation 的最后一个开源发布版本。&lt;/li&gt;
&lt;li&gt;官方建议：尽早升级到最新的 4.0.x（&lt;code&gt;2025.1.x&lt;/code&gt; release train）或 4.1.x release。&lt;/li&gt;
&lt;li&gt;Spring 官方博客还说明，Spring Boot 3.5.16 计划在次日拾取这次 Spring Data 发布。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这几个信息放在一起，结论很直接：&lt;code&gt;2025.0.13&lt;/code&gt; 适合作为 Spring Boot 3.5.x 项目的稳定补丁基线，但不应该被当作长期停留点。&lt;/p&gt;
&lt;h2 id="2025013-包含哪些模块"&gt;&lt;a href="#2025013-%e5%8c%85%e5%90%ab%e5%93%aa%e4%ba%9b%e6%a8%a1%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;2025.0.13 包含哪些模块
&lt;/h2&gt;&lt;p&gt;Spring Data 使用 release train / BOM 管理一组子项目版本。&lt;code&gt;2025.0.13&lt;/code&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;Spring Data Commons&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Repository 抽象、分页、排序、映射基础设施&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data JPA&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;JPA / Hibernate 持久化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data MongoDB&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;MongoDB 文档数据库访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Redis&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Redis 数据访问与缓存相关集成&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Cassandra&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;Cassandra 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Elasticsearch&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5.5.13&lt;/td&gt;
					&lt;td&gt;Elasticsearch 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Couchbase&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5.5.13&lt;/td&gt;
					&lt;td&gt;Couchbase 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Neo4j&lt;/td&gt;
					&lt;td style="text-align: right"&gt;7.5.13&lt;/td&gt;
					&lt;td&gt;图数据库访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data REST&lt;/td&gt;
					&lt;td style="text-align: right"&gt;4.5.13&lt;/td&gt;
					&lt;td&gt;Repository REST 暴露&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data Relational&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;JDBC / R2DBC 等关系型访问基础&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data LDAP&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;LDAP 数据访问&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spring Data KeyValue&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3.5.13&lt;/td&gt;
					&lt;td&gt;Key-Value 抽象支持&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果你的项目通过 Spring Boot 管理依赖，多数情况下不需要手工声明这些子模块版本；跟随 Spring Boot 3.5.16 或显式导入 &lt;code&gt;spring-data-bom:2025.0.13&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2 id="值得关注的修复点"&gt;&lt;a href="#%e5%80%bc%e5%be%97%e5%85%b3%e6%b3%a8%e7%9a%84%e4%bf%ae%e5%a4%8d%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;值得关注的修复点
&lt;/h2&gt;&lt;p&gt;这次发布不是大版本功能升级，重点是降低已知回归风险。官方 Release 中比较值得应用开发团队关注的点包括下面几类。&lt;/p&gt;
&lt;h3 id="1-spring-data-commons字段解析相关修复"&gt;&lt;a href="#1-spring-data-commons%e5%ad%97%e6%ae%b5%e8%a7%a3%e6%9e%90%e7%9b%b8%e5%85%b3%e4%bf%ae%e5%a4%8d" class="header-anchor"&gt;&lt;/a&gt;1. Spring Data Commons：字段解析相关修复
&lt;/h3&gt;&lt;p&gt;Spring Data Commons 3.5.13 修复了 &lt;code&gt;TypeDiscoverer&lt;/code&gt; 在子类字段与父类同名字段场景下的解析问题。Release 描述中提到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保留子类字段，而不是被父类中被遮蔽的字段覆盖。&lt;/li&gt;
&lt;li&gt;修复属性类型从隐藏的父类字段解析，而不是从子类字段解析的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类问题通常不会影响简单 Entity，但在下面这些场景里更容易踩坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;领域模型存在继承层次；&lt;/li&gt;
&lt;li&gt;子类重新声明了父类中的同名字段；&lt;/li&gt;
&lt;li&gt;Repository 查询、投影、映射元数据依赖属性类型推断；&lt;/li&gt;
&lt;li&gt;代码生成或历史模型导致字段命名不够干净。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的项目里有复杂继承模型，升级后建议重点跑 Repository 查询、DTO 投影、Example 查询、排序分页与自定义转换器相关测试。&lt;/p&gt;
&lt;h3 id="2-spring-data-jpajpql--eql-单字符字符串解析修复"&gt;&lt;a href="#2-spring-data-jpajpql--eql-%e5%8d%95%e5%ad%97%e7%ac%a6%e5%ad%97%e7%ac%a6%e4%b8%b2%e8%a7%a3%e6%9e%90%e4%bf%ae%e5%a4%8d" class="header-anchor"&gt;&lt;/a&gt;2. Spring Data JPA：JPQL / EQL 单字符字符串解析修复
&lt;/h3&gt;&lt;p&gt;Spring Data JPA 3.5.13 修复了 JPQL 和 EQL 中集合成员表达式的单字符字符串字面量解析问题。Release 中提到的问题是：单字符字符串字面量作为 &lt;code&gt;MEMBER OF&lt;/code&gt; 左操作数时解析失败。&lt;/p&gt;
&lt;p&gt;受影响的代码可能长得类似下面这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;select u from User u where &amp;#39;A&amp;#39; member of u.flags&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="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;findUsersWithFlagA&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;手写 &lt;code&gt;@Query&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;动态拼接 JPQL / HQL；&lt;/li&gt;
&lt;li&gt;使用集合属性做成员判断；&lt;/li&gt;
&lt;li&gt;查询中存在单字符枚举值或标记值；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;就应该把这些查询纳入回归测试。&lt;/p&gt;
&lt;h3 id="3-spring-data-jpahibernate-依赖升级"&gt;&lt;a href="#3-spring-data-jpahibernate-%e4%be%9d%e8%b5%96%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;3. Spring Data JPA：Hibernate 依赖升级
&lt;/h3&gt;&lt;p&gt;Spring Data JPA 3.5.13 的 Release 还列出了 Hibernate 升级到 &lt;code&gt;6.6.53.Final&lt;/code&gt;。这通常是好事，但 ORM 层升级也最容易暴露边界行为差异，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL 生成变化；&lt;/li&gt;
&lt;li&gt;方言兼容性差异；&lt;/li&gt;
&lt;li&gt;延迟加载与关联抓取问题；&lt;/li&gt;
&lt;li&gt;Criteria / JPQL 解析差异；&lt;/li&gt;
&lt;li&gt;数据库驱动与 Hibernate 方言组合问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，JPA 项目升级时不要只跑单元测试，最好至少补一轮连接真实数据库的集成测试。&lt;/p&gt;
&lt;h2 id="maven-项目怎么升级"&gt;&lt;a href="#maven-%e9%a1%b9%e7%9b%ae%e6%80%8e%e4%b9%88%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;Maven 项目怎么升级
&lt;/h2&gt;&lt;p&gt;如果你的项目使用 Spring Boot 3.5.x，并通过 &lt;code&gt;spring-boot-starter-parent&lt;/code&gt; 管理版本，推荐优先升级 Spring Boot 到包含该 Spring Data 版本的补丁版本，例如 Spring Boot 3.5.16：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;parent&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;3.5.16&lt;span class="nt"&gt;&amp;lt;/version&amp;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;&amp;lt;relativePath/&amp;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;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果项目不能立刻升级 Spring Boot，但确实需要单独锁定 Spring Data release train，可以在 &lt;code&gt;dependencyManagement&lt;/code&gt; 中导入 BOM：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependencyManagement&amp;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;&amp;lt;dependencies&amp;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;&amp;lt;dependency&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.data&lt;span class="nt"&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-data-bom&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;2025.0.13&lt;span class="nt"&gt;&amp;lt;/version&amp;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;&amp;lt;scope&amp;gt;&lt;/span&gt;import&lt;span class="nt"&gt;&amp;lt;/scope&amp;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;&amp;lt;type&amp;gt;&lt;/span&gt;pom&lt;span class="nt"&gt;&amp;lt;/type&amp;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;&amp;lt;/dependency&amp;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;&amp;lt;/dependencies&amp;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;&amp;lt;/dependencyManagement&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不过要注意：Spring Boot 自身也会管理 Hibernate、数据库驱动、Spring Framework 等版本。单独覆盖 Spring Data BOM 虽然可行，但更容易形成“局部升级、整体依赖矩阵未验证”的状态。生产项目优先选择 Spring Boot 补丁版本，通常更稳。&lt;/p&gt;
&lt;h2 id="gradle-项目怎么升级"&gt;&lt;a href="#gradle-%e9%a1%b9%e7%9b%ae%e6%80%8e%e4%b9%88%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;Gradle 项目怎么升级
&lt;/h2&gt;&lt;p&gt;使用 Spring Boot Gradle 插件的项目，最简单的方式同样是升级 Boot 版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.springframework.boot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;3.5.16&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;io.spring.dependency-management&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.1.7&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;java&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果需要显式导入 Spring Data BOM，可以这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dependencyManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;imports&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mavenBom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;org.springframework.data:spring-data-bom:2025.0.13&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&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;./gradlew dependencyInsight --dependency spring-data-commons --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./gradlew dependencyInsight --dependency spring-data-jpa --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./gradlew dependencyInsight --dependency hibernate-core --configuration runtimeClasspath
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Maven 项目可以用：&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;./mvnw dependency:tree -Dincludes&lt;span class="o"&gt;=&lt;/span&gt;org.springframework.data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./mvnw dependency:tree -Dincludes&lt;span class="o"&gt;=&lt;/span&gt;org.hibernate.orm:hibernate-core
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="升级前后的回归检查清单"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d%e5%90%8e%e7%9a%84%e5%9b%9e%e5%bd%92%e6%a3%80%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;升级前后的回归检查清单
&lt;/h2&gt;&lt;p&gt;建议把这次升级当作一次低风险但必须验证的维护发布。可以按下面顺序做。&lt;/p&gt;
&lt;h3 id="升级前"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%89%8d" class="header-anchor"&gt;&lt;/a&gt;升级前
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;记录当前 Spring Boot、Spring Data、Hibernate、数据库驱动版本。&lt;/li&gt;
&lt;li&gt;确认项目是否手工覆盖过 &lt;code&gt;spring-data-*&lt;/code&gt; 或 Hibernate 版本。&lt;/li&gt;
&lt;li&gt;找出所有自定义 Repository、&lt;code&gt;@Query&lt;/code&gt;、Specification、Querydsl、Criteria 查询。&lt;/li&gt;
&lt;li&gt;标记有继承结构的 Entity / Document / Projection。&lt;/li&gt;
&lt;li&gt;准备真实数据库或测试容器环境，而不是只跑 mock 测试。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="升级后"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;升级后
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;运行完整单元测试与集成测试。&lt;/li&gt;
&lt;li&gt;对核心 Repository 做 CRUD、分页、排序、复杂条件查询回归。&lt;/li&gt;
&lt;li&gt;对 JPA 项目检查启动日志中的 Hibernate 版本与 SQL 方言。&lt;/li&gt;
&lt;li&gt;对 MongoDB / Redis / Elasticsearch 项目检查连接、序列化与索引相关逻辑。&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;dependency:tree&lt;/code&gt; 或 &lt;code&gt;dependencyInsight&lt;/code&gt; 确认没有混入旧版本 Spring Data 模块。&lt;/li&gt;
&lt;li&gt;在预发环境跑一轮真实流量或关键任务。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="为什么不要长期停留在-20250x"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%a6%81%e9%95%bf%e6%9c%9f%e5%81%9c%e7%95%99%e5%9c%a8-20250x" class="header-anchor"&gt;&lt;/a&gt;为什么不要长期停留在 2025.0.x
&lt;/h2&gt;&lt;p&gt;官方已经把 2025.0.13 定位为 3.5.x generation 预期的最后一个开源服务版本，这意味着后续开源补丁会转向更新的 release train。对企业项目来说，这通常带来三个影响：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安全与兼容性窗口变窄&lt;/strong&gt;：依赖链上的修复会优先进入新分支。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;升级跨度变大&lt;/strong&gt;：越晚迁移到 2025.1.x / 4.x，累计变化越多，单次升级风险越高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生态同步成本上升&lt;/strong&gt;：Spring Boot、Spring Framework、Hibernate、数据库驱动、云原生组件都会继续演进。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此更合理的策略是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;短期：把 3.5.x 项目升级到 &lt;code&gt;2025.0.13&lt;/code&gt; / Boot 3.5.16，拿到最后一批回归修复。&lt;/li&gt;
&lt;li&gt;中期：建立 2025.1.x 或 4.x 的兼容性分支，跑依赖树和自动化测试。&lt;/li&gt;
&lt;li&gt;长期：把主要业务线迁移到新的 Spring Boot / Spring Data 主线，减少历史分支维护成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="团队落地建议"&gt;&lt;a href="#%e5%9b%a2%e9%98%9f%e8%90%bd%e5%9c%b0%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;团队落地建议
&lt;/h2&gt;&lt;p&gt;如果你负责一个线上 Spring 项目，可以按下面的节奏推进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先做补丁升级&lt;/strong&gt;：在当前主线或维护分支升级到 Spring Boot 3.5.16，或至少导入 Spring Data BOM 2025.0.13。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;锁定验证范围&lt;/strong&gt;：重点测试 Repository、JPA 查询、继承模型、MongoDB/Redis 序列化、Elasticsearch 查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保留依赖快照&lt;/strong&gt;：把升级前后的依赖树保存到 CI 产物或变更单，方便排查回滚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预留迁移分支&lt;/strong&gt;：不要等到必须升级时才看 2025.1.x / 4.x，提前建分支验证编译、启动和关键链路。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免局部硬覆盖&lt;/strong&gt;：除非有明确原因，否则不要长期手工覆盖 Spring Data 子模块版本，让 Spring Boot BOM 统一管理更安全。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="小结"&gt;&lt;a href="#%e5%b0%8f%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;小结
&lt;/h2&gt;&lt;p&gt;Spring Data 2025.0.13 的价值不在于新特性，而在于给 3.5.x generation 一个相对稳妥的收尾版本。对生产系统来说，这类版本往往比“功能发布”更值得及时处理：它能减少已知回归风险，也给后续迁移到 2025.1.x / 4.x 留出缓冲。&lt;/p&gt;
&lt;p&gt;如果你的系统还在 Spring Boot 3.5.x，建议尽快完成补丁升级与回归验证；如果你已经准备进入 Spring Boot 4 / Spring Data 4.x 阶段，则可以把 &lt;code&gt;2025.0.13&lt;/code&gt; 当作维护分支的最后稳定基线，同时把主要精力放到下一代 release train 的兼容性验证上。&lt;/p&gt;</description></item><item><title>Spring Boot 4.0 正式发布后该怎么升级：核心变化、兼容性影响与实战建议</title><link>https://blog.waihost.com/posts/spring-boot-4-0-upgrade-guide/</link><pubDate>Mon, 29 Jun 2026 14:40:54 +0000</pubDate><guid>https://blog.waihost.com/posts/spring-boot-4-0-upgrade-guide/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/spring-boot-4-0-upgrade-guide.svg" alt="Featured image of post Spring Boot 4.0 正式发布后该怎么升级：核心变化、兼容性影响与实战建议" /&gt;&lt;p&gt;Spring Boot 4.0 已经正式 GA。对很多 Java 团队来说，这不是一次普通的小版本升级，而是一次带有明显“代际变化”特征的升级：底座切换到 Spring Framework 7，运行基线与依赖兼容性被整体抬高，同时也带来了更现代的工程能力。&lt;/p&gt;
&lt;p&gt;如果你关心的问题是“Spring Boot 4.0 到底带来了什么”“现有项目升不升、怎么升、坑在哪”，这篇文章会从&lt;strong&gt;已公开的官方信息&lt;/strong&gt;出发，梳理它的核心变化、升级影响以及更稳妥的落地路径。&lt;/p&gt;
&lt;h2 id="spring-boot-40-现在处于什么状态"&gt;&lt;a href="#spring-boot-40-%e7%8e%b0%e5%9c%a8%e5%a4%84%e4%ba%8e%e4%bb%80%e4%b9%88%e7%8a%b6%e6%80%81" class="header-anchor"&gt;&lt;/a&gt;Spring Boot 4.0 现在处于什么状态
&lt;/h2&gt;&lt;p&gt;从 Spring 官方博客可以确认，&lt;strong&gt;Spring Boot 4.0.0 已于 2025-11-20 正式发布&lt;/strong&gt;，并且已经可以从 Maven Central 获取。与此同时，Spring 官方文档站已经提供 4.0.x 文档线，这说明 4.0 不再是预览版，而是进入了正式维护周期。&lt;/p&gt;
&lt;p&gt;这意味着对于已经在 3.x 稳定运行的团队来说，现在讨论的重点已经不再是“能不能尝鲜”，而是“是否值得安排升级窗口，以及怎么降低升级风险”。&lt;/p&gt;
&lt;h2 id="为什么说-spring-boot-40-是一次代际升级"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%af%b4-spring-boot-40-%e6%98%af%e4%b8%80%e6%ac%a1%e4%bb%a3%e9%99%85%e5%8d%87%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;为什么说 Spring Boot 4.0 是一次代际升级
&lt;/h2&gt;&lt;p&gt;Spring 官方对 4.0 的定调非常明确：它是&lt;strong&gt;构建在 Spring Framework 7 之上的新一代 Spring Boot&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这句话的分量很重。因为这意味着它不只是新增几个 starter 或者小幅改造自动配置，而是整个技术基线都随之上移。很多团队在迁移时遇到的问题，根源并不在 Boot 自身，而是出在与之配套的 Java 版本、Servlet 基线、第三方依赖、容器选择和老旧 API 使用方式上。&lt;/p&gt;
&lt;p&gt;换句话说，Spring Boot 4.0 的价值和成本都比常规次版本升级更高：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;价值在于它把 Spring 生态带入了新的工程阶段；&lt;/li&gt;
&lt;li&gt;成本在于你必须把项目的“底层地基”一起升级。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="spring-boot-40-的核心变化有哪些"&gt;&lt;a href="#spring-boot-40-%e7%9a%84%e6%a0%b8%e5%bf%83%e5%8f%98%e5%8c%96%e6%9c%89%e5%93%aa%e4%ba%9b" class="header-anchor"&gt;&lt;/a&gt;Spring Boot 4.0 的核心变化有哪些
&lt;/h2&gt;&lt;h3 id="1-基于-spring-framework-7"&gt;&lt;a href="#1-%e5%9f%ba%e4%ba%8e-spring-framework-7" class="header-anchor"&gt;&lt;/a&gt;1. 基于 Spring Framework 7
&lt;/h3&gt;&lt;p&gt;这是所有变化的起点。Spring Boot 4.0 不是孤立演进，而是直接建立在 Spring Framework 7 之上。&lt;/p&gt;
&lt;p&gt;对于开发者来说，这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring 体系下的一些长期演进点会在 4.0 更集中地体现出来；&lt;/li&gt;
&lt;li&gt;与 Framework 7 绑定的兼容性要求会直接影响 Boot 项目；&lt;/li&gt;
&lt;li&gt;周边生态是否支持 Spring Framework 7，会成为升级前必须核查的事项。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-spring-boot-代码库完成模块化重构"&gt;&lt;a href="#2-spring-boot-%e4%bb%a3%e7%a0%81%e5%ba%93%e5%ae%8c%e6%88%90%e6%a8%a1%e5%9d%97%e5%8c%96%e9%87%8d%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;2. Spring Boot 代码库完成模块化重构
&lt;/h3&gt;&lt;p&gt;官方将其描述为 &lt;strong&gt;complete modularization of the Spring Boot codebase&lt;/strong&gt;。这不是一个面向业务代码直接可见的“语法特性”，但它对框架演进很重要。&lt;/p&gt;
&lt;p&gt;模块化重构带来的直接信号是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot 内部的职责边界更加清晰；&lt;/li&gt;
&lt;li&gt;产物会朝着&lt;strong&gt;更小、更聚焦的 jar&lt;/strong&gt; 方向演进；&lt;/li&gt;
&lt;li&gt;对后续自动配置裁剪、维护成本控制和长期扩展更友好。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你所在团队很关注启动体积、依赖治理和框架透明度，这会是一个值得关注的变化。&lt;/p&gt;
&lt;h3 id="3-null-safety-增强引入-jspecify-方向的统一改进"&gt;&lt;a href="#3-null-safety-%e5%a2%9e%e5%bc%ba%e5%bc%95%e5%85%a5-jspecify-%e6%96%b9%e5%90%91%e7%9a%84%e7%bb%9f%e4%b8%80%e6%94%b9%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;3. Null Safety 增强，引入 JSpecify 方向的统一改进
&lt;/h3&gt;&lt;p&gt;官方明确提到，Spring Boot 4.0 在整个产品线范围内加强了 &lt;strong&gt;null safety&lt;/strong&gt;，并采用 &lt;strong&gt;JSpecify&lt;/strong&gt; 相关改进。&lt;/p&gt;
&lt;p&gt;这类变化不会像一个新 starter 那样直观，但它会逐步影响：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IDE 的静态提示；&lt;/li&gt;
&lt;li&gt;Java / Kotlin 混合工程中的空安全语义；&lt;/li&gt;
&lt;li&gt;编译期和分析期对潜在 NPE 风险的暴露。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对老项目来说，这也意味着一些“以前没出事但本来就不安全”的写法，可能会在升级后更容易被工具识别出来。&lt;/p&gt;
&lt;h3 id="4-java-25-一等支持同时最低仍要求-java-17"&gt;&lt;a href="#4-java-25-%e4%b8%80%e7%ad%89%e6%94%af%e6%8c%81%e5%90%8c%e6%97%b6%e6%9c%80%e4%bd%8e%e4%bb%8d%e8%a6%81%e6%b1%82-java-17" class="header-anchor"&gt;&lt;/a&gt;4. Java 25 一等支持，同时最低仍要求 Java 17
&lt;/h3&gt;&lt;p&gt;Spring 官方对 Java 支持给出了非常清晰的说法：Spring Boot 4.0 对 &lt;strong&gt;Java 25 提供一等支持&lt;/strong&gt;，同时继续兼容 &lt;strong&gt;Java 17&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从官方系统要求页还能进一步确认：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot 4.0.x &lt;strong&gt;最低需要 Java 17&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;当前文档线显示其兼容范围可覆盖更高版本 Java；&lt;/li&gt;
&lt;li&gt;它要求与 Spring Framework 7.0.x 配套使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这说明两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果你的项目还停留在 Java 11 或更低，升级 Boot 4.0 就不是单纯改依赖版本，而是一次 JDK 基线升级项目；&lt;/li&gt;
&lt;li&gt;如果你已经在 Java 17 或更高版本上运行，那么迁移门槛会显著降低。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="5-http-service-clients-获得自动配置支持"&gt;&lt;a href="#5-http-service-clients-%e8%8e%b7%e5%be%97%e8%87%aa%e5%8a%a8%e9%85%8d%e7%bd%ae%e6%94%af%e6%8c%81" class="header-anchor"&gt;&lt;/a&gt;5. HTTP Service Clients 获得自动配置支持
&lt;/h3&gt;&lt;p&gt;Spring Boot 4.0 Release Notes 提到，它新增了 &lt;strong&gt;HTTP Service Clients 的自动配置支持与配置属性支持&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这类能力的价值在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以通过接口式、声明式方式定义 HTTP 调用；&lt;/li&gt;
&lt;li&gt;比手写一层层 WebClient / RestTemplate 包装更简洁；&lt;/li&gt;
&lt;li&gt;更适合内部服务调用、SDK 化封装和接口契约式开发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的系统中已经存在大量对外部服务或内部微服务的 HTTP 调用，这部分能力值得重点关注，因为它能让调用层写法更现代、更一致。&lt;/p&gt;
&lt;h3 id="6-mvc-与-webflux-获得-api-versioning-自动配置"&gt;&lt;a href="#6-mvc-%e4%b8%8e-webflux-%e8%8e%b7%e5%be%97-api-versioning-%e8%87%aa%e5%8a%a8%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;6. MVC 与 WebFlux 获得 API Versioning 自动配置
&lt;/h3&gt;&lt;p&gt;Spring Boot 4.0 还正式引入了 &lt;strong&gt;API Versioning 自动配置&lt;/strong&gt;，并同时支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring MVC&lt;/li&gt;
&lt;li&gt;Spring WebFlux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着 API 版本治理从“每个团队自己约定、自己封装”进一步走向“框架级支持”。&lt;/p&gt;
&lt;p&gt;对于存在以下需求的系统，这个改动很有价值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同时维护多个 API 版本；&lt;/li&gt;
&lt;li&gt;对旧客户端进行平滑兼容；&lt;/li&gt;
&lt;li&gt;需要在版本治理层面减少重复样板代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="7-jms-支持扩展到新的-jmsclient-api"&gt;&lt;a href="#7-jms-%e6%94%af%e6%8c%81%e6%89%a9%e5%b1%95%e5%88%b0%e6%96%b0%e7%9a%84-jmsclient-api" class="header-anchor"&gt;&lt;/a&gt;7. JMS 支持扩展到新的 JmsClient API
&lt;/h3&gt;&lt;p&gt;对于仍在使用 JMS 的企业应用场景，Spring Boot 4.0 的自动配置现在支持新的 &lt;strong&gt;JmsClient API&lt;/strong&gt;，同时保持对 &lt;strong&gt;JmsTemplate&lt;/strong&gt; 和 &lt;strong&gt;JmsMessagingTemplate&lt;/strong&gt; 的兼容。&lt;/p&gt;
&lt;p&gt;这意味着老系统不会被强迫一次性大改，但新接口能力已经被纳入官方支持范围，团队可以根据节奏逐步迁移。&lt;/p&gt;
&lt;h3 id="8-taskdecorator-组合能力增强"&gt;&lt;a href="#8-taskdecorator-%e7%bb%84%e5%90%88%e8%83%bd%e5%8a%9b%e5%a2%9e%e5%bc%ba" class="header-anchor"&gt;&lt;/a&gt;8. TaskDecorator 组合能力增强
&lt;/h3&gt;&lt;p&gt;在任务执行和任务调度自动配置方面，Spring Boot 4.0 支持多个 &lt;strong&gt;TaskDecorator&lt;/strong&gt;，并会将它们组合成 &lt;strong&gt;CompositeTaskDecorator&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个变化对日常业务代码未必显眼，但对工程化能力很有帮助，尤其在这些场景下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;链路追踪上下文透传；&lt;/li&gt;
&lt;li&gt;日志 MDC 传递；&lt;/li&gt;
&lt;li&gt;租户上下文封装；&lt;/li&gt;
&lt;li&gt;审计信息注入。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于对异步任务治理比较重视的团队，这属于非常实用的增强。&lt;/p&gt;
&lt;h3 id="9-新增-opentelemetry-starter"&gt;&lt;a href="#9-%e6%96%b0%e5%a2%9e-opentelemetry-starter" class="header-anchor"&gt;&lt;/a&gt;9. 新增 OpenTelemetry starter
&lt;/h3&gt;&lt;p&gt;Spring Boot 4.0 新增了 &lt;strong&gt;&lt;code&gt;spring-boot-starter-opentelemetry&lt;/code&gt;&lt;/strong&gt;，并会自动带入面向 &lt;strong&gt;OTLP&lt;/strong&gt; 的指标与链路导出依赖，同时自动配置 OpenTelemetry SDK。&lt;/p&gt;
&lt;p&gt;这释放出一个非常明确的信号：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Spring Boot 在可观测性方向上，正进一步向 OpenTelemetry 标准体系靠拢。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;如果你的系统正在推进云原生可观测性，或者已经使用统一的 metrics / traces 采集链路，那么 4.0 会比过去更顺手。&lt;/p&gt;
&lt;h3 id="10-gradle-9-支持"&gt;&lt;a href="#10-gradle-9-%e6%94%af%e6%8c%81" class="header-anchor"&gt;&lt;/a&gt;10. Gradle 9 支持
&lt;/h3&gt;&lt;p&gt;官方 Release Notes 明确确认：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot 4.0 支持 &lt;strong&gt;Gradle 9&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;同时继续支持 &lt;strong&gt;Gradle 8.14+&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;Maven 方面要求 &lt;strong&gt;3.6.3+&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的项目使用 Gradle，并且一直想与新版本构建链路保持同步，4.0 在这方面给出了比较明确的支持保证。&lt;/p&gt;
&lt;h2 id="升级到-spring-boot-40-时真正需要注意什么"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e5%88%b0-spring-boot-40-%e6%97%b6%e7%9c%9f%e6%ad%a3%e9%9c%80%e8%a6%81%e6%b3%a8%e6%84%8f%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;升级到 Spring Boot 4.0 时真正需要注意什么
&lt;/h2&gt;&lt;p&gt;对于大多数团队来说，真正决定“能不能顺利升级”的，不是新特性，而是下面这些兼容性影响。&lt;/p&gt;
&lt;h3 id="1-官方建议先升到最新-35x再升-40"&gt;&lt;a href="#1-%e5%ae%98%e6%96%b9%e5%bb%ba%e8%ae%ae%e5%85%88%e5%8d%87%e5%88%b0%e6%9c%80%e6%96%b0-35x%e5%86%8d%e5%8d%87-40" class="header-anchor"&gt;&lt;/a&gt;1. 官方建议：先升到最新 3.5.x，再升 4.0
&lt;/h3&gt;&lt;p&gt;Spring 官方 Release Notes 和 Migration Guide 都明确建议：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;如果你当前项目还不在 3.5.x，最好先升级到最新 3.5.x，再迁移到 4.0。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这是一个非常实用的建议。&lt;/p&gt;
&lt;p&gt;因为跨越多个大/小版本直接跳到 4.0，意味着你会同时面对：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;旧依赖问题；&lt;/li&gt;
&lt;li&gt;废弃 API 问题；&lt;/li&gt;
&lt;li&gt;配置属性变更问题；&lt;/li&gt;
&lt;li&gt;底层基线问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而如果先对齐到 3.5.x，再迁移到 4.0，很多问题会更容易分层定位。&lt;/p&gt;
&lt;h3 id="2-javakotlingraalvmservlet-基线整体抬升"&gt;&lt;a href="#2-javakotlingraalvmservlet-%e5%9f%ba%e7%ba%bf%e6%95%b4%e4%bd%93%e6%8a%ac%e5%8d%87" class="header-anchor"&gt;&lt;/a&gt;2. Java、Kotlin、GraalVM、Servlet 基线整体抬升
&lt;/h3&gt;&lt;p&gt;官方 Migration Guide 给出的迁移基线包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java 17+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kotlin 2.2+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GraalVM native-image 25+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jakarta EE 11&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Servlet 6.1 baseline&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着，如果你的项目还有这些历史包袱：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;老 JDK；&lt;/li&gt;
&lt;li&gt;低版本 Kotlin；&lt;/li&gt;
&lt;li&gt;旧 native-image 构建链；&lt;/li&gt;
&lt;li&gt;旧 servlet 容器；&lt;/li&gt;
&lt;li&gt;对 Jakarta 升级准备不足；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么升级 Boot 4.0 的工作量会比“改 pom 版本号”大得多。&lt;/p&gt;
&lt;h3 id="3-undertow-支持被移除"&gt;&lt;a href="#3-undertow-%e6%94%af%e6%8c%81%e8%a2%ab%e7%a7%bb%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;3. Undertow 支持被移除
&lt;/h3&gt;&lt;p&gt;这是 4.0 升级里最容易被忽略、但实际影响很大的点之一。&lt;/p&gt;
&lt;p&gt;官方 Migration Guide 明确指出：由于 Spring Boot 4.0 需要 &lt;strong&gt;Servlet 6.1 baseline&lt;/strong&gt;，而 &lt;strong&gt;Undertow 当前不兼容这一基线&lt;/strong&gt;，因此 Boot 4 &lt;strong&gt;移除了 Undertow 支持&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Undertow starter&lt;/li&gt;
&lt;li&gt;作为嵌入式服务器的 Undertow 能力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的应用当前使用 Undertow，那么升级到 4.0 时必须评估迁移到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tomcat 11.0.x&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jetty 12.1.x&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这不是一个可忽略的 warning，而是明确的支持移除。&lt;/p&gt;
&lt;h3 id="4-spring-boot-3x-中已废弃的类方法配置在-40-中可能已经删除"&gt;&lt;a href="#4-spring-boot-3x-%e4%b8%ad%e5%b7%b2%e5%ba%9f%e5%bc%83%e7%9a%84%e7%b1%bb%e6%96%b9%e6%b3%95%e9%85%8d%e7%bd%ae%e5%9c%a8-40-%e4%b8%ad%e5%8f%af%e8%83%bd%e5%b7%b2%e7%bb%8f%e5%88%a0%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;4. Spring Boot 3.x 中已废弃的类、方法、配置，在 4.0 中可能已经删除
&lt;/h3&gt;&lt;p&gt;官方 Migration Guide 明确说明：&lt;strong&gt;Spring Boot 3.x 中标记为 deprecated 的 classes、methods、properties，在 4.0 中已经被删除。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这会带来几类典型问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编译失败；&lt;/li&gt;
&lt;li&gt;启动失败；&lt;/li&gt;
&lt;li&gt;配置项失效但不一定立即显眼；&lt;/li&gt;
&lt;li&gt;某些集成测试才暴露行为变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以在升级前，最好先把当前项目里所有 Boot 3.x 已知的 deprecated 用法清理一遍，不要把这些历史债务带进 4.0。&lt;/p&gt;
&lt;h3 id="5-配置属性迁移建议使用-spring-boot-properties-migrator"&gt;&lt;a href="#5-%e9%85%8d%e7%bd%ae%e5%b1%9e%e6%80%a7%e8%bf%81%e7%a7%bb%e5%bb%ba%e8%ae%ae%e4%bd%bf%e7%94%a8-spring-boot-properties-migrator" class="header-anchor"&gt;&lt;/a&gt;5. 配置属性迁移建议使用 &lt;code&gt;spring-boot-properties-migrator&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;对于配置项重命名或移除问题，官方建议可以在升级阶段&lt;strong&gt;临时引入&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-properties-migrator&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;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;&amp;lt;scope&amp;gt;&lt;/span&gt;runtime&lt;span class="nt"&gt;&amp;lt;/scope&amp;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;&amp;lt;/dependency&amp;gt;&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;ul&gt;
&lt;li&gt;在启动时分析环境配置；&lt;/li&gt;
&lt;li&gt;打印属性迁移相关诊断信息；&lt;/li&gt;
&lt;li&gt;对部分已改名属性提供运行时迁移帮助。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但要注意两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它只是迁移辅助工具，不是长期依赖；&lt;/li&gt;
&lt;li&gt;迁移完成后应该移除。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="6-第三方依赖兼容性比-boot-自身更值得重点检查"&gt;&lt;a href="#6-%e7%ac%ac%e4%b8%89%e6%96%b9%e4%be%9d%e8%b5%96%e5%85%bc%e5%ae%b9%e6%80%a7%e6%af%94-boot-%e8%87%aa%e8%ba%ab%e6%9b%b4%e5%80%bc%e5%be%97%e9%87%8d%e7%82%b9%e6%a3%80%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;6. 第三方依赖兼容性比 Boot 自身更值得重点检查
&lt;/h3&gt;&lt;p&gt;官方迁移建议中特别提到，要对比 3.5.x 与 4.0.x 的 dependency management，并重点检查&lt;strong&gt;非 Boot 管理的依赖&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这在真实项目里非常关键。因为很多升级失败案例并不是 Spring Boot 本身出问题，而是下面这些组件没有及时跟上：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring Cloud 对应版本；&lt;/li&gt;
&lt;li&gt;第三方 starter；&lt;/li&gt;
&lt;li&gt;安全、监控、消息队列等中间件整合包；&lt;/li&gt;
&lt;li&gt;自定义 BOM；&lt;/li&gt;
&lt;li&gt;直接锁定版本的 Jakarta / servlet 生态库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以在升级方案上，最稳妥的方式是把“依赖兼容性核查”当作单独工作项来做，而不是在 CI 报错后再被动补救。&lt;/p&gt;
&lt;h2 id="一条更稳妥的-spring-boot-40-升级路径"&gt;&lt;a href="#%e4%b8%80%e6%9d%a1%e6%9b%b4%e7%a8%b3%e5%a6%a5%e7%9a%84-spring-boot-40-%e5%8d%87%e7%ba%a7%e8%b7%af%e5%be%84" class="header-anchor"&gt;&lt;/a&gt;一条更稳妥的 Spring Boot 4.0 升级路径
&lt;/h2&gt;&lt;p&gt;如果你准备在生产项目中升级到 Spring Boot 4.0，可以参考下面这条更稳妥的路径：&lt;/p&gt;
&lt;h3 id="第一步先把项目升级到最新-spring-boot-35x"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%85%88%e6%8a%8a%e9%a1%b9%e7%9b%ae%e5%8d%87%e7%ba%a7%e5%88%b0%e6%9c%80%e6%96%b0-spring-boot-35x" class="header-anchor"&gt;&lt;/a&gt;第一步：先把项目升级到最新 Spring Boot 3.5.x
&lt;/h3&gt;&lt;p&gt;这样做的目的不是浪费时间，而是先把 3.x 线上的兼容问题清干净，降低跨代升级风险。&lt;/p&gt;
&lt;h3 id="第二步清理所有已知-deprecated-api-与配置"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e6%b8%85%e7%90%86%e6%89%80%e6%9c%89%e5%b7%b2%e7%9f%a5-deprecated-api-%e4%b8%8e%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;第二步：清理所有已知 deprecated API 与配置
&lt;/h3&gt;&lt;p&gt;重点排查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;启动日志中的 deprecated 提示；&lt;/li&gt;
&lt;li&gt;配置文件中的旧属性；&lt;/li&gt;
&lt;li&gt;自定义 starter 或公共组件里的旧用法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="第三步确认技术基线满足-40-要求"&gt;&lt;a href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e7%a1%ae%e8%ae%a4%e6%8a%80%e6%9c%af%e5%9f%ba%e7%ba%bf%e6%bb%a1%e8%b6%b3-40-%e8%a6%81%e6%b1%82" class="header-anchor"&gt;&lt;/a&gt;第三步：确认技术基线满足 4.0 要求
&lt;/h3&gt;&lt;p&gt;至少要确认：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK 是否已到 17+；&lt;/li&gt;
&lt;li&gt;Kotlin / GraalVM 是否需要同步升级；&lt;/li&gt;
&lt;li&gt;容器是否使用 Undertow；&lt;/li&gt;
&lt;li&gt;是否依赖老 Servlet 规范。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="第四步核查外围依赖与-spring-framework-7--boot-4-的兼容性"&gt;&lt;a href="#%e7%ac%ac%e5%9b%9b%e6%ad%a5%e6%a0%b8%e6%9f%a5%e5%a4%96%e5%9b%b4%e4%be%9d%e8%b5%96%e4%b8%8e-spring-framework-7--boot-4-%e7%9a%84%e5%85%bc%e5%ae%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;第四步：核查外围依赖与 Spring Framework 7 / Boot 4 的兼容性
&lt;/h3&gt;&lt;p&gt;这一步尤其适合通过依赖树、BOM 对比、第三方 starter 发布说明来完成。&lt;/p&gt;
&lt;h3 id="第五步必要时临时引入-spring-boot-properties-migrator"&gt;&lt;a href="#%e7%ac%ac%e4%ba%94%e6%ad%a5%e5%bf%85%e8%a6%81%e6%97%b6%e4%b8%b4%e6%97%b6%e5%bc%95%e5%85%a5-spring-boot-properties-migrator" class="header-anchor"&gt;&lt;/a&gt;第五步：必要时临时引入 &lt;code&gt;spring-boot-properties-migrator&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;把它当作迁移辅助，而不是长期依赖。&lt;/p&gt;
&lt;h3 id="第六步做完整的回归验证"&gt;&lt;a href="#%e7%ac%ac%e5%85%ad%e6%ad%a5%e5%81%9a%e5%ae%8c%e6%95%b4%e7%9a%84%e5%9b%9e%e5%bd%92%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;第六步：做完整的回归验证
&lt;/h3&gt;&lt;p&gt;需要重点回归的通常包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web 层接口兼容性；&lt;/li&gt;
&lt;li&gt;安全配置；&lt;/li&gt;
&lt;li&gt;配置绑定；&lt;/li&gt;
&lt;li&gt;任务调度 / 异步执行；&lt;/li&gt;
&lt;li&gt;消息队列集成；&lt;/li&gt;
&lt;li&gt;监控与链路追踪；&lt;/li&gt;
&lt;li&gt;原生镜像构建（如果你在用）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="spring-boot-40-值不值得升"&gt;&lt;a href="#spring-boot-40-%e5%80%bc%e4%b8%8d%e5%80%bc%e5%be%97%e5%8d%87" class="header-anchor"&gt;&lt;/a&gt;Spring Boot 4.0 值不值得升
&lt;/h2&gt;&lt;p&gt;如果你的项目还处于以下状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;仍在 Java 11 或更老版本；&lt;/li&gt;
&lt;li&gt;依赖大量历史包袱；&lt;/li&gt;
&lt;li&gt;使用 Undertow 且短期不愿迁移；&lt;/li&gt;
&lt;li&gt;对升级窗口极度敏感；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么短期内不一定适合马上升级到 4.0。&lt;/p&gt;
&lt;p&gt;但如果你的项目已经：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运行在 Java 17+；&lt;/li&gt;
&lt;li&gt;对可观测性、声明式 HTTP 客户端、API 版本治理等能力有明确需求；&lt;/li&gt;
&lt;li&gt;希望尽早跟上 Spring Framework 7 的长期演进路线；&lt;/li&gt;
&lt;li&gt;愿意安排一次成体系的升级与回归；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么 Spring Boot 4.0 是值得认真评估并纳入计划的。&lt;/p&gt;
&lt;p&gt;它的意义不只是“版本更高了”，而是它开始把 Spring Boot 带到一个更现代的工程基线上：更清晰的模块边界、更明确的技术底座、更统一的可观测性与 API 治理能力，以及对新一代 Java 的更完整支持。&lt;/p&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;Spring Boot 4.0 的核心价值可以概括为三点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;它是基于 Spring Framework 7 的代际升级&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;它在工程能力上继续向现代化靠拢&lt;/strong&gt;，尤其是 HTTP Service Clients、API Versioning、OpenTelemetry 与任务装饰能力；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;它的升级成本主要来自底层基线提升与旧能力移除&lt;/strong&gt;，尤其是 Undertow 支持下线、Java/Servlet/Jakarta 体系全面抬升。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对团队来说，最好的做法不是盲目追新，也不是长期停留在旧版本，而是根据自己的技术债水平与业务窗口期，选择一个可控的节奏完成升级。&lt;/p&gt;
&lt;p&gt;如果你的项目准备迈向 Spring Boot 4.0，最稳妥的关键词只有一个：&lt;strong&gt;先把基础清干净，再升级。&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>