公众号旧文
从矿井里的金丝雀,到软件工程里的 Canary:一个词是如何穿越一百多年的?
#Canary
在软件工程里,我们经常会看到一个很有意思的词:
Canary
直译是金丝雀。
比如:
- Canary Release(金丝雀发布)
- Canary Deployment(金丝雀部署)
- Canary Worker(金丝雀工作节点)
- Canary Test(金丝雀测试)
如果第一次接触这个词,很容易觉得奇怪:
软件和金丝雀有什么关系?
原来这个词背后有一段来自矿井的历史。
而理解了矿工为什么带金丝雀下矿,就很容易理解为什么今天的软件工程师会把一个小规模试运行的节点叫作Canary。
一、矿工为什么要带金丝雀下矿?
过去的煤矿开采,最大的危险之一并不是塌方,而是有毒气体和可燃气体。
尤其是在通风条件不好的矿井中,可能积累:
- 一氧化碳
- 甲烷
- 缺氧环境
这些东西对于矿工来说非常危险。
问题是人类并不能总是及时发现这些危险。
很多有毒气体没有颜色,也可能没有明显气味。
矿工进入矿井之后,如果空气中出现危险气体,人在意识到危险之前,就可能已经受到影响。
于是,一个非常朴素但有效的方法出现了:带一只小动物下去。
而金丝雀就成为了最著名的预警器。
二、为什么偏偏是金丝雀?
金丝雀是一种体型很小的鸟。
它的新陈代谢比较快,对空气环境中的变化也比较敏感。
当矿井里的空气出现严重问题时,金丝雀可能比矿工更早表现出异常。
例如:
正常空气
↓
金丝雀正常活动
↓
继续工作
而如果空气质量恶化:
有害气体增加
↓
金丝雀首先出现异常
↓
矿工发现危险
↓
停止工作
↓
撤离矿井
所以金丝雀承担的并不是矿工工作。
而是:
提前发现危险。
这才是后来 Canary 这个概念最核心的含义。
三、这是一种非常早期的监控系统
今天我们谈监控系统,通常会想到:
CPU
内存
磁盘
网络
错误率
日志
Prometheus
Grafana
Alert
但矿工使用金丝雀的时候,并没有这些东西。
他们实际上是在做一件非常现代的事情:
用一个更敏感的观察对象,提前判断整个环境是否安全。
可以把它抽象成:
真实环境
↓
风险发生
↓
Canary 首先产生异常信号
↓
人类发现
↓
停止进一步行动
这和今天的软件工程中的 Canary 思想,已经非常接近了。
四、这个思想后来进入了软件工程
假设你维护一个大型系统。
现在有:
100 台服务器
你准备上线一个新的程序版本。
最危险的做法是什么?
直接:
100 台全部升级
如果新版本存在问题:
新版本 Bug
↓
100 台全部受到影响
↓
整个系统异常
这就相当于:
把100个矿工全部派进矿井,然后才发现里面有毒气。
于是工程师想到:
为什么不先拿一小部分机器试一下?
例如:
100 台服务器
↓
先升级 2 台
Server 1
Server 2
这两个节点就是:
Canary
接下来观察:
CPU 是否异常?
内存是否异常?
错误率是否增加?
请求延迟是否增加?
网络是否正常?
程序是否崩溃?
用户请求是否正常?
如果两个 Canary 一切正常:
Canary 正常
↓
扩大规模
↓
10 台
↓
30 台
↓
100 台
如果 Canary 出现严重问题:
Canary 异常
↓
立即停止扩大规模
↓
回滚
↓
分析问题
这就是:
Canary Release(金丝雀发布)
五、Canary 的核心不是少量,而是提前暴露风险
这里有一个很重要的区别。
Canary 并不是简单地说:
先启动几个。
真正的思想是:
在风险扩大之前,用少量样本验证整个系统。
例如:
传统方式:
100%
████████████████████████████████
↓
出问题
Canary:
2%
██
↓
发现问题 → 停止
或者
2%
██
↓
正常
10%
████
↓
正常
30%
██████████
↓
正常
100%
████████████████████████████████
所以 Canary 实际上是一种渐进式风险控制机制。
六、Canary Release 是怎么工作的?
一个典型的互联网服务可能有:
Load Balancer
↓
┌─────┼─────┐
↓ ↓ ↓
旧版本 旧版本 Canary
A A B
绝大多数用户仍然访问旧版本。
只有很少一部分流量进入新版本:
99% → Old Version
1% → Canary Version
然后持续观察。
如果:
错误率 正常
延迟 正常
CPU 正常
内存 正常
业务指标 正常
就逐渐扩大:
1%
↓
5%
↓
10%
↓
25%
↓
50%
↓
100%
如果出现:
错误率 ↑↑
延迟 ↑↑
崩溃 ↑↑
马上:
停止发布
↓
流量切回旧版本
因此,Canary 的本质其实可以概括成一句话:
让小部分实例先替整个系统踩雷。
七、Canary 不一定是服务器
这是理解这个概念非常重要的一点。
Canary 可以是:
- 一台服务器
- 一个容器
- 一个进程
- 一个 Worker
- 一部分用户
- 一部分流量
- 一组测试任务
只要它承担:
提前验证风险
的作用,都可以称为 Canary。
比如:
100 个 Worker
没有必要一次全部启动。
可以:
Scanner
↓
2 个 Canary Worker
↓
观察
↓
正常
↓
启动更多 Worker
这时候:
Canary Worker = 先承担小规模任务、用于验证整个 Worker 集群是否安全和正常的节点。
八、放到扫描系统里就非常容易理解了
假设你有一个扫描系统。
最终可能需要:
1 Scanner
↓
100 Workers
↓
扫描大量目标
如果一开始就启动 100 个 Worker,可能出现很多问题:
CPU 爆满
网络拥塞
丢包增加
扫描速度反而下降
VPS 被限速
连接数过多
目标网络开始丢弃请求
所以可以设计成:
Scanner
↓
2 Canary Workers
↓
观察
├── CPU
├── 内存
├── 扫描速度
├── 丢包率
├── 错误率
├── 网络响应
└── VPS 限速情况
如果正常:
Canary 正常
↓
扩大规模
↓
7 Workers
↓
继续观察
↓
动态扩容
如果异常:
Canary 异常
↓
停止扩容
↓
降低并发
↓
调整参数
这时候,Canary 这个词就非常准确。
因为这几个 Worker 就像矿井里的金丝雀:
它们不是为了完成全部工作,而是为了先告诉你这个环境到底安不安全。
九、Canary 和 Shadow 有什么区别?
在实际系统里,Canary 经常和另外一个概念一起出现:
Shadow(影子流量 / 影子实例)
两者虽然都可以用来降低风险,但目的不同。
Canary 更强调:
真实承担一小部分任务,然后观察结果。
例如:
10000 个请求
9900 → Old
100 → Canary
Canary 真正处理这100个请求。
而 Shadow 更像:
真实请求
↓
Old Version
↓
产生副本
↓
Shadow Version
Shadow 收到的是复制出来的请求,但通常不应该影响真实业务。
所以可以简单理解:
Canary:
少量真实任务
↓
观察真实结果
Shadow:
复制真实任务
↓
让新系统偷偷跑一遍
↓
观察结果
因此:
Canary 是小规模真实运行,Shadow 更接近无风险旁路验证。
十、为什么这个词这么形象?
因为它实际上保留了最初矿井里的逻辑:
矿井时代
矿工
↓
金丝雀
↓
发现危险
↓
停止深入
软件工程
完整系统
↓
Canary
↓
发现异常
↓
停止扩大部署
两者的共同点只有一个:
不要让所有人一起承担未知风险。
先让一个小对象进入未知环境。
如果它安全:
扩大
如果它危险:
停止
十一、Canary 背后的工程哲学
所以,Canary 真正值得学习的,并不是一个英文单词。
它代表了一种非常重要的工程思想:
面对未知风险,不要一次性下注。
例如:
你有100台服务器,不要一次升级100台。
你有100个 Worker,不要一次启动100个。
你有一个新算法,不要一次让100%的用户使用。
你有一个新版本,不要一次切100%的流量。
而是:
1%
↓
观察
↓
5%
↓
观察
↓
10%
↓
观察
↓
30%
↓
观察
↓
100%
这就是工程上的:
渐进式验证。
十二、从一只鸟,到今天的云计算
非常有意思的是,一个源自煤矿的词,最后进入了现代互联网。
最开始:
金丝雀
↓
矿工
↓
危险预警
后来:
Canary
↓
软件部署
↓
小规模验证
↓
风险控制
再后来:
Canary Release
Canary Deployment
Canary Testing
Canary Worker
Canary Traffic
你今天在 Kubernetes、微服务、CI/CD、云计算平台里看到的 Canary,本质上仍然是在做一百多年前矿工做的那件事情:
先派一个金丝雀进去看看。
只不过以前金丝雀告诉矿工:
这里可能有毒。
而今天的 Canary 告诉工程师:
这个版本可能有问题。