第 23 章 部署、环境与配置
副题:问题链——一台服务器够不够?不够怎么办?容器为何出现?什么时候其实不需要 K8s?
流水线建好了:提交代码、自动验证、自动部署,一气呵成。但整条链上还剩最后一步藏着最大的问号——"部署生产"这四个字,落实下去就是两个问题:**服务器从哪来?一台够吗?**本章沿着一条问题链往前走:一个问题接一个问题,每个问题都是前一个形态的代价逼出来的——一直走到最后一个问题:什么时候其实不需要 K8s。
读完本章,要建立三条认知。
- 部署形态的每一步演进,都是被前一个形态的代价逼出来的:裸机、多实例、容器、编排,一级一级,各有各的不得已;
- 环境与配置是部署的一半:代码跑不跑得起来,一半看制品,一半看环境——配置错一个值,全站 502;
- K8s 不是标配:"业务没到就别上"——这个结论不是感觉,是用决策模型一步步推出来的,阶段 4 的答案是 Docker 够用。
本章的引导词是"问题链"。链条的样子是这样的:一个问题接一个问题,每个问题都是前一个的代价逼出来的——所以问题链本身就是决策链,链上的每一环,都该正经理一遍决策流程:要不要多实例、要不要容器、要不要编排,本章会走完其中最关键的两个。
23.1 一台服务器够不够:502 事故
发布三段式走到最后一站:构建、流水线都建好了,本章把"部署"两个字展开。打个比方:流水线是剧本,部署是舞台。
舞台刚搭好,就被一次事故推到了台前。新同事部署时,把配置文件里的数据库地址写错了——写成了测试环境的库。上线后,首页 502,全站打不开。回滚、恢复、复盘,复盘挖出的根源是:配置写死在代码里,每次部署都要手动改配置文件,改错一个字符,就是一次事故。故事线的事故档案里,这起登记为"配置错误导致全站 502"——它是本章环境问题的来源,也是问题链的入口。
这场事故留下了三层教训,正好一层一个问题。第一层,配置不该是"每次部署时手改"的——配置是环境的一部分,应该和环境一起管理。第二层,一台服务器是单点——它挂了全站挂,这次是配置错误,下次可能是磁盘满、进程崩,硬件总有出场的机会。第三层,也是认知上的转弯:部署这件事,已经从"把文件放上去"变成了"把环境配好"。制品不含环境、环境由部署注入——注入的那部分,就是配置。
处置过程也走一遍,这是人工确认的实景。发现 502,团队频道炸锅;查流水线日志——部署记录里能看到这次改了什么,这是可观测性的雏形;定位到数据库地址;回滚,指回上一个制品;恢复。全程 15 分钟。这个速度是制品体系给的:指回即恢复。没有这个体系,就得重新部署加改配置,一小时起步。同一个事故,有制品和没制品,差的是 15 分钟和一个小时的区别——"制品可回滚"的价值,在事故里就是这么计算的。
三层教训里,单点这一条要单独展开,因为单点里最要命的是数据库。应用可以多实例——它是无状态的,一个实例挂了,另一个接着服务,用户无感。数据库不行:一台数据库挂了,所有应用实例都白搭,本章的多实例救不了数据库的单点。所以多实例有一个必须说清的边界:应用层多实例了,数据层还是单点。这块最后的短板,留到高可用那一章处理——主从、备份、故障转移。本章先立住认知:多实例不等于高可用。
现在回答问题链的第一问:一台服务器,够吗?阶段 4 的答案:不够了。两个信号。一是流量:一万日活的时候,单机数据库就已经吃紧,阶段 4 流量翻了几倍,单台服务器的 CPU 和带宽开始见顶。二是单点风险:一台机器挂了等于全站挂——鸡蛋在一个篮子里,这次 502 只是配置错误,下一场可能是硬件故障。
单点风险的"风险"两个字,值得用数学夯实一下,因为这是"为什么要多实例"最硬的理由。假设一台机器的年故障率是 1%——已经是很乐观的假设——那么两台机器同时挂的概率,是 1% 乘 1%,万分之一。多实例的精髓就在这里:它把故障概率从加法变乘法。防线是叠出来的,这个原则在硬件层同样成立——多实例不是"流量需要",是"故障数学"。这个数学后面还会展开,展开成 SLA 的语言。
23.2 不够怎么办:多实例与负载均衡
一台不够,答案自然浮出水面:两台、三台,多实例。但"加机器"在部署体系里,绝不是"再买一台"这么简单——它是一连串新问题的开始,问题链的第二环。
新问题有三个,正好说明部署为什么从这里开始变复杂。流量怎么分:用户请求打到哪台?需要一个入口,把请求分发给多台机器——这就是负载均衡,分发算法按轮询或按负载。制品体系说过,所有实例部署同一个制品,负载均衡做的事,就是把流量分发到这些相同的实例上。环境怎么一致:两台机器的 Node 版本、系统库、依赖,必须长得一样——环境差异一直是部署不一致的根源,同样的制品,两台机器跑出不同行为,"构建一次、处处运行"就破在部署侧。发布怎么逐台:新版本上线,绝不能两台一起换——换挂了全站挂。正确做法是逐台发布:先换一台,验证没事,再换下一台。
"验证没事"靠什么判定?靠健康检查。换了一台之后,看它的深检查(健康检查还分深浅,23.6 展开):配置错了当场暴露,立刻停住,不换下一台。逐台发布加上每台验证,效果是:坏版本最多影响一台。失败要前置——失败的影响也要前置:从"全站挂"前置成"一台挂"。回到开场那场 502 事故:在多实例加逐台发布的世界观里,它会变成"一台 502,其余正常"。同一场事故,伤害从 100% 降到 1/N——这就是部署形态演进的意义。
负载均衡本身有两个细节,是多实例的第一课。第一个,健康检查:负载均衡定期问每台机器"活着吗",不健康的实例不分配流量。注意,这和逐台发布里那台"换完先看它健不健康"是同一个机制、两个用途——发布时它是验收员,运行时它是哨兵。第二个,会话亲和。用户登录态存在 A 机器的内存里,请求打到 B 机器,B 就不认识这个用户了。所以当有多台服务器时,凭证要共享,缓存里那个"第二个用途"就是为这一刻准备的。多实例把它从"将来"正式激活,变成"现在"。
会话问题有两种解法。一种是粘性会话:负载均衡把同一用户的请求固定打到同一台机器。简单,但机器一挂,这台机器上的用户登录态全丢。另一种是共享会话:Session 存进 Redis。之前已经做过把 Session 表挪进缓存的改造,机器挂了会话不丢,另一台机器照样认识你。阶段 4 选共享会话——理由很实际:缓存已经在跑,把会话挪进去是几行代码的事。当年"多台服务器要共享凭证"的承诺,在多实例里兑现;缓存"缓存加会话两个用途"里的第二个用途,从"将来"变成"现在"。
多实例也有代价,而这笔代价正是问题链往下一环的推力:每台机器的环境都要手动配——装 Node、装依赖、改配置。机器越多,手工配环境的成本越高,出错率越高。开场那场 502 的配置错误,在多实例时代会错 N 次,因为每台机器都要改一遍配置。问题到这里卡住了:不是机器不够,是环境配不平。
形态上再交代一句。多实例有两种排法。同构多实例,几台机器跑同一个服务——水平扩展,本章的场景。异构多实例,不同服务不同机器——数据库一台、应用两台,这是阶段 4 的实际形态:应用层多实例,数据库仍单点。排序有讲究:先解决应用层——它是无状态的,好扩展,会话入缓存之后更是如此;数据库的多实例是更难的事——有状态、难扩展,留给高可用那一章,那里有主从和读写分离的完整答案。
问题链走到这里,卡点已经很清楚了:不是"机器不够",是"环境配不平"。下一环的答法只有一句话——把环境也打包。
23.3 容器为何出现:环境也打包
容器,就是把代码、依赖、环境一起打包的东西——按第 21 章的菜谱类比,这是连厨房一起打包。它的核心是两个机制:
镜像,是容器运行所需的完整环境快照:基础系统、运行环境、依赖、代码,全部打包,而且分层堆叠。基础层——操作系统加 Node——所有应用共享;依赖层按项目分;代码层每次构建更新。容器,是镜像的运行实例,每台机器上跑起来的那个进程。分层这个设计有两个直接收益。一是构建快:代码变了,只重新构建最上层——之前说过构建时长是流水线的瓶颈,分层把"换代码"的构建从全部重来变成只更新一层。二是存储省:两台机器跑同一个镜像,底层共享,不重复存。
容器和虚拟机经常被放在一起比,一句话说清差别。虚拟机模拟整台电脑,每个虚拟机一个操作系统,重,启动要分钟级。容器共享宿主机的操作系统,只隔离进程,轻,启动秒级。轻重之差,就是部署速度之差——秒级启动,发布和回滚都快。
收益还可以量化成一笔时间账。容器化之后,改一行代码:构建镜像——分层,只重建最上层,分钟级;推到机器;重启容器——秒级。之前呢?每台机器手工配环境,几十分钟起步,机器越多越夸张。部署从"小时级手工活"变成"分钟级自动化"——流水线的部署阶段,正是配了容器之后才真正配得上一键两个字。
容器到底解决了什么问题?回到问题链:第二环的卡点是"环境配不平"。制品体系立过一条规矩——制品不含环境,环境由部署注入。容器做的事,是让环境也进制品:镜像等于制品加环境。"构建一次、处处运行"由此升级成"打包一次、处处运行"——之前说"处处运行"指代码行为一致,容器让环境行为也一致:两台机器跑同一个镜像,就是两个一模一样的运行环境。上一环"环境怎么一致"的问题,被镜像解决了。
容器和开发环境的关系也值得说透,它是"在我机器上是好的"的终极解。之前立过边界:本地开发跑源码,交付用制品。容器让本地也可以跑镜像——开发环境起一个容器,用的就是镜像里的环境。本地容器和生产跑的是同一个环境,"我机器上"和"生产"的差别被抹平。"在我机器上是好的应该绝迹"这句话,容器是让它绝迹的最后一块砖:本地、测试、生产,同一个镜像环境——彩排升级成同环境预演,复现升级成环境也复现。
再往深看一层,镜像分层呼应了这本书的一个元思想:复用。表结构复用、组件复用、缓存把热数据放近处复用——镜像分层做的是同一件事:基础层共享,同一套 Node 环境,N 个应用共用,不用每个应用装一遍。同一个词,四种形态,复用贯穿始终。
要不要用 Docker?这是本章决策链的第一站,理一遍决策流程。① 目标:多实例环境一致、部署可复现。② 约束:阶段 4 团队 8 人,机器 2 到 3 台,发布频率周更。③ 问题:要不要容器化?④ 候选:A,继续手工配环境;B,容器化,用 Docker。⑤ 维度:环境一致性、部署速度、学习成本。⑥ 打分:A 在两到三台机器时已经吃力——每台手工配,出错率随机器数上涨,上一环的代价就在眼前;B 的环境打包一次解决——镜像就是环境,机器再多也一样。⑦ 选择:B,Docker。⑧ 收益:环境一致,镜像保证;部署可复现,同一个镜像到处跑;开发、测试、生产同环境——彩排和生产用同一个镜像,彩排升级成同环境预演。⑨ 代价:容器技术要学,8 人团队人人得会一点;镜像要维护,构建时间和存储都是成本。演进条件也记下:机器再多、手工真的管不过来时,或部署频率再涨、镜像的好处更明显时,再评估下一步。决策记录照例填进表里——要不要容器化。第二个半场,要不要编排,下一节走。
23.4 容器多了怎么办:编排与"不上 K8s"
容器解决了环境问题,但容器自己带来了新问题——问题链的第四环:容器多了怎么管?两台机器上跑着十几个容器,谁负责这个容器挂了自己重启?谁负责新版本容器换掉旧版本?谁负责流量分给哪个容器?这些"容器的管理",就是编排:K8s 一类的容器编排平台。
什么时候需要编排?给"容器多了"一个量化的说法。容器数量少——十几个以内,机器少——几台,手动管理还管得过来,不需要。容器几十上百个、机器几十台、要自动伸缩、要自愈,需要。判断标准压缩成一句话:手动管容器的成本,超过学编排的成本时,就上。编排本质上是容器管理的复杂度预算——技术债的算法在这里同样适用。
编排的收益清单先摆出来,让"需要时再上"有具体所指。一共四项:自愈,容器挂了自动重启;伸缩,流量大了自动加容器;调度,容器自动分配到合适的机器;滚动更新,新版本容器自动逐批替换。每一项都是"容器多了"之后的痛点:容器少时,手动重启 30 秒;容器多时,"哪个挂了"都找不到。所以编排的收益跟着容器数量涨——这正是"业务没到就别上"的依据:收益是规模的函数。
成本这边也要算实账,而 K8s 的学习曲线陡峭是出了名的。概念多:Pod、Deployment、Service、Ingress,每个都是新抽象,K8s 几乎是名词密度最高的技术之一。运维重:K8s 集群本身要升级、监控、排障——集群挂了比应用挂了更惨。排障难:容器网络和存储的问题层层叠叠。给这笔成本一个量级:8 人团队学 K8s 的三个月,够发十次版。时间也是预算,三个月就是三个月。
现在走本章决策链的核心一站:要不要上 K8s?前面 Docker 的决策算热身,这个才是完整示例。① 目标:容器管理自动化——自愈、伸缩、调度。② 约束:阶段 4 的团队现状——机器 2 到 3 台、容器十几个、发布周更,还有一条最要命的:8 个人里没有专职运维。③ 问题:要不要上 K8s?④ 候选:A,上;B,不上,Docker 加轻量管理。⑤ 维度:收益,自动化程度;成本,学习曲线和运维负担;时机,当前规模匹配吗。⑥ 打分:收益侧,K8s 的招牌能力在"两三台机器、十几个容器"面前全部用不上——自愈?手动重启一个容器 30 秒。伸缩?流量还没大到要自动扩。成本侧,前面算过:没人专职运维,三个月够发十次版,是实打实的时间账。⑦ 选择:B,不上 K8s。容器用轻量方式管——发布脚本加简单工具。⑧ 收益:省下 K8s 的学习与运维成本,团队的精力留在业务上。⑨ 演进条件:机器到十几台、容器到几十个、或专职运维加入——满足任何一条,再评估。
"为什么不上 K8s?"这个问题,架构演进到更大规模时大概率会被再问。记录里存着当时的理由:规模、成本、时机。而且请注意,"不上"和"上"一样,都是决策——之前有过"缓存阶段 1 不上""微服务不上",这里再添一笔"不上 K8s"。每一个"不上",都是决策记录里的演进条件换来的:条件没到,记录挡驾;条件到了,记录放行。
这个决策也浓缩了全书一以贯之的决策观:业务没到就别上。K8s 是好技术,但好技术乘以用不上的规模,等于负债——学习成本和运维成本都是利息。决策模型的用处恰恰在这里:挡住名声最好的方案。名声大但用不上,这个坑在认证环节踩过——JWT 名声大,阶段 1 用不上;K8s 名声大,阶段 4 用不上。同一个决策习惯,从认证一路用到部署。
那"不上"之后的日常长什么样?容器用轻量方式管。发布有脚本:流水线的部署阶段,拉新镜像、停旧容器、起新容器,脚本一条龙。监控靠人加日志:容器挂了,日志里看得到,人工重启 30 秒——K8s 的自愈是"机器 30 秒",我们是"人 30 秒",在两三台机器的规模上,差别不大。这就是"收益用不上"的量化。演进条件照例写清楚:十几台、几十容器、专职运维——将来触发了,不用重新吵。
最后交代这个决策的"将来"。演进条件就是重新评估的触发点:将来规模到了,当年的决策记录会被翻出来——"当时为什么不上、现在条件到了吗",一问一答,结论自己长出来。决策模型立过一条规矩:约束变化时,重选。演进条件,就是约束变化的显式标记。
23.5 多环境与配置管理:502 的根源
回到开场那场 502 事故,本章的环境问题主线还没收。事故的根源是配置写死在代码里——为什么这是错的?因为配置是环境的一部分,而环境是分层的:本地、测试、生产。配置写死在代码里,等于换环境就要改代码,等于每个环境一个代码版本——这和"配置进制品"是同一场灾难的两种写法,结局都是复现破产。所以"配置与代码分离"的本质,一句话:让代码只有一个版本,差异全在配置。这是"同一个制品到处跑"在配置侧的前提。
配置放在哪?三个层级,由错到对排开。代码配置,写死在代码里——最错,换环境改代码,502 事故就是这么来的。环境变量,部署时注入——推荐做法。数据库地址、密钥、域名这些环境相关的值,放在环境变量里,同一个制品,不同环境注入不同值。配置中心,集中管理配置的服务——配置多了以后的方案,十几个服务各有配置时,集中管理、版本化、热更新。阶段 4 用环境变量就够——配置中心是"配置多了"时的演进,复杂度预算的原则在此适用。
这套做法有个行业共识的名字,12-Factor。它的配置要点一句话:配置与代码分离——配置随环境变,代码不随环境变,这正是"制品不含环境"的完整表述。判断标准也极简单:**同一份代码,能不能不经修改部署到任何环境?**能,配置管理对了;不能,配置又混进代码了。12-Factor 还有两个与本章相关的要点,一句带过,它们后面各有主场:日志是事件流——应用把日志打到标准输出,由平台收集;进程无状态——进程不存本地状态,会话入 Redis 就是这个原则的实践。所以 12-Factor 不是舶来的口号,而是本章每件事的行业共识版——配置分离、无状态、日志流,前面章节的做法,它给起了个总名。
配套纪律有三条,这是 502 事故的直接修复。第一,密钥不进代码、不进仓库——密钥是命门,这个后面安全部分还会展开;环境变量或密钥服务是它该待的地方。第二,debug 不上生产——调试信息和堆栈是攻击者的地图;生产环境的错误详情只进日志,响应体只给通用格式。第三,配置变更要审批——改配置就是改生产行为,所以配置和代码一样要走评审轨道,要被人看。
事故修复的收尾,也照例走一遍。修复后,开发者做了三件事:数据库地址移进环境变量——代码里不再出现环境相关的字;部署清单加一步"配置核对"——人工确认三问之外,再对一遍这次部署的环境变量;配置变更走 PR——改配置和改代码一个流程。事故的每一层教训,都变成了机制的一环:事故意味着漏掉了某个承诺,配置管理的承诺,从这场事故里补上了。
配置和安全的关系也要点一笔。配置是攻击者的目标之一:数据库地址和密钥泄露,就是攻击面;配置仓库被拖,等于密钥全丢,这也是供应链的一种形态。所以配置管理的安全纪律是:密钥放环境变量或密钥服务,配置仓库做访问控制——谁能看配置,按最小权限来。配置是环境的一半,也是安全的一半——信任模型在配置环节的形态,就这两条。
最后把"配置里都有什么"分个类,顺便把变更的节奏也分出来。三类内容:连接类,数据库地址、缓存地址——502 事故的主角;密钥类,支付密钥、签名密钥——命门所在;行为类,功能开关、限流阈值——决定系统"怎么跑"。三类配置,两种变更频率:连接和密钥低频,行为类可以高频——功能开关就是"不发布也能改行为"的配置,配置管理的进阶形态,灰度发布时会用到它。
那么"配置和代码一样要被看",是所有配置同等待遇吗?不是,要按风险分级。连接和密钥类配置错了,就是 502,高危,必须审批加测试;行为类配置错了,是功能异常,中危,可以快速回改。高风险走评审,低风险走快速通道——按风险排序,这个逻辑在安全投入上用过,在配置管理上原样复用。
23.6 发布与回滚:把"上线"变成"切换"
部署体系还剩最后一块:发布方式。发布方式的核心转变是一句话:从"替换"变成"切换"。
三种方式
蓝绿部署:两套环境,蓝和绿,新版本部署到绿环境,验证通过后把流量切到绿——蓝留着,随时切回。切换是瞬间的、可逆的。金丝雀发布:先放 1% 的流量到新版本,盯着监控看,没异常再逐步放量到全量——小步放量,坏版本最多影响 1% 的用户。滚动发布:一台一台换——多实例时代逐台发布的默认方式。
这三种方式,和流水线那章的人工确认清单一问正好接上。清单有三问,其中"回滚方案是什么"这一问,答案就是本章的发布方式:蓝绿的回答是切回蓝,滚动的回答是逐台换回——确认"能回",确认的就是"发布方式支持回"。清单上问的每一问,到这里都实现了一部分。
回滚机制由此完整。敢部署的前提是能回滚,而回滚的本质动作是同一个:把流量切回上一个制品。差别只在速度:蓝绿的回滚是切回蓝,秒级;滚动的回滚是逐台换回旧版本,分钟级;金丝雀的回滚是把那 1% 撤回来,最快。所以发布方式的差别,就是回滚速度的差别——选发布方式时先问一个问题:出事了,多久能回?
回滚还应该自动化。部署是自动的,回滚也该自动:一个按钮、一条命令,指回上一个制品、切回流量——而不是深夜手忙脚乱地敲命令。"能回滚"的完整定义,就是一条自动化的回滚路径。出事了先按回滚按钮,再慢慢查原因——应急响应的雏形,就从这个按钮开始。
发布流程里还有一步老熟人:数据库迁移。规矩早就立了——先迁移,再部署新制品——现在它正式写进发布单,成为人工确认清单里明确的一步。同时要记住那条不对称:回滚时代码能指回去,数据库不能,只能前进。
不对称的完整版要展开一句。代码可以回滚,数据不能:新代码写下的数据,旧代码可能读不了——表结构一变,旧代码就不认了。所以"回滚"的完整含义是两件事:代码指回旧制品,数据要兼容旧代码——迁移必须设计成向前兼容。发布与回滚的每一环都绑着数据库,这也是为什么迁移是发布里最不能出错的步骤——改表很贵,在发布现场改错表,贵上加贵。
发布现场靠什么把守?健康检查。发布后的规矩是:新实例的健康检查通了,才接流量——负载均衡只给健康实例分流量。发布不是"放上去",是"健康了才放上去"。
而健康检查有深度之分,这个分别决定了它能发现什么。浅检查问"进程在吗"——接口返回 200 就完事。深检查问"依赖在吗"——接口里真去查数据库连接、缓存连接,报出关键依赖的状态。回到开场那场 502:数据库地址写错,进程照样活着,浅检查照过,但数据库连不上,深检查当场现形。所以深度决定发现能力:浅检查防"进程挂了",深检查防"配置错了"。发布现场这道哨兵,必须用深检查。
用户那边是什么体验?三种方式有个共同点:用户无感知。切换瞬间完成,或者流量逐步放量,用户看到的始终是"一直可用"。部署体系给用户的礼物就在这里——发布冻结消失了,发布本身也变得不可见。从发布日求神拜佛,到无感知发布,"发"这件事,从团队的负担变成了团队的日常。
那阶段 4 选哪种?两个维度。回滚速度:出事了多久恢复——蓝绿秒级,滚动分钟级。环境成本:蓝绿要两套环境,机器翻倍,两三台机器的规模玩不起。所以答案:滚动发布为主——多实例天然支持逐台换;金丝雀作为高风险发布的选项——涉及数据库迁移或大版本时,先放 1% 试试。发布方式同样是一次正经的决策,权衡回滚速度和环境成本——它是前面容器和编排两次决策的配套。
什么发布算高风险?也分个级。常规发布:小功能、修复,滚动即可。高风险发布:数据库迁移、大版本、核心链路改动,金丝雀先放 1%。紧急修复:线上事故,快速直接——回滚优先于发布形式的完美。发布方式跟着风险走,和当年安全投入的排序逻辑同一个:按风险排,不按热度排。
发布之后呢?发布后是"看着它"。金丝雀的"观察"靠监控指标——错误率、延迟;滚动发布"换不换下一台"的判定,靠的也是同一双眼睛。所以发布的完整定义是"放上去加看着它":部署完成不是终点,是运行期的起点,而看着它的工具,就是监控系统。本章负责发布,监控那一章负责看。
最后一件事,把"能回滚"从承诺变成能力:回滚要演练过。测试是对行为的承诺,回滚也是承诺——"出事了能回"不能靠嘴说。办法是定期做一次演练发布:在测试环境完整走一遍——发一个新版,故意弄挂,回滚——验证回滚真的能回。"能回滚"和"演练过能回滚"是两件事:约束是机制在才算数,回滚机制要证明过才算数。
演练的意义还不止回滚。演练验证的是整个"出事的处置流程",而主动制造故障、验证系统扛得住的故障演练,是高可用一章的日常。本章的演练发布是它的雏形:先练回滚,再练故障——演练,会成为高可用的基本动作。
23.7 部署形态的演进全景
本章的最后,把问题链收成一张全景图。前面每节是链条的一环,这张图把环串成链:
裸机(一台服务器,手动部署)
→ 代价:单点、手动、环境不一致
多实例(几台机器 + 负载均衡)
→ 代价:环境每台手工配,机器越多越难
容器(Docker:环境也打包)
→ 代价:容器多了手动管不过来
编排(K8s:容器自动管理)
→ 代价:学习与运维成本陡——"业务没到就别上"
这张图和全书最早那张 Web 系统总架构图放在一起看,分工就清楚了。总架构图画的是"系统由什么组成"——浏览器、网络、服务端、数据层;这张图 23-3 画的是"系统怎么被放置"——部署形态的演进。同一套系统,两张图:一张看组成,一张看放置。
再读一遍这张图的箭头,方向全是被逼的:裸机的单点逼出多实例,多实例的环境手工逼出容器,容器的管理问题逼出编排——演进的老规律,这是它在部署领域的版本。但注意,链条不是必经之路。案例停在"容器"——Docker 够用,没有走到编排。规则一句话:原方案无法承受才往前走,能承受就停。复杂度预算在部署领域同样适用:"够用"是标准,不是"最新"。
"能承受就停"还要再说透一句,因为它是本章全部决策的收尾。停在容器不是遗憾,是预算管理——复杂度预算要花在刀刃上,K8s 的预算属于"容器多到管不过来"的那一天,不属于今天。把眼光放回全书的"不上"系列——缓存不上、微服务不上、K8s 不上——它们全是同一个逻辑:今天的预算买今天的问题,将来的问题用将来的预算。至于"上"的时机什么时候到,架构演进那一章会看到。
部署形态还和团队规模有一条对应关系,这是"流程跟着团队走"在部署侧的版本。1 人团队,裸机加手动部署就够用;8 人团队,多实例加容器,本章的选择;几十人的团队,才轮到编排和云原生。部署形态跟着团队和规模长,不跟着"最新技术"长。
本章在时间线上也有它的位置。这是阶段 4 的收官章:团队 8 人、机器 2 到 3 台、发布周更——开场那场 502 事故,登记为阶段 4"环境配置问题"的案例来源。更重要的是跨度:构建、流水线、部署这三章走完,"从代码到用户"的整条链路闭合了。阶段 4 的目标——"部署从手搓到流程"——在这里正式完成。
最后回到流水线,收一个口。流水线有八步,第八步是"部署生产",而"部署"两个字,正是本章的全部内容:部署到哪——多实例;怎么部署——容器;配置怎么注入——环境变量与配置管理;发布怎么切——蓝绿、金丝雀、滚动。剧本和舞台的比喻在这里落地:流水线是剧本,本章把舞台搭起来了——第 ⑧ 步从此有了全部的机械结构。
23.8 本章对应表
| 业务诉求 | 技术选择 | 为什么 | 代价/取舍 |
|---|---|---|---|
| 一台服务器不够(流量/单点) | 多实例 + 负载均衡 | 单点挂了全站挂,流量单机见顶 | 环境每台手工配(问题链下一环) |
| 多实例环境不一致 | 容器(Docker) | 环境也打包:镜像=制品+环境 | 容器技术要学、镜像要维护 |
| 容器多了管不过来 | 编排(K8s 等) | 自愈/伸缩/调度自动化 | 学习运维成本陡 |
| K8s 要不要上 | 九步决策(★不上:Docker 够用) | 收益在 2-3 台机器前用不上,成本实打实 | 演进条件=十几台/几十容器/专职运维 |
| 配置错误全站 502 | 配置与代码分离(环境变量) | 配置是环境的一部分,部署时注入 | 密钥管理要跟上(19 章) |
| 发布不能全站一起换 | 蓝绿/金丝雀/滚动 | 切换优于替换:可逆、可观察 | 环境/流量要支持切换 |
| 出事了能回 | 回滚=指回制品 | 第 21 章制品版本是回滚坐标 | 迁移"只能前进"(回滚不对称) |
本章小结
- 部署形态的每一步,都是被前一步的代价逼出来的:裸机 → 多实例 → 容器 → 编排——但链条不是必经,能承受就停;
- 环境与配置是部署的一半:配置错误会让全站 502——配置与代码分离,部署时注入;
- K8s 不是标配:"业务没到就别上"——九步决策挡住"名声最好的方案";
- 发布从"替换"变成"切换":蓝绿/金丝雀/滚动——发布方式的差别,就是回滚速度的差别。
下一章
部署体系建好了:多实例、容器、配置管理、发布回滚——制品被稳稳地放上了服务器,业务在跑。但一个新的问题浮出水面:"在跑"不等于"不会挂"——服务器硬件会坏、进程会崩、流量会突然暴涨。
第 24 章,高可用:业务不能挂的保障——多实例的真正意义、故障转移、容量规划——部署上线了,业务不能挂。