五次"绿色通过"都是假象:部署、回滚、负载测试中的隐蔽失败
五次绿色支票都毫无意义
这些都是我同一个项目的,都是我的,而且都在两周内找到。连接它们的是,每次监测都显示正常。没有静音,没有降级,没有颤动。绿色。
他们都没有被警报触发。每个人都是通过查看实际的伪影来发现的:磁盘上的文件、表格中的行、运行容器实际构建的镜像ID。这是我想记住的部分,因为我反复看了五次才看到。
1. 三周的部署,但一无所获
在检查漏洞修复是否成功时发现的。没有。盒子上的编译处理程序日期早了三周,期间的每次部署都报告成功。
链条:
- 每次部署都留下了之前的镜像悬而未决。其中31个镜像累积了,35个镜像和14GB存储在一个20GB硬盘上,硬盘容量达到100%。
docker compose pull然后失败了no space left on device.- 远程命令列表没有
set -e所以失败并没有阻止任何东西。 docker compose up -d重新开始了陈旧的本地形象。- 容器生成时很健康,因为旧代码本身就很健康。
- 烟雾测试进行了民调
/api/health/ready, 得到status: ok,并通过了。 - 部署报告成功。
所有信号都是绿色,什么都没发货。健康检查证明有东西在运行。但这不能证明正确的设备在运行,这两件事在三周里都被打破了,什么都没注意到。
立刻解决的办法是set -e,拉取前的修剪,以及运行容器的图像ID等于刚拉取的图像的断言。断言才是关键,因为set -e解决了该原因,断言捕获了该类。
真正的解决办法是让失败无法存活。部署现在写了一个组合覆盖,将两个服务钉在sha-,因此失败的拉取不会留下该标签下可运行的图像,且up -d失败了,而不是默默地为之前的版本服务。
值得注意的是,这个变化破坏了什么,因为这三者都隐形了。图像断言是与:latest,该组合已停止拉取,因此每次成功部署都失败。回滚无声地死了,因为它重新标记了:latest而且 Compose 不再读取那个标签,意味着烟雾测试失败会“回滚”到同样的损坏构建并报告恢复。
修剪也停止了恢复,因为被替换的镜像现在会保持标签,而不是悬空,磁盘每次部署又增加了大约 1.4GB。
用不可变的标签替换移动标签意味着要审计所有引用该移动标签的内容。这些引用不会大声失败。
2. 一个附加到未写入文件的部署
这次是我直接造成的。一个从容器开始的迁移解耦变更让部署做了:
grep -q '^MIGRATE_ON_START=' .env || echo 'MIGRATE_ON_START=false' >> .env
/opt/tellsight/.env没有尾随新线,所以附加物焊接到了最后一行上:
ENCRYPTION_KEY=<64 chars>MIGRATE_ON_START=false
配置验证拒绝了80字符密钥,API崩溃,生产环境服务了500个字符。
这是唯一一个警报确实正确触发的情况,而且是第一次真正的触发,而不是演习。但回应毫无用处,这才是真正的教训:回滚是重新钉住镜像,而不是恢复文件。
镜像回滚到之前的构建,API 依然崩溃,因为损坏依然存在于.env,除了回滚控制之外。
我做过回滚演练。其中两次,故意失败,多次回溯构建。每次钻探都测试了一个坏图像。没有一个测试过坏文件,而这正是钻头无法显示的漏洞,因为它只练习你认为要分阶段的失败。
两个错误叠加在一起:从自动化部署中变更一个手工管理的秘密文件,以及用一个裸echo >>那是假设后面有换行。解决方法是停止做第一个换行。
该标志现在存在于镜像上,Dockerfile 的生产阶段设置,因此部署永远不会被处理.env而且该价值在回滚后免费存活。
3. 负荷测试,404分通过
k6 脚本一直在请求/api/datasets,API 从未提供过这条路径。它已经报告该流程已通过好几个月。
原因在于:认证中间件会在整个受保护挂载中回答401,然后才开始路由。当真正的认证在前时,匹配的路径没有路径,和匹配的路径无法区分。
两者都返回401,检查显示响应不是5xx。
解决方法是通过测试直接解析k6脚本中的请求路径,启动带有认证截图的路由器,并且断言没有任何路径是404。从脚本中解析路径而不是在测试中列出是有意为之,所以在负载测试中添加一个没有路由的流程,反而是测试失败,而不是安静通过。
4. 自发布以来被拒绝的分析事件
对生产分析表的一次查询:82dashboard.viewed排位,完全没有任何主题活动。
两个主题呼叫网站都会触发trackClientEvent('theme.changed', ...).该活动名称从未注册ANALYTICS_EVENTS,路线从中构建其允许列表Object.values(ANALYTICS_EVENTS)其他问题回答400。trackClientEvent忽略回复,所以切换功能正常,事件从发货那天起就被默默拒绝了。
有一个单元测试。它断言客户打电话trackClientEvent,传输被模拟,因此从未捕获到任何内容。被模拟客户端证明调用了函数。只有末端证明调用被接受,而合同的两半存在不同的包中,如事件名称和服务器端允许列表,测试套件中没有任何内容加入它们。
解决办法是不注册那个事件。trackClientEvent现在AnalyticsEventName,因此未注册名称类型检查失败。
5. 一个不可能得分的评估 1.00
汇总评估会根据模型输出与手工制作的灯具进行评分。没有任何工具根据生成这些工具的公式来评分。所以一个灯具可以存储任何输入无法生成的数字,并且永远获得满分1.00,这正是它所做的。
三场比赛中有五项统计数据错误。其中两项数据颇具趣味:
收入为48000,来自16000的固定成本,利润率为20%,公式为80000。模型获得了真实五分之一的缺口,并忠实地重复了这个差额,这正是忠实度所衡量的。
趋势显示,从1200和800端点下降了18%,即下降了33%。这两个数字都在同一提示行上,因此模型收到了自相矛盾的句子,而一个正确计算出33%的模型则被评为无支持,且基于错误的实地事实。
现在有一个测试将每个计算函数的不变量对其夹具进行断言,并且该测试运行在CI中,而评估则没有。任何带有手工制作的基础真实值的评估都需要一个。
裁判看不到该计量是不可能的,分数也看不出来。
它们的共同点
这些检查都是在看代理,而不是监视那个东西。
健康检查证明进程正在运行,而不是说明它就是你发货的那个版本。401证明是授权运行,而不是路线存在。模拟传输证明呼叫已发出,但没有什么设备接受。
忠实度评分证明摘要与灯具匹配,而不是灯具可能存在。回滚钻机证明机制触发,但不能修复你实际遇到的故障。
在每种情况下,代理和该物品在支票开出时是等价的,后来渐渐分开,支票继续向代理报告。
那些能通过直接与设备进行比较的修复。断言运行中的映像ID,而不是容器是否健康。解析加载测试中的路径,而不是某个人维护的列表。
把事件名设为类型,而不是与服务器允许列表约定一致的字符串。测试灯具的算术,而不仅仅是模型对它的忠实度。
找到全部五种的方法是便宜的,我默认也不会这么做:去远端看看。查询表,读盒子里的文件,检查容器实际运行的是什么。这些工具只要我决定运行一次,就只需一个命令。