速成课 · No. 20
有两个想法改变了软件从开发者笔记本走到真实用户手中的方式。第一,把从代码到生产环境的整条路自动化,让发布变得无聊而不是吓人——这就是 CI/CD。第二,把应用连同它需要的一切一起打包,让它在任何地方都跑得一模一样——这就是容器。两者结合,把「在我机器上能跑」从借口变成了正经的计划。
21 个概念 · 每个都有图 · 结尾有速查表
写代码只是工作的一半;把它安全地送到用户面前是另一半。在很长一段时间里,后一半都缓慢、靠手工、令人胆战心惊——而理解它为什么如此,正是理解一切修补手段的关键。
课程地图:七组共 21 个概念
一张地铁线路图:先看整条线路,再一站一站地坐过去。
这门课跟着一个改动,从笔记本一路走到用户手里。先讲难题:为什么手工 deploy 吓人。然后是 CI(检查每个改动)、CD(把它送到用户那里)、pipeline(完成这一切的机器)、容器(到处都是同一个环境)、编排(很多容器跑在很多服务器上),以及安全 deploy(怎样发布才能随时撤销)。
每个概念怎么读
一本护照:每个人的栏目都一样,所以很容易比较。
下面每个概念的结构都相同:它的英文名称(你在文档和工作中会遇到的术语)、一幅生活中的画面、一张图,以及一张有若干栏目的卡片——问题或「是什么」、思路、例子、代价或陷阱,以及相关概念。整门课只有一个大想法:交付软件应该是无聊的、自动化的、处处一致的。
Deploy——为什么手工做很吓人
一位外科医生在午夜里凭记忆徒手做一台精细的手术,又累又困——手一抖,病人就垮了。这就是手工的生产环境 deploy。
所谓 deploy,不过就是「让新版本上线」。过去的做法,是一个人按部就班地手工操作:把文件复制到服务器,敲几条命令,然后祈祷。这又慢又容易出错,风险大到团队都很少 deploy——于是每次 release 都变得庞大,从而风险更高。靠手工去做,它就成了一周里最让人紧张的时刻。整个 CI/CD 领域存在的意义,就是把那个时刻变得无聊。
- 是什么
- 让应用的新版本对用户上线。
- 例子
- 手工 deploy:SSH 登进服务器,复制文件,重启进程——然后祈祷。
- 陷阱
- 靠手工又慢又有风险——所以很少做,每次 release 都变得庞大。
- 关联
由 Continuous Delivery 和 Pipeline 自动化;由 Rollback、Canary、Blue-green 变得安全。
Works on my machine——经典陷阱
一份食谱在你自家厨房做出来完美无缺,到了别人厨房却失败了——烤箱不同、面粉不同、海拔不同。食谱并不是故事的全部。
在开发者笔记本上跑得好好的代码,到了生产环境常常崩掉,因为两个环境不一样——版本不同、设置不同、库不同、操作系统不同。**「在我机器上能跑」**正是这一整类 bug 的笑点,它们都源于这些差异。笔记本和服务器不是同一个地方,所以「在我构建它的地方能跑」对它实际运行的地方什么都保证不了。
- 是什么
- 一个只在另一个环境里出现的 bug,因为两个环境不一样。
- 例子
笔记本上是 Node 20,服务器上是 Node 16——用了较新语言特性的代码,只在生产环境里报错。
- 陷阱
- 代码不是故事的全部:版本、设置、库,以及它周围的操作系统,也都有影响。
- 关联
由 Container 根治——应用自带环境——也靠 Artifact——你测试的就是你发布的。
Boring releases——发布应该是无聊的
一部电梯,你一天进出上百次都不会多想一下——因为它是自动化的、经过测试的、安全的。你绝不会去坐一部每趟都得手工小心操作的电梯。
这门课里所有内容的目标,就是让交付软件变得无聊:自动化、经过测试、可重复到这种地步——你一天里多次 release 小改动,却毫无波澜。这有点反直觉:deploy 得 越 勤,反而 越 安全——每次 release 都很小(small batches),所以一旦出问题,原因一目了然,修起来也很轻。罕见、庞大、手工的 release 才是危险的那种。「无聊」是一次 deploy 所能赢得的最高赞美。
- 问题
- 一次大 release 里有几百个改动,出问题时没人知道是哪一个干的。
- 思路
- 经常发布小改动——自动化地、毫无波澜地。
- 例子
- 不再一个月发一次——而是一天发好几次很小的 release,每次只有几行。
- 代价
- 需要自动化:没人能靠手工 deploy 得这么勤。
- 关联
- 只有靠 CI、Continuous Delivery 和 Rollback 才可能做到。
手工 deploy 又慢、又危险、又罕见——这反而让它更危险。目标恰恰相反:让交付软件自动化到、细碎到无聊的地步,一天里做很多次。
CI/CD 的前一半,讲的是在问题一冒头的瞬间就抓住它——做法是在每个改动落地时自动 build 并 test 它——这样那份共享的代码库就始终能正常工作。
Continuous Integration(CI)——每个改动都被 build 和 test
一个拼写检查器,你每打一句话它就检查一句,当场标出错误——而不是一个校对员,一个月后等一切纠缠在一起时,才找出上百个错误。
Continuous integration(CI) 的意思是:每当有人提出一个改动,一个自动化系统就会 build 这个项目并对它运行 test——在几分钟之内完成,全程不经人手。如果这个改动让 build 坏掉或没通过某个 test,你立刻就会知道,而此时它还很小、还鲜活地留在你脑子里。CI 是一个常开的哨兵,在每一份贡献能惹麻烦之前就先检查它。
- 问题
- 一个有问题的改动,几周后才被发现,那时它已经和其他一切纠缠在一起了。
- 思路
- 每个改动一到,就自动 build 并 test。
- 例子
每个 pull request 上,一个 CI 服务(比如 GitHub Actions)运行 build 和 test,并在上面打一个绿色或红色的标记。
- 代价
- 你需要好的自动化 test——CI 只检查你的 test 检查的东西。
- 关联
尽早抓住问题(Shift left),保持 Green main branch;是任何 Pipeline 的第一个阶段。
Shift left——趁问题还小就抓住它
发现一块砖砌歪了,趁早改很容易;等在上面又盖了三层楼才发现,那就是一场灾难。早发现很便宜;晚发现很昂贵。
CI 的价值在于时机。一个 bug 在被引入的当下就被抓住,修起来不费吹灰之力——你清清楚楚知道改了什么。同一个 bug 几周后才被发现,埋在上面盖起来的一切之下,可能要花上几天才能理清。这就是 shift left 的想法——把发现问题的时间点在时间线上往左移,更靠近改动发生的那一刻。CI 把发现故障的时机从「很久以后,跟一切混在一起」挪到了「就是现在,孤立地」。
- 是什么
- 尽可能早地发现问题——靠近改动发生的那一刻。
- 例子
commit 3 分钟后某个 test 失败了——你知道是哪一行干的。同一个 bug 一个月后在生产环境里出现——要查好几天。
- 陷阱
- 检查必须快:如果 CI 要跑一个小时,人们就不再等它了。
- 关联
- CI 的核心价值;与 Fail fast 是同一种精神。
Green main branch——让 main 始终可用
一间共用的厨房,每个人走之前都必须收拾干净——这样下一个人随时都能用,而不是一堆要别人先去收拾的烂摊子。
CI 更深一层的目的,是让 main 分支——那个共享的、可信赖的版本——始终处于可用状态。一个改动只有在能 build 并通过 test 时才会并入 main,所以 main 永远不会变成一团堵住整个团队的烂摊子。这正是频繁、有把握地交付得以成立的原因:当 main 始终可用,你随时都可以从它发布。
- 问题
- 一个坏掉的 main 会堵住整个团队,让任何 release 都无法进行。
- 思路
- 只有 build 成功并通过 test 的东西才能进入 main。
- 例子
- 仓库里的一条规则:在 CI 检查全绿之前,「Merge」按钮一直是禁用的。
- 代价
- 有时你得等 CI,并在合并前先修好 test。
- 关联
- 由 CI 强制执行;让 Continuous Delivery 可以随时从 main 发布。
Continuous integration 在每个改动落地的瞬间就 build 并 test 它,趁问题还小就抓住它,让 main 分支始终处于可用状态。
如果说 CI 确保每个改动都是好的,那么 CD 就负责把这个好改动自动送到用户面前——把 release 从一场手工仪式,变成一个按钮,甚至连按钮都不用。
Continuous Delivery(CD)——自动化的是发布,不只是测试
一条工厂流水线,并不止于质检,而是把合格的产品一路送到装货码头、装上卡车——没人用手搬箱子。
Continuous delivery(CD) 是 CI 的延伸:一个改动通过所有自动检查之后,系统还会自动为 release 做好准备,并且可以把它推到生产环境。那些手工的、容易出错的 deploy 步骤,变成了每次都以同样方式运行的代码。CI 证明改动是好的,CD 把这个好改动送到用户手里——不需要有人在半夜里小心翼翼地往服务器上敲命令。
- 问题
- 改动已经证明是好的,但发布仍靠手工——又慢又出错。
- 思路
- 发布步骤就是代码;每个好改动都自动处于随时可发布的状态。
- 例子
- CI 跑绿之后,一个脚本构建软件包、存起来,再用一个按钮把它推到生产环境。
- 代价
- 你要把整条发布路径一次性写成代码,而且得信任你的自动化 test。
- 关联
- 建立在 CI 之上;发布的是 Artifact;运行在 Pipeline 里。
Continuous Delivery vs Continuous Deployment——有按钮还是没按钮
一个封好、写好地址、放在门边等着的包裹,对比一个已经交给快递员的包裹——都已准备好出发,但前一个要等你发话。
这里有个微妙的区别。Continuous delivery 指每个好改动都被自动准备到随时 可 deploy 的状态,但要由人按下按钮来发布。Continuous deployment 则更进一步:每个通过检查的改动都会自动发布到生产环境,完全没有按钮。两者都叫「CD」;区别在于最后一步是否由人批准。
- 是什么
- Delivery——准备好发布,由人按按钮。Deployment——自己发布,没有按钮。
- 例子
银行在签字批准后按一下按钮发布(delivery);一个网络服务每个绿色 commit 都自己上线(deployment)。
- 陷阱
- 怎么选,取决于你有多信任自己的自动检查。
- 关联
两者都属于 Continuous Delivery;没有按钮时,Canary 和 Rollback 就更重要了。
Artifact——你发布的那个东西
一个封好、贴了标签的箱子,只打包一次,然后作为一个整体被运输、存放、送达——而不是每到一站都用散件重新组装。
pipeline 会产出一个 artifact——你的应用的一个构建好的、带版本号的、可直接运行的软件包。你只构建一次,这同一个 artifact 会依次经过 testing、staging 和生产环境,所以你测试的就恰恰是你发布的。如果每个阶段都重新构建,细微的差异就可能悄悄混进来。Build once, deploy everywhere——一个 artifact,构建一次,到处部署——消除了一整类「可是在测试里是通过的」这种意外。
- 是什么
- 应用的一个构建好的、带版本号的软件包,可直接运行。
- 例子
app v1.4.2——同一个软件包依次经过 test、staging 和生产环境。- 陷阱
- 每个阶段都重新构建,到达生产环境的就不再是你测试过的那个东西。
- 关联
Pipeline 的产出;对容器来说,artifact 就是 Image;让 Rollback 成为可能。
Continuous delivery 把每个好改动的发布,作为一个构建好的 artifact 自动化——无论是由人按下最后的按钮(delivery),还是它自己上线(deployment)。
CI 和 CD 由一条 pipeline 来运行:这是一条自动化的装配线,一个改动从 commit 到生产环境都沿着它走,路上只要出问题就会停下。
Pipeline——自动化的装配线
一条汽车工厂的流水线:底盘依次经过焊接、喷漆、检验,每个工位做完自己的活才往下传——没有跳过的步骤,也没有人用手搬运。
pipeline 是一个改动在通往生产环境的路上所经过的一系列自动化阶段——通常是 build,然后 test,然后 deploy。一个 commit 从一头进去,如果一切顺利,另一头出来的就是一次上线的 release,中间没有任何人工步骤。pipeline 就是让 CI/CD 变成现实的那台具体机器:「把整条路自动化」真正落在这里。
- 是什么
- 从 commit 到生产环境之间,一串定义好的自动化阶段。
- 例子
仓库里的一个配置文件描述了三个阶段:
build→test→deploy;CI 服务在每个 commit 上运行它们。- 陷阱
- pipeline 也是代码:它需要维护,而且一条很慢的 pipeline 会拖慢整个团队。
- 关联
CI 和 Continuous Delivery 的家;由 Fail fast 守护;产出 Artifact。
Fail fast——红灯停,绿灯行
一条装配线上有一根拉绳,谁都可以拉,一出现缺陷就能让一切立刻停下——线上还有一盏红绿灯:绿灯,工作继续往下流;红灯,一切等待,直到问题被清除。
一条好的 pipeline 就是为 fail fast 而建的:如果在任何一个阶段 build 坏了或 test 失败了,pipeline 就停下,这个改动不再往前走。pipeline 用绿色(通过)和红色(失败)来说话。这个简单的信号,用一道毫不含糊的关卡取代了焦虑的人工判断。团队的规矩很简单:红灯时不在上面继续构建,红灯时不发布。
- 问题
- 如果流水线不停下,一个有问题的改动可能一路滚到生产环境。
- 思路
- 任何一个阶段变红,pipeline 立刻停下;只有绿色才往下走。
- 例子
- 两个 test 失败了——
deploy阶段连启动都不会;团队先去修。 - 代价
- 不稳定的 test 会无缘无故让流水线停下——必须把它们修好。
- 关联
- Pipeline 的安全属性;与 Shift left 是同一种精神。
pipeline 是从 commit 到生产环境的自动化装配线——build、test、deploy——它 fail fast,遇红就停,所以没有哪个有问题的改动能溜过去。
故事的另一半直接解决「在我机器上能跑」:把应用连同它需要的一切打包成一个单元,让它在任何地方都跑得一模一样。这个单元就是容器。
Container——应用连同它的环境
宇航员的航天服自带空气、温度和气压——所以无论在空间站里还是在太空中,人都能一样地工作,因为他把自己的环境带在身上。
container 把你的应用和它运行所需的一切——代码、库、设置、正确的版本——打包成一个自成一体的单元。因为它自带环境,所以无论你把它放在哪里,都跑得一样:你的笔记本、测试服务器、生产环境。容器不依赖宿主机上装了什么,所以造成「在我机器上能跑」的那些差异就这样被填上了。
- 是什么
- 应用加上它的整个环境,打包成一个自成一体的单元。
- 例子
里面装着你的 API 和 Node 20 的容器,在 macOS 笔记本和 Linux 服务器上跑得一样。
- 陷阱
- 容器修好的是环境,不是你代码里的 bug——你仍然需要 test。
- 关联
根治 Works on my machine;从 Image 启动;大规模由 Kubernetes 管理。
Docker——给它命名的那个集装箱
钢制的海运集装箱:任何港口、轮船、卡车处理它的方式都一样,因为这个箱子是标准的,不管里面装的是什么。全世界的货物就靠这一个想法在流动。
这个名字本身就是比喻。在有标准集装箱之前,货物是一件一件笨拙地装上去的;标准箱让任何起重机、任何轮船都能以同样的方式处理任何货物。软件的容器对应用做的是同一件事:一个标准单元,任何兼容的系统都能运行它,而不必关心里面是什么。Docker 就是让这一切成为主流的工具。把箱子标准化,才让整条物流链——也就是你的 pipeline——顺畅运转。
- 是什么
- 让容器成为主流的工具:它构建并运行容器。
- 例子
docker build根据 Dockerfile 构建一个 image;docker run从它启动一个容器。- 陷阱
- 标准化的是箱子,不是里面的东西:里面装的仍然得是一个能正常工作的应用。
- 关联
- 制造 Container 和 Image;由 Kubernetes 运行它们。
Image vs Container——食谱和菜
一份食谱,对比照它做出来的那顿饭:一个是固定的一套说明,另一个是一个具体的实例,你可以做出很多份,份份一样。
有两个词常被混淆。image 是打包好的、冻结的蓝图——包含你的应用及其环境的构建产物,存放在仓库里(一个 registry)。container 是从这个 image 启动的、正在运行的实例。一个 image 可以启动许多一模一样的容器,就像一份食谱能做出许多一模一样的菜。你在 pipeline 里只构建一次 image,然后想跑多少容器就从它跑多少——每一个都是一样的。
- 是什么
- Image——冻结的蓝图(食谱)。Container——它正在运行的实例(菜)。
- 例子
- 一个 image
api:1.4.2——五个一模一样的运行中的容器。 - 陷阱
- 在运行中的容器里改东西没有意义——应该改 image,然后重新构建。
- 关联
- 对容器来说,image 就是 Artifact;Kubernetes 让这些副本持续运行。
容器把应用连同它需要的一切打包,所以它在哪里都跑得一样。你只构建一次 image(食谱),然后从它运行一模一样的容器(菜)。
运行一个容器很容易。要在许多机器上运行几百个——让它们保持健康、随需求伸缩、不停机地更新——就需要一位指挥。这就是编排。
Orchestration / Kubernetes——容器的指挥
一位空中交通管制员在调度满天的飞机——决定哪架降落在哪里,遇到问题就改道,让几百个移动的部件保持安全、协调。
当你有许多容器分散在一大批服务器上时,必须有个东西来决定每个容器跑在哪里、重启挂掉的那些、并均衡负载。这就是 orchestration,占主导地位的工具是 Kubernetes。你告诉它想要的状态——「把这个服务跑五份」——它就持续不断地努力让现实与之相符:安放容器、盯着它们、修正偏差。
- 问题
- 没人能靠手工盯住许多服务器上的几百个容器。
- 思路
- 编排器决定每个容器跑在哪里,重启挂掉的,并均衡负载。
- 例子
你在 YAML 里用
replicas: 5描述一个服务——Kubernetes 就在不同服务器上维持五份副本。- 代价
- 一个有自己一套语言的复杂系统;对一个小服务来说往往大材小用。
- 关联
从 Image 运行 Container;保存 Desired state;执行 Rolling update。
Desired state——自愈与自动伸缩
一个你设定了目标温度的恒温器:它不停地感知房间,自己调节,你不用再碰它。你声明目标,它来维持。
编排是声明式的:你描述目标,而不是步骤。说一句「始终保持五份健康的副本在运行」,如果有一份崩了,系统会自动启动一个替补——这就是 self-healing(自愈)。如果流量激增,它可以增加副本,需求回落时再撤掉——这就是 auto-scaling(自动伸缩)。你不再手工照看服务器,而是声明你想要什么。从「做」到「声明」的这个转变,正是它的核心。
- 是什么
- 你声明目标(「5 份健康副本」);系统不断把现实拉回到目标上。
- 例子
- 夜里一份副本崩了——几秒钟内就有新的顶上,没人被叫醒。
- 陷阱
- 系统会精确地维持你所描述的东西:目标写错了,它也会同样固执地维持错的。
- 关联
Kubernetes 的核心;与 Infrastructure as Code 是同一种「描述它,而不是手工去做」的想法。
Rolling update——不停机地发布
在行驶的卡车上一个一个地换轮胎,让它永远不用停下——车一直在往前开,每个部件却在底下被换掉。
编排让你可以通过 rolling update 不停机地更新:它每次替换几个旧容器为新容器,并在继续之前检查每个新容器是否健康(一次 health check),所以服务自始至终都在线。如果新版本表现不对,它可以停下来,并自动 roll back 到旧版本。现代服务就是这样在没有维护窗口的情况下发布更新的——在线、逐步、可逆。
- 问题
- 靠停掉服务来更新,意味着用户要经历停机。
- 思路
- 一次替换几份副本,并对每个新副本做 health check。
- 例子
- 四份 v1 副本中,先有两份变成 v2,再轮到另外两份——服务一秒钟都没停过。
- 代价
- 有一段时间两个版本同时在运行——它们必须能够并存。
- 关联
由 Kubernetes 完成;出问题时——Rollback;近亲——Canary 和 Blue-green。
编排(Kubernetes)在许多机器上运行许多容器:你声明期望状态,它就自愈、自动伸缩,并且不停机地推出更新。
这一整套机制,都是为了让交付既安全又频繁。最后这些习惯,是为了缩小任何一次 release 的爆炸半径——因为最安全的 deploy,是你能撤销的那一种。
Rollback——永远留一条退路
一份带「撤销」的文档:你放心大胆地做一个大改动,因为知道按一下键就能恢复上一个版本,所以可以自由尝试,而不是害怕每一次编辑。
最重要的安全属性是 rollback——当一次 release 出了问题时,能立刻回到上一个已知良好的版本。因为 pipeline 发布的是带版本号的 artifact,所以退回去就只是把前一个重新 deploy 一遍。知道自己能在几秒钟内撤销一次 deploy,正是让频繁发布变得安全的原因:一个坏版本只是一个小小的、可逆的波折,而不是一场危机。永远不要发布你收不回来的东西。
- 问题
- 一个坏的 release 已经在生产环境里了,而修复要花好几个小时。
- 思路
- 立刻把上一个好版本放回去,之后再从容地修。
- 例子
- v43 在报错——一条命令就把 artifact v42 放回生产环境。
- 代价
- 需要带版本号的 artifact;数据库的改动比代码更难回滚。
- 关联
靠 Artifact 才行得通;内置于 Rolling update、Canary 和 Blue-green 之中。
Canary release——先给少数用户
在把新菜端给整场宴席之前,先让几位愿意尝鲜的客人试一口——如果味道不对,只有几个人注意到,你什么也没损失。
canary deploy 会先把新版本发给一小部分用户。如果他们那边的指标保持健康,你就把它推给所有人;如果不健康,你把它撤回,几乎没影响到任何人。它把一次 release 从「全赢或全输」的赌博,变成一个受控的、可逆的步骤,并让一次失误的爆炸半径保持很小。
- 问题
- 新版本里的一个 bug 会一下子打到所有用户。
- 思路
- 先把新版本交给一小部分流量,并观察指标。
- 例子
- 5% 的用户用上 v2;错误并不比 v1 多——比例逐步升到 100%。
- 代价
- 需要能按百分比拆分流量,并且要有好的指标来判断。
- 关联
- 不好就 Rollback;邻居——Blue-green 和 Rolling update。
Blue-green deployment——两套环境和一个开关
剧院里两个一模一样的舞台:演出在一个上面进行,同时另一个上面布置新的一场。到了合适的时机,观众只需转向第二个——而第一个仍然备好,万一要退回去。
Blue-green 保持两套生产环境,并把流量一次性地从旧的(blue)切到新的(green),需要时还能瞬间切回来。新版本在旧版本仍然服务用户的时候就部署好并检查完毕;然后一个开关——所有人就都在新版本上了。和 canary 一样,它把一次 release 变成一个受控的、可逆的步骤。
- 问题
- 在线切换到新版本有风险,而且难以撤销。
- 思路
- 两套完整的环境;流量一次性从旧的翻到新的。
- 例子
v2 在 green 里启动并检查完毕;路由器把 100% 的流量发给它;出问题——切回 blue。
- 代价
- 你要维持两套完整的环境——release 期间资源翻倍。
- 关联
- 瞬间 Rollback;与 Canary 不同,它一次性切换所有人。
Infrastructure as Code(IaC)——用文件描述你的服务器
一张蓝图,你能照着它把整栋房子原样重建出来;对比一栋凭记忆盖的房子,一旦烧毁,没人能把它复原。
同样的纪律也延伸到服务器本身。Infrastructure as code(IaC) 的意思是:用文件来定义你的服务器、网络和配置,而不是在控制台里手工点来点去。这样,你的整个环境就是带版本的、可审查的、可复现的——你能把它原样重建,改动也和代码一样走同一条 pipeline。它堵上了最后一个缺口:免得「有人手工改了个设置然后忘了」,把别处所有的严谨都一笔勾销。
- 问题
- 有人手工改了服务器的设置然后忘了——环境没法复现。
- 思路
- 用文件描述服务器、网络和设置,并通过同一条 pipeline 来改动它们。
- 例子
Terraform 文件描述一台服务器和一个数据库;改动要经过评审,再由 pipeline 应用。
- 代价
- 多学一门语言,再加一点纪律:不在控制台里手工做「快速修复」。
- 关联
- 经由 Pipeline 流转;与 Desired state 是同一种声明式的想法。
- 是不是自动化的——从 commit 到生产环境全程走 pipeline,而不是靠手工?
- CI 是不是 build 并 test 每个改动,让 main 始终可用?
- 你发布的是不是同一个 artifact 或容器,从 test 到生产环境完全一致?
- 你能不能立刻 rollback 到上一个好版本?
- 有风险的 release 是不是逐步推出——canary 或 blue-green,而不是一下子全上?
- 基础设施本身是不是代码——带版本、可复现?
安全的交付就是可逆的交付:把 pipeline 自动化,发布一个经过测试的 artifact,留一个一键 rollback,把有风险的改动逐步推出,并把基础设施定义成代码。
二十一个概念其实是一条线。一个改动通过 CI,变成一个 artifact,由 CD 交付,由编排器运行,而安全发布的各种技巧则在出问题时接住你。在这一切之下,是被写成代码的基础设施。
一张图:从 commit 到用户
一个包裹的路线:只打包一次,在仓库里检查,开车送出,交到手上——而且每一步都可以退回给寄件人。
从左到右读这张图:Commit → CI(build 和 test;红灯就停)→ Image(artifact,只构建一次)→ CD(release)→ Kubernetes(运行它并维持 desired state)。怎样安全地发布,由 Rolling update、Canary、Blue-green 和 Rollback 来决定。而 Infrastructure as Code 则是把同样的规则用到服务器本身:它们也要走 pipeline。
- 发布是无聊的——小、自动化、经常做,而不是一件罕见又吓人的事。
- 任何东西合并之前,CI 都是绿的,而且 main 始终可用。
- 你一路发布到生产环境的,是你测试过的同一个 artifact/容器。
- 一个坏的 release 只是一次快速的 rollback,而不是一场危机。
- 有风险的改动通过 canary 或 blue-green 推出,而你的基础设施是代码。
速查表:全部 21 个概念
deploy 这个难题
- Deployrelease
让新版本上线。靠手工——又慢、又吓人、又罕见。
- Works on my machineenvironment drift
由环境不同引起的 bug:同样的代码,不同的版本和设置。
- Boring releasessmall batches
小而频繁的 release,比罕见而庞大的更安全。
Continuous Integration
- Continuous IntegrationCI
每个改动在几分钟内被自动 build 并 test。
- Shift leftcatch it early
bug 发现得越早,修复得越便宜。
- Green main branchmain always works
只有通过 CI 的东西才能进入 main。
Continuous Delivery
- Continuous DeliveryCD
好改动的发布被自动化:deploy 的步骤就是代码。
- Delivery vs Deployment有按钮还是没按钮
Delivery——由人按按钮;deployment——直接上生产环境。
- Artifactbuild once, deploy everywhere
一个构建好的软件包流经每个环境——你测试的就是你发布的。
Pipeline
- PipelineCI/CD pipeline
自动化的流水线:build → test → deploy。
- Fail fastred / green
一个阶段变红,流水线就停;只有绿色才往下走。
容器
- Container应用 + 环境
应用连同它的环境——在哪里都跑得一样。
- Docker工具
让容器成为标准「箱子」的那个工具。
- Image vs Container食谱和菜
image 是冻结的蓝图;container 是它正在运行的实例。
Orchestration
- Kubernetes编排器
把容器分散到各台服务器上,盯着它们,重启它们。
- Desired stateself-healing, auto-scaling
你声明目标——系统让现实一直停在那里。
- Rolling updatezero downtime
一次替换几份副本——不停机。
安全的 deploy
- Rollback撤销
立刻放回上一个好版本。
- Canary release先给少数用户
新版本先发给一小部分用户。
- Blue-green deployment两套环境
流量一次性从旧环境翻到新环境——也能翻回来。
- Infrastructure as CodeIaC
服务器和网络用文件描述,并通过 pipeline 改动。
交付应该是无聊的:把路径自动化,发布同一个经过测试的 artifact,并且永远留一条退路。