跳到主要内容

第 23 章 部署、环境与配置

副题:问题链——一台服务器够不够?不够怎么办?容器为何出现?什么时候其实不需要 K8s?

第 22 章流水线建好了:提交代码 → 自动验证 → 自动部署。但"部署生产"这一步,还藏着最大的问题:服务器从哪来?一台够吗?——本章按一条问题链走:一个问题接一个问题,每个问题都是前一个的代价逼出来的——直到"什么时候其实不需要 K8s"。

本章要建立的认知

  1. 部署形态的每一步演进,都是被前一个形态的代价逼出来的:裸机 → 多实例 → 容器 → 编排;
  2. 环境与配置是部署的一半:代码跑不跑得起来,一半看制品,一半看环境——配置错误会让全站 502;
  3. K8s 不是标配:"业务没到就别上"——用决策模型走一遍,阶段 4 的答案是 Docker 够用。

本章的引导词:"问题链"——一个问题接一个问题,每个问题都是前一个的代价逼出来的;问题链即决策链(方案原话):链上的每一环,都是一个九步决策(要不要多实例/要不要容器/要不要 K8s——第 15 次应用会走完其中最关键的两个)。


23.1 一台服务器够不够:502 事故

第五部分进度(最后一站):21 构建 ✅ 22 CI/CD ✅——本章(23)把"部署"两个字展开:第 22 章说"流水线是剧本,部署是舞台"——本章搭舞台。

阶段 4 的发布流程刚跑顺,一次部署事故把部署问题推到台前。

**事故:配置错误导致全站 502。**新同事部署时,把配置文件里的数据库地址写错了(写成了测试环境的库)——上线后,首页 502(网关错误),全站打不开。回滚(人工确认 + 指回制品),恢复。复盘时发现:配置是"写死在代码里"的——每次部署都要手动改配置文件,改错一个字符就是一次事故(五阶段故事线登记的事故案例:"配置错误导致全站 502"——它是第 23 章环境/配置问题的来源)。

本章与第 4 章的关系(它是"验收标准"的部署侧):第 4 章的约束清单里有"数据不能丢""首页要快"——部署体系是这些约束的"运行保障":多实例(挂了不丢)、回滚(错了能回)、配置管理(配置错能防)——第 4 章的验收标准,第 23 章给了"运维侧"的答案(第 24 章高可用继续给)。

502 事故的三层教训(它是本章的问题链入口):

  • 配置不该是"每次部署时手改"的——配置是"环境的一部分",应该和环境一起管理(23.5);
  • 一台服务器是单点——它挂了全站挂(这次是配置错误,下次是磁盘满/进程崩——第 24 章高可用的主角);
  • 部署这件事,从"把文件放上去"变成了"把环境配好"——第 21 章说"制品不含环境,环境由部署注入"——注入的部分,就是配置。

事故的处理过程也走一遍(它是人工确认的实景):发现 502 → 团队频道炸锅 → 查流水线日志(可观测性的雏形——部署记录里看到"配置改了什么")→ 定位到数据库地址 → 回滚(指回上一个制品)→ 恢复——处置 15 分钟(有制品体系:指回即恢复;没有:重新部署+改配置,一小时起步)——第 21 章"制品可回滚"的价值,在事故里是 15 分钟 vs 1 小时的差别

"单点"里最要单独说的:数据库(它是 24 章高可用的伏笔):应用可以多实例(无状态,23.2 说过),数据库是单点(一台数据库挂了,所有应用都白搭——第 23 章的多实例救不了数据库的单点)——"多实例"的边界:应用层多实例了,数据层还是单点(第 24 章高可用会处理它:主从/备份/故障转移——本章先立认知:多实例 ≠ 高可用,数据层的单点是最后一块短板)。

先回答问题链的第一问:一台服务器够不够?——阶段 4 的答案:不够了。两个信号:流量(第 16 章阶段 2 的 1 万日活已经让单机数据库吃紧,阶段 4 的流量翻了几倍——单台服务器的 CPU/带宽开始见顶);单点风险(一台机器挂了=全站挂——"鸡蛋在一个篮子里",这次 502 只是配置错误,下一场可能是硬件故障)。

"单点风险"的"风险"也量化一下(它是"为什么要多实例"的最硬理由):单机的可用性假设——一台机器年故障率哪怕只有 1%,两台机器同时挂的概率是 1% × 1% = 万分之一(第 17 章"防线是叠的"在硬件层的版本:多实例=故障概率的乘法)——多实例不是"流量需要",是"故障数学"(第 24 章高可用会把这个数学展开成 SLA)。

23.2 不够怎么办:多实例与负载均衡

一台不够,答案自然:两台、三台……多实例。但"加机器"在部署体系里不是"再买一台"这么简单——它是一连串新问题的开始(问题链的第二环):

多实例的三个新问题(它们是"部署为何变复杂"的答案):

  • 流量怎么分:用户请求打到哪台?——需要负载均衡(一个入口,把请求分发给多台机器:轮询/按负载——第 21 章说"所有实例部署同一个制品",负载均衡就是"分发到这些实例");
  • 环境怎么一致:两台机器的 Node 版本、系统库、依赖——第 21 章说过"环境差异让部署不一致"——两台机器要长得一样(不然同样的制品,两台跑出不同行为——"构建一次处处运行"(第 21 章)的部署侧,被环境差异破坏);
  • 发布怎么逐台:新版本上线,不能"两台一起换"(换挂了全站挂)——逐台发布(先换一台,验证没事再换下一台——这就是第 22 章预告过的"部署 N 台"的雏形)。

逐台发布的"验证"靠什么(它是"换下一台"的判定,一句):换了一台之后,看它的健康检查(23.6 的深检查——配置错了当场发现,停住不换下一台)——"逐台"+ "每台验证"= 坏版本最多影响一台(第 22 章说"失败前置"——逐台发布是"失败影响前置":从"全站挂"变成"一台挂")——502 事故在多实例+逐台发布下,会变成"一台 502,其余正常"(第 23.1 事故的"多实例版"结局——这就是部署形态演进的意义:同一场事故,伤害从 100% 降到 1/N)。

负载均衡的两个细节(它是"多实例"的第一课):健康检查(负载均衡定期问"这台机器活着吗"——不健康的实例不分配流量,第 23.6 会看到它在发布中的用法——同一个机制,两个用途);会话亲和(第 13 章说过的 Session——用户登录态在 A 机器上,请求打到 B 机器就"不认识"了——第 13 章说过"多台服务器时要共享凭证表,第 16 章 Redis 的第二个用途"——多实例把第 13/16 章的伏笔正式激活:Session 入 Redis 在阶段 4 不是"将来",是"现在")。

会话亲和的两种解法(它是"激活伏笔"的执行细节,一段):粘性会话(负载均衡把同一个用户的请求固定到同一台机器——简单但机器挂了用户登录态就丢);共享会话(Session 存 Redis——第 16 章说过的"把 Session 表从 PostgreSQL 挪进 Redis,多台服务器共享"——机器挂了会话不丢,另一台机器照样认识你)——阶段 4 选共享会话(Redis 已经在跑(第 16 章),把 Session 挪进去是第 16 章那两行代码的事——第 13 章的"会话共享"承诺,在第 23 章的多实例里兑现(第 16 章说"缓存与会话两个用途"——多实例让"会话"这个用途从"将来"变成"现在")。

多实例的代价(它是问题链往下一环的推力):每台机器的环境都要手动配(装 Node、装依赖、配配置——第 21 章说过的"谁的电脑构建的谁说了算",现在变成"每台机器的手工活")——机器越多,手工配环境的成本越高,出错率越高(502 事故的配置错误,在多实例时代会错 N 次——每台机器都要改配置)。

多实例的形态选择("几台机器怎么排",一句):同构多实例(几台机器跑同一个服务——本章的场景,水平扩展);异构多实例(不同服务不同机器——数据库一台、应用两台——阶段 4 的形态:数据库仍单点(第 24 章高可用的主角),应用多实例)——"多实例"先解决应用层(无状态、好扩展——第 13 章 Session 入 Redis 后应用层无状态了),数据库的多实例是第 24 章的事(有状态、难扩展——第 24 章主从/读写分离)。

问题链走到这里,卡点变了:不是"机器不够",是"环境配不平"——下一环:把环境也打包。

23.3 容器为何出现:环境也打包

容器(Container):把"代码 + 依赖 + 环境"一起打包的东西(第 21 章预告过:Docker 是"连厨房一起打包"的形态)。容器的核心是两个机制(图 23-1):

图23-1 图稿占位
容器镜像分层
镜像分层与复用

镜像(image):容器运行所需的完整环境快照——基础系统 + 运行环境(Node)+ 依赖 + 代码,分层堆叠(镜像分层:基础层(OS+Node)所有应用共享,依赖层按项目,代码层每次构建更新——分层让"换代码"只更新最上面一层,底层复用);容器(container):镜像的运行实例——每台机器上跑起来的"那个进程"。

镜像分层的两个直接收益(它是"分层"设计的理由):构建快(代码变了只重新构建最上层——第 21 章"构建时长是流水线的瓶颈",分层把"换代码"的构建从"全部重来"变成"只更新一层");存储省(两台机器跑同一个镜像的底层——共享,不重复存)——镜像分层是"复用"思想在部署侧的形态(第 6 章表结构复用、第 12 章组件复用——同一个思想)。

容器与虚拟机的差别(一句,它是"为什么容器更轻"的答案):虚拟机(VM)模拟整台电脑(每个 VM 一个操作系统——重,启动分钟级);容器共享宿主机的操作系统(只隔离进程——轻,启动秒级)——容器是"进程级隔离",VM 是"系统级隔离"——轻重之差,是部署速度之差(秒级启动=发布/回滚都快)。

容器化的"收益量化"(它是 Docker 决策的落地验证,一段):容器化之后,部署的时间账——改一行代码 → 构建镜像(分层:只重建最上层,分钟级)→ 推到机器 → 重启容器(秒级)——和之前比:不再有"每台机器手工配环境"的几十分钟(镜像自带环境)——部署从"小时级手工活"变成"分钟级自动化"(流水线部署阶段,配了容器之后才真正"一键"——第 22 章说"部署是第 ⑧ 步",容器让第 ⑧ 步真正自动化)。

容器解决什么问题(它是问题链第三环的答案):环境不一致——第 21 章说"制品不含环境"——容器让环境也进制品(镜像=制品+环境):"构建一次、处处运行"升级为"打包一次、处处运行"——第 21 章说"处处运行指代码行为一致"——容器让"环境行为"也一致(两台机器跑同一个镜像,就是两个一模一样的运行环境——23.2 的"环境怎么一致"问题,被镜像解决)。

容器与开发环境的关系(它是"在我机器上是好的"的终极解,一段):第 21 章说"本地开发仍跑源码,交付用制品"——容器让本地也可以"跑镜像":开发环境起一个容器(镜像里的环境)——"我机器上"和"生产"的差别,被镜像抹平(本地容器=生产镜像的同一个环境)——第 21 章说"在我机器上是好的应该绝迹"——容器是让这句话绝迹的最后一块砖(本地、测试、生产,同一个镜像环境——第 15 章"彩排"升级成"同环境预演",第 21 章"复现"升级成"环境也复现")。

容器与第 16 章缓存的类比(它是"打包"思想的跨章呼应,一句):第 16 章说"缓存把热数据放近处"(数据复用)——镜像分层把"环境"复用(基础层共享——同一套 Node 环境,N 个应用共用,不用每个应用装一遍)——"复用"是本书的元思想(第 6 章表结构、第 12 章组件、第 16 章缓存、第 23 章镜像——同一个词,四种形态)。

要不要用 Docker——九步的一部分(它是本章决策链的第一站):① 目标:多实例环境一致、部署可复现;② 约束:阶段 4 团队 8 人、机器 2-3 台、发布频率周更;③ 问题:要不要容器化?④ 候选:A 继续手工配环境;B 容器化(Docker);⑤ 维度:环境一致性、部署速度、学习成本;⑥ 打分:A 在 2-3 台机器时已经吃力(每台手工配、出错率随机器数涨——23.2 的代价);B 的环境打包一次解决(镜像=环境,机器再多也一样);⑦ 选择B(Docker)⑧ 收益:环境一致(镜像)、部署可复现(同一个镜像到处跑)、开发/测试/生产同环境(第 15 章"测试环境是生产的彩排"——容器让彩排和生产用同一个镜像,彩排升级成"同环境预演");⑨ 代价:容器技术要学(8 人团队人人会一点)、镜像要维护(构建镜像的时间与存储);演进条件:机器再多(环境手工真的管不过来)、或部署频率再涨(镜像的好处更明显)时——Docker 的决策记录写进第 5 章那张表(第 15 次填写的第一个半场:要不要容器化;第二个半场:要不要 K8s——23.4)。

23.4 容器多了怎么办:编排与"不上 K8s"

容器解决了环境问题,但容器自己带来了新问题(问题链第四环):容器多了怎么管?——两台机器上跑着 N 个容器,谁负责"这个容器挂了自己重启"?谁负责"新版本容器换旧版本"?谁负责"流量分给哪个容器"?——这些"容器的管理"就是编排(orchestration):K8s(Kubernetes)一类的容器编排平台。

何时需要编排(它是"容器多了"的量化):容器数量少(十几个以内)、机器少(几台)、手动管理还管得过来——不需要;容器几十上百个、机器几十台、要自动伸缩、要自愈——需要。判断标准一句话:手动管容器的成本 > 学编排的成本时——编排是"容器管理"的复杂度预算(第 4 章)。

编排的"收益清单"(它让"需要时再上"有了具体所指,一句):自愈(容器挂了自动重启)、伸缩(流量大了自动加容器)、调度(容器自动分配到合适的机器)、滚动更新(新版本容器自动逐批替换)——四项收益,每一项都是"容器多了"之后的痛点:容器少时手动重启 30 秒,容器多时"哪个挂了"都找不到——编排的收益跟着容器数量涨(这正是"业务没到就别上"的依据:收益是规模函数)。

编排的学习曲线(它是"成本实打实"的展开,一段):K8s 的陡峭是出了名的——概念多(Pod/Deployment/Service/Ingress……每个都是新抽象,第 1 章说"名词地图"的典型——K8s 就是名词密度最高的技术之一)、运维重(集群本身要升级/监控/排障——K8s 集群挂了比应用挂了更惨)、排障难(容器网络/存储的问题层层叠叠)——8 人团队学 K8s 的 3 个月,够发 10 次版(第 23.4 打分时的量化)——"成本实打实"不是感觉,是这三个月的时间账(第 4 章复杂度预算:时间也是预算)。

★ 要不要 K8s——决策模型完整示例(它是本章决策链的核心,也是"业务没到就别上"的正面教材):团队认真评估过 K8s——它是问题链的最后一问,也是九步模型第 15 次应用的重头戏(前面 Docker 的九步是热身,这个才是完整示例):① 目标:容器管理自动化(自愈/伸缩/调度);② 约束:阶段 4——机器 2-3 台、容器十几个、发布周更、团队 8 人没有专职运维③ 问题:要不要上 K8s?④ 候选:A 上 K8s;B 不上(Docker + 轻量管理);⑤ 维度:收益(自动化程度)、成本(学习曲线——K8s 是出了名的陡;运维负担——K8s 集群本身要维护)、时机(当前规模匹配吗);⑥ 打分:K8s 的收益(自愈/伸缩)在"2-3 台机器、十几个容器"面前全部用不上(自愈:手动重启一个容器 30 秒;伸缩:流量还没大到要自动伸缩);K8s 的成本是实打实的(团队 8 人没人专职运维,学 K8s 的时间够发十次版);⑦ 选择B(不上 K8s)——Docker 够用,容器用轻量方式管理(脚本/简单工具);⑧ 收益:省下 K8s 的学习与运维成本,团队精力留在业务;⑨ 演进条件机器到十几台、容器到几十个、或专职运维加入——再评估 K8s(决策记录写清楚,将来不用重新吵)。

这个决策的"记录"意义(它是第 5 章决策记录的第 15 次填写,也是"正面教材"的完整形态):决策记录 = 将来质疑时的答案——"为什么不上 K8s?"(第 26 章架构演进时大概率会再问)——记录里存着当时的理由(规模/成本/时机——第 20 章说"决策记录是选择的历史")——"不上 K8s"和"上 K8s"一样,都是决策(第 13 章"不上 CDN"、第 14 章"不上 Redis"——本书的"不上"系列,每一个都是决策记录里的演进条件换来的)。

"业务没到就别上"(它是这个决策的总结,也是全书的决策观):K8s 是好技术,但好技术 × 用不上的规模 = 负债(第 20 章技术债:学习成本 + 运维成本都是利息)——决策模型的用处就在这:挡住"名声最好的方案"(第 13 章说过同一句话——JWT 名声大但阶段 1 用不上;K8s 名声大但阶段 4 用不上——同一个决策习惯,从认证到部署)。

"不上 K8s"之后的日常(它是决策的落地,一段):容器用轻量方式管——发布脚本(流水线的部署阶段:拉新镜像→停旧容器→起新容器——脚本做,第 22 章说"部署是流水线的第 ⑧ 步",这一步的"执行者"就是脚本);容器监控靠"人+日志"(第 24/25 章:容器挂了日志看得到、人工重启 30 秒——K8s 的自愈是"机器 30 秒",我们是"人 30 秒",在 2-3 台机器的规模,差别不大——这就是"收益用不上"的量化);决策记录写清楚演进条件(十几台/几十容器/专职运维——将来触发,不用重新吵)。

与第 26 章的衔接(它是演进条件的"将来"):第 26 章架构演进时,如果规模到了(十几台/几十容器),K8s 的决策记录会被翻出来——"当时为什么不上、现在条件到了吗"——决策记录的"演进条件",就是第 26 章重新评估的触发点(第 5 章模型说"约束变化时重选"——演进条件是约束变化的显式标记)。

23.5 多环境与配置管理:502 的根源

回到 502 事故(它是本章的环境问题主线):配置写死在代码里——为什么这是错的?因为配置是"环境的一部分",而环境是分层的(第 15 章:本地/测试/生产):

配置的三个层级(图 23-2):

先交代"为什么配置写死是错的"的机制(它是 502 事故的根):配置写死在代码里 = 换环境要改代码 = 每个环境一个代码版本(第 21 章说"配置进制品=每个环境一个制品=复现破产"——代码写死配置是同一件事的代码版)——"配置与代码分离"的本质:让代码只有一个版本,差异全在配置(第 22 章"同一个制品到处跑"的配置侧前提)。

  • 代码配置(写死在代码里):最错——换环境要改代码(502 事故的根源);
  • 环境变量(部署时注入):推荐——第 21 章说"配置由部署时注入"——数据库地址/密钥/域名这些"环境相关"的配置,放在环境变量里,同一个制品,不同环境注入不同值
  • 配置中心(集中管理配置的服务):配置多了以后(十几个服务各自的配置)——集中管理+版本化+热更新——阶段 4 用环境变量就够(配置中心是"配置多了"时的演进,第 4 章复杂度预算)。
图23-2 图稿占位
多环境配置
环境差异与配置中心

12-Factor 的配置要点(一句,它是"配置管理"的行业共识):配置与代码分离(配置随环境变,代码不随环境变——第 21 章"制品不含环境"的完整表述)——判断标准:同一份代码,能不能不经修改部署到任何环境(能=配置管理对了;不能=配置又混进代码了)。

12-Factor 还有两个与本章相关的要点(一句带过,它们在第 25 章有主场):日志是事件流(应用把日志打到标准输出,由平台收集——第 25 章日志体系的部署侧);进程无状态(进程不存本地状态——第 13 章 Session 入 Redis 就是"进程无状态"的实践)——12-Factor 不是口号,是本章每件事的行业共识版(配置分离/无状态/日志流——前面章节的实践,12-Factor 给它们起了个总名)。

配置管理的配套纪律(它是 502 事故的直接修复):

  • 密钥不进代码/仓库(第 13 章说过"密钥是命门",第 19 章展开过——环境变量或密钥服务);
  • debug 不上生产(第 19 章说过——调试信息/堆栈是攻击者的地图,也是 502 排查时"看得见错误"的反面:生产环境的错误详情只进日志(第 25 章),响应给第 7 章通用格式);
  • 配置变更要审批(改配置=改生产行为——走第 20 章的评审轨道,配置和代码一样要被看)。

502 事故修复的收尾(它是本章配置管理部分的闭环):修复后,小明做了三件事——数据库地址移进环境变量(代码里不再有环境相关的字);部署清单加"配置核对"一步(人工确认三问的补充:这次部署的环境变量对吗);配置变更走 PR(第 20 章轨道:改配置=改代码一样的流程)——事故的每一层教训,都变成了机制的一环(第 18 章"事故=漏掉的承诺"——配置管理的承诺,从 502 事故里补上了)。

配置与安全的关系(它是第 19 章的收束,一句):配置是攻击者的目标之一——数据库地址/密钥泄露 = 第 19 章的攻击面(配置仓库被拖=密钥全丢——第 21 章说过的供应链)——配置管理的安全纪律:密钥用环境变量/密钥服务(第 13 章的"密钥是命门",第 19 章的安全章展开)、配置仓库访问控制(谁能看配置——第 20 章协作轨道 + 第 19 章最小权限)——配置是"环境的一半",也是"安全的一半"(第 19 章的信任模型,配置环节的形态)。

配置的三类内容("配置里都有什么",一句):连接类(数据库地址/缓存地址——502 事故的主角)、密钥类(支付密钥/签名密钥——第 13 章"密钥是命门",第 19 章安全章展开)、行为类(功能开关/限流阈值——第 16 章限流的配置)——三类配置,两种变更频率(连接/密钥低频,行为类可以高频——功能开关就是"不发布也能改行为"的配置)——"功能开关"是配置管理的进阶(不用发布就能开关功能——第 26 章灰度发布会用到它)。

"改配置"与"改代码"的边界(它是"配置也要审批"的补充,一句):配置变更的风险分级——连接/密钥类配置错了=502(高危,必须审批+测试);行为类配置错了=功能异常(中危,可快速回改)——"配置和代码一样要被看"不是说所有配置同等待遇——按风险分级,高风险走评审,低风险走快速通道(第 19 章"按风险排序"的同一逻辑)。

23.6 发布与回滚:把"上线"变成"切换"

部署体系的最后一块:发布方式——第 22 章预告过蓝绿/金丝雀,现在展开。发布的核心转变:从"替换"变成"切换"

蓝绿部署:两套环境(蓝/绿)——新版本部署到绿环境,验证通过后把流量切到绿(蓝留作回滚)——切换是瞬间的、可逆的(切回去=指回蓝);金丝雀发布:先放 1% 流量到新版本,观察(监控——没异常再逐步放量到全量——"小步放量"让坏版本只影响 1% 用户滚动发布(23.2 说过的逐台):一台一台换——多实例时代的默认方式。

发布方式与人工确认清单的衔接(它是"确认能回"的展开):人工确认三问(第 22 章)——"回滚方案是什么"这一问的答案,就是本章的发布方式(蓝绿的回答:切回蓝;滚动的回答:逐台换回——确认"能回",确认的就是"发布方式支持回")——清单问的每一问,本章都实现了一部分

回滚机制(它是"能回滚才能发"的完整实现——第 22 章说"敢部署的前提是能回滚"):回滚 = 把流量切回上一个制品(第 21 章"指回制品")——蓝绿的回滚是"切回蓝"(秒级),滚动的回滚是"逐台换回旧版本"(分钟级),金丝雀的回滚是"把 1% 撤回来"(最快)——发布方式的差别,就是回滚速度的差别——选发布方式时,先问"出事了多久能回"(第 22 章人工确认的三问之一)。

回滚的自动化(它是"回滚进流水线"的形态,一句):回滚也应该是一条流水线动作——部署是自动的(第 22 章),回滚也该自动(一个按钮/一条命令:指回上一个制品+切流量——不是深夜手忙脚乱地敲命令)——"能回滚"的完整定义:一条自动化的回滚路径(第 18 章事故处置的"先回滚"——自动化的回滚让"先"字更快——应急响应的雏形:出事了先按回滚按钮,再慢慢查)。

迁移与发布的配合(第 21 章说过的"先迁移再部署"):数据库迁移是发布流程的一部分——发布单(人工确认的清单)里,迁移是明确的一步(先迁移、再部署新制品、回滚时"数据库只能前进"——第 21 章说过的回滚不对称)。

回滚与数据的关系(它是"不对称"的完整版,一句):代码可以回滚,数据不能——新代码写的数据,旧代码可能读不了(第 6 章"表结构变了旧代码不认")——"回滚"的完整含义:代码指回旧制品 + 数据要兼容旧代码(迁移设计成"向前兼容",第 21 章说过——发布与回滚的每一环,都绑着数据库(第 6 章"改表贵"在发布侧的形态:迁移是发布里最不能出错的步骤)。

健康检查(它是发布验证的哨兵,第 21 章的 /health 在这里上岗):发布后自动检查(新实例的 /health 通了才接流量——负载均衡的"健康检查":不健康的实例不分配流量)——"接流量"以"健康"为前提——发布不是"放上去",是"健康了才放上去"。

健康检查的"深度"("活着"和"能用"是两回事,一段):浅检查(进程在吗——/health 返回 200 就完事)和深检查(依赖在吗——/health 里查数据库连接/缓存连接——第 21 章说"/health 返回'服务活着'+关键依赖状态")——502 事故的教训:配置错了,进程照样"活着"(浅检查通过),但数据库连不上(深检查发现)——健康检查的深度,决定它能发现什么(浅检查防"进程挂了",深检查防"配置错了"——第 23.1 的事故,深检查能在发布时就拦住)。

发布与用户感知("切换"对用户意味着什么,一句):蓝绿/金丝雀/滚动——用户无感知(切换瞬间完成或逐步放量,用户看到的是"一直可用")——"无感知发布"是部署体系给用户的礼物:第 22 章的"发布冻结"消失(随时可发),第 23 章的"切换发布"让"发"对用户不可见——从"发布日求神拜佛"(第 22 章)到"无感知发布"(本章),发布从团队的负担变成团队的日常

发布方式的选型(它是"先问出事多久能回"的配套,一句):选哪种发布方式,看两个维度——回滚速度(出事了多久恢复:蓝绿秒级/滚动分钟级)和环境成本(蓝绿要两套环境——机器翻倍,阶段 4 的 2-3 台机器玩不起蓝绿)——阶段 4 的选择:滚动发布为主(多实例天然支持逐台换),金丝雀作为"高风险发布"的选项(数据库迁移/大版本——先 1% 试试,第 26 章灰度会深化)——发布方式也是九步决策(回滚速度 vs 环境成本,第 5 章模型第 15 次的配套)。

发布风险的分级("什么发布用金丝雀"的补充,一句):常规发布(小功能/修复——滚动即可);高风险发布(数据库迁移/大版本/核心链路改动——金丝雀先 1%);紧急修复(线上事故——快速直接(回滚优先于完美发布)——发布方式跟着风险走,和 19.8 安全投入同一个排序逻辑(按风险排,不按热度排)。

发布与监控的衔接(它是"发布完成不是终点"的完整版,一句):发布后的验证靠监控——金丝雀的"观察"靠的是第 25 章的指标(错误率/延迟),滚动发布的"换下一台"的判定也是——发布是"放上去+看着它"(第 22 章说"部署完成是运行期的起点"——看着它的工具,是第 25 章的监控——本章发布、第 25 章看着)。

回滚的"演练"(它把"能回滚"从承诺变成能力,一段):回滚要演练过(第 18 章"测试是承诺"——回滚也是承诺:"出事了能回"不能靠嘴说)——定期做一次"演练发布"(测试环境完整走一遍:发一个新版→故意弄挂→回滚——验证回滚真的能回)——"能回滚"和"测试过能回滚"是两件事(第 15 章说"约束是机制在才算数"——回滚机制要证明过才算数)。

演练与第 24 章的衔接(它是"演练"的完整意义,一句):演练不只是验证回滚,是验证"出事的处置流程"——第 24 章高可用会有正式的"故障演练"(主动制造故障,验证系统扛得住)——本章的"演练发布"是它的雏形(先练回滚,再练故障——第 24 章会看到"演练"成为高可用的日常)。

23.7 部署形态的演进全景

把问题链收成一张全景图(图 23-3)——它是本章的"总览":前面每节是问题链的一环,这张图把环串成链:

裸机(一台服务器,手动部署)
→ 代价:单点、手动、环境不一致
多实例(几台机器 + 负载均衡)
→ 代价:环境每台手工配,机器越多越难
容器(Docker:环境也打包)
→ 代价:容器多了手动管不过来
编排(K8s:容器自动管理)
→ 代价:学习与运维成本陡——"业务没到就别上"
图23-3 图稿占位
部署形态演进
裸机→VM→容器→编排

图 23-3 与第 3 章 Web 系统总架构的关系(它是"架构图"的部署侧,一句):第 3 章的 Web 系统总架构画的是"系统由什么组成"(浏览器/网络/服务端/数据层)——图 23-3 画的是"系统怎么被部署"(部署形态的演进)——同一套系统,两张图:一张看组成,一张看放置(第 1 章地图的"技术线"和"工程线",在部署章各有一张图)。

每一步都是被前一步的代价逼出来的(第 2 章演进循环的部署版):裸机的单点逼出多实例,多实例的环境手工逼出容器,容器的管理问题逼出编排——但链条不是必经:案例停在"容器"(Docker 够用),没有走到"编排"——"原方案无法承受"才往前走,能承受就停(第 4 章复杂度预算的部署版:"够用"是标准,不是"最新")。

"能承受就停"再展开一句(它是"部署形态"决策的收尾):停在"容器"不是遗憾,是预算管理——第 4 章说"复杂度预算花在刀刃上"——K8s 的复杂度预算花在"容器多到管不过来"的那天(演进条件触发),不是今天——本书的"不上"系列(Redis 不上/CDN 不上/K8s 不上)都是同一个逻辑:今天的预算买今天的问题,将来的问题用将来的预算(第 26 章架构演进时会看到"上"的时机)。

部署形态与团队规模的关系(它是"流程是团队的函数"(第 20 章)的部署版):1 人团队(阶段 1-3):裸机+手动部署够用(第 4 章);8 人团队(阶段 4):多实例+容器(本章的选择);几十人团队(阶段 5):才走到编排/云原生(第 26 章架构演进会看到)——部署形态跟着团队和规模长,不跟着"最新技术"长

阶段 4 的时间锚点(五阶段故事线的登记):本章是阶段 4 的收官章——团队 8 人、机器 2-3 台、发布周更(第 20 章说过的节奏)、502 事故成为"环境/配置问题"的案例来源——第五部分(21-23)把"从代码到用户"的链路走完,阶段 4 的"部署从手搓到流程"(五阶段故事线原话)在这里正式完成。

本章与第 22 章流水线的衔接(它是"第 ⑧ 步"的展开,一句):第 22 章说"部署是流水线的第 ⑧ 步"——本章就是第 ⑧ 步的全部内容:部署到哪(23.2 多实例)、怎么部署(23.3 容器)、配置怎么注入(23.5)、发布怎么切(23.6)——流水线的"部署"两个字,在第 23 章展开成一整套机制(第 22 章说"流水线是剧本,部署是舞台"——本章把舞台搭起来了)。

23.8 本章对应表

业务诉求技术选择为什么代价/取舍
一台服务器不够(流量/单点)多实例 + 负载均衡单点挂了全站挂,流量单机见顶环境每台手工配(问题链下一环)
多实例环境不一致容器(Docker)环境也打包:镜像=制品+环境容器技术要学、镜像要维护
容器多了管不过来编排(K8s 等)自愈/伸缩/调度自动化学习运维成本陡
K8s 要不要上九步决策(★不上:Docker 够用)收益在 2-3 台机器前用不上,成本实打实演进条件=十几台/几十容器/专职运维
配置错误全站 502配置与代码分离(环境变量)配置是环境的一部分,部署时注入密钥管理要跟上(19 章)
发布不能全站一起换蓝绿/金丝雀/滚动切换优于替换:可逆、可观察环境/流量要支持切换
出事了能回回滚=指回制品第 21 章制品版本是回滚坐标迁移"只能前进"(回滚不对称)

每一行都在本章正文里有完整论证:502 在 23.1,多实例在 23.2,容器在 23.3,K8s 在 23.4,配置在 23.5,发布在 23.6,全景在 23.7。先看"业务诉求"列——这一章的每一行,都是从问题链的一环一环里长出来的

本章对两类读者的收益(与前几章同款):前端读者——静态资源部署到 CDN(第 11/16 章)和多环境配置(前端域名/接口地址),是前端部署的日常;后端读者——容器/配置/发布回滚,是后端部署的主场;两类读者共同的收获:部署第一次有了完整的形态地图(裸机→多实例→容器→编排)和决策链(每一步都是九步)——"把制品放上服务器"从手工活变成体系(阶段 4 的"部署从手搓到流程",到这里走完)。

本章与第 15 章环境分层的关系(它是"环境"主题的收束):第 15 章说"本地/测试/生产三层环境"——第 23 章把三层环境"实现"了:环境变量(每层注入不同配置)、容器(每层跑同一个镜像)、流水线("环境之间的搬运工")——第 15 章画的三层,第 23 章让它们真正分开又一致(配置分开、镜像一致——"分开"的是环境,"一致"的是制品和镜像)——"环境分层"从第 15 章的约定,变成第 23 章的机制

本章小结

部署速查(第五部分回指本章时翻回这里):

  1. 问题链七问:一台够不够→多实例→环境为何难→容器→容器多了→编排→K8s 要不要——每步被前一步代价逼出;

  2. 容器=环境也打包(镜像分层:基础层共享/依赖层/代码层)——"构建一次"升级为"打包一次";

  3. K8s 决策(★九步完整示例):不上——收益用不上、成本实打实;演进条件=十几台/几十容器/专职运维;

  4. 配置三层次:代码写死(错)→ 环境变量(对)→ 配置中心(多了再上)——配置与代码分离;

  5. 发布三方式:蓝绿(切换)/金丝雀(小步放量)/滚动(逐台)——发布方式的差别=回滚速度的差别;

  6. 回滚=指回制品;迁移"只能前进";健康检查=接流量的前提。 (六条速查对应本章结构:1 是"问题链",2-3 是"容器与决策",4 是"配置",5-6 是"发布与回滚"——第 24 章高可用回指本章时重点翻 2(多实例)和 5(发布回滚);第 26 章架构演进回指时重点翻 3(K8s 决策记录)。)


  • 部署形态的每一步,都是被前一步的代价逼出来的:裸机 → 多实例 → 容器 → 编排——但链条不是必经,能承受就停
  • 环境与配置是部署的一半:配置错误会让全站 502——配置与代码分离,部署时注入;
  • K8s 不是标配:"业务没到就别上"——九步决策挡住"名声最好的方案"(第 13 章同款习惯);
  • 发布从"替换"变成"切换":蓝绿/金丝雀/滚动——发布方式的差别,就是回滚速度的差别

下一章

部署体系建好了:多实例、容器、配置管理、发布回滚——制品被稳稳地放上了服务器,业务在跑。但一个新的问题浮出水面:"在跑"不等于"不会挂"——服务器硬件会坏、进程会崩、流量会突然暴涨。

第 24 章,高可用:业务不能挂的保障——多实例的真正意义、故障转移、容量规划——部署上线了,业务不能挂。

第五部分(第 21-23 章)收官:构建(做出制品)→ CI/CD(自动验证与部署编排)→ 部署(放上环境)——发布三段式走完:从代码提交到用户可见的完整链路闭合(第 1 章工程线的主干道,到这里通了)——第六部分开始(24-27):运行期的事——高可用、可观测性、架构演进、业务迭代——"能一直跑的产品"(第 1 章工程线的后半段)。