TT Lab
开始
学习 学习路径 课程

FDE综合实战:仓库收到了三次相同订单

1.5.0 显示已受理,仓库里却没有订单

在 TT Lab 中继续学习

目标

制作一个脚本:用 releases/<版本> 和 current 符号链接进行部署,经过健康检查和冒烟测试,失败时回滚到上一个版本并留下记录。清理保留时,要保护 current 和上一个版本。

为什么重要

健康检查问的是进程是否活着,冒烟测试问的是一笔业务能否走完。客户公司上次的部署只问了前一个问题就结束了,结果一整夜都在丢订单。 把版本拆成目录,用一个符号链接切换,回滚也就成了同一个动作。但如果切换的瞬间 current 消失了,或者旧进程替新版本亮起绿灯,或者清理脚本把要回去的版本删掉了,那么即使有这套结构也还是会出事故。 评分器每次都会用不同的版本号,构建出正常的、只在冒烟测试中失败的、一启动就崩溃的、启动很慢的构建包,来运行你的脚本,并重新测量实际响应的版本和符号链接,而不是日志措辞。

预计 60 分钟。请在默认会话结束前用“+时间”延长(最长 180 分钟)。会话结束后 /root 中的文件会消失,所以脚本要另外保存。

步骤

  1. 把构建包 orderapp-1.4.0.tar.gz 解压到 /root/site/releases/1.4.0,并让 /root/site/current 符号链接指向该目录。
  2. 制作 /root/release/switch.sh ROOT 版本。拒绝不存在的版本,并且一次性切换,不出现 current 消失的瞬间。
  3. 制作 /root/release/deploy.sh ROOT 构建包 PORT。让正常的构建包通过解压、切换、替换旧进程、健康检查(确认 version)、冒烟订单、写入 deploy.log 的全部流程。
  4. 当 deploy.sh 的冒烟测试失败时,把 current 回滚到上一个版本并重新启动该版本,记录 rolled_back、reason smoke,并以退出码 2 结束。
  5. 对 deploy.sh 的健康检查:一启动就崩溃的版本,以 health 为原因回滚;延迟 2.5 秒才启动的正常版本,要等待后完成部署。等待上限为 10 秒。
  6. 制作 /root/release/prune.sh ROOT KEEP。保留 mtime 最新的 KEEP 个,但把 current 和上一个版本从待删除列表中排除。
  7. 确认连续进行四次部署(最后一次冒烟测试失败)和保留 1 的清理后,记录、剩余版本和响应的版本依然正确。
  8. 用 deploy.sh 把客户的 1.5.0 部署到 /root/site(端口 8480),查看结果,并把事件记录到 /root/release/incident.json。

参考

手动解压第一个版本并建立 current

把构建包 1.4.0 解压到 /root/site/releases/1.4.0,并让 /root/site/current 符号链接指向那里。

用 tar 的 -C 决定解压位置。current 必须是用 ln -s 建立的链接,而不是目录拷贝。相对路径链接(releases/版本)即使把整个部署根目录搬走也不会失效。

一次性切换 current 的切换脚本

制作 /root/release/switch.sh ROOT 版本。对不存在的版本要以非 0 的代码拒绝,并且 current 的切换不能出现消失的瞬间。

rename(2) 会原子地替换已有名称。在同一目录创建一个临时链接,再把它移到 current 之上。mv 有一个选项,即使目标指向目录,也不会把它放进该目录里面。如果使用 ln,别忘了 -n。

解压、切换、启动,直到冒烟测试

制作 /root/release/deploy.sh ROOT 构建包 PORT,部署正常的构建包,并在 deploy.log 中留下一行 deployed。

版本从 tar 包内的 VERSION 读取。旧进程用 shared/app.pid 停掉,新应用用 nohup 和输出重定向在后台启动。要检查健康检查响应中的 version 是否为新版本,以及冒烟订单(SMOKE- 前缀)能否用 GET 读回。用 jq -cn 生成记录,就不会出引号方面的错误。

冒烟测试失败就回滚到上一个版本

让 /root/release/deploy.sh 在冒烟测试失败时切换回上一个版本并重新启动,记录 rolled_back(reason smoke),然后以退出码 2 结束。

切换之前要把 current 指向的版本记成 previous,才知道该回到哪里。回滚也用 switch.sh 来做。失败的 releases/版本 要留作调查用。

崩溃的版本要快,启动慢的版本要等

让 /root/release/deploy.sh 的健康检查最多等待 10 秒,但进程一旦死掉就立刻以 health 为原因回滚。

只失败一次就放弃的话,会把启动较慢的正常版本回滚掉。反过来,已经死掉的进程也没有必要再等 10 秒。kill -0 PID 不会发送信号,只确认进程是否存在。

清理时不要删掉要回去的版本

制作 /root/release/prune.sh ROOT KEEP。除 mtime 最新的 KEEP 个之外全部删除,但保留 current 和上一个版本。

回滚之后,current 不是最新的。上一个版本是 deploy.log 中 result 为 deployed 且 version 等于 current 的最后一行的 previous。ls -t 按修改时间从新到旧列出。

部署四次再清理,记录也要对得上

让 /root/release/deploy.sh 和 /root/release/prune.sh 即使连续进行四次部署(最后一次冒烟测试失败)和保留 1 的清理,记录与实际状态也能对得上。

被回滚的那次部署的 previous,是回到的那个版本。之后再清理,应当剩下最新的 1 个(失败的版本)以及 current 和上一个版本。请在临时部署根目录中亲自连续运行试一试。

部署客户的 1.5.0 并留下事件记录

用 deploy.sh 把 1.5.0 部署到 /root/site(端口 8480),并把结果用 failed_version、restored_version、reason、deploy_log_line、health_passed 写入 /root/release/incident.json。

deploy_log_line 是回滚记录在 /root/site/deploy.log 中的第几行(从 1 开始)。health_passed 写的是这个版本是否通过了健康检查——请根据 app.log 和记录的 reason 来判断。评分器会把 incident.json 与 deploy.log、current 和构建包原件核对。