1. 每次部署都卡住的服务器,问题出在哪?



在运营个人项目时,经常会碰到资源有限的环境(如 GCP 超小实例、树莓派等)。 我维护着多台服务器,从用于 AI 推理和训练的高配机器,到仅能勉强跑 DNS 的低配 VM,应有尽有。 尤其喜欢 Raspberry Pi 5,24 小时运行电费几乎可以忽略,性能也相当可靠。 如果把其他服务器比作被管理的牲畜,Raspberry Pi 更像是让人倍感亲切的宠物。

然而,让这只“小宠物”承担太多任务后,部署时 CPU 会瞬间飙到 100%。根源是 Celery Worker。 在进行无停机部署时,同时启动 Blue 与 Green 两套环境会导致 Worker 数量翻倍,系统承受不住。 减小 Worker 数会让处理速度变慢,保留原样又会让服务器崩溃,进退两难。

低配 PC 上的自动化脚本象征图

为了解决这个问题,我编写了一套 自定义 Blue‑Green 部署脚本,在尽可能降低资源消耗的同时,确保系统的可靠性。


2. 解决思路:省资源、保稳定

传统的 Blue‑Green 部署会同时运行两套环境,而我采用了以下策略来 降低 CPU 负载

  1. 先行停止后台服务(Celery):在新版本 Web 服务器启动前,先停止旧版本的重量级后台任务(Worker、Beat),为新进程腾出 CPU。
  2. 分阶段启动:先启动 Web + Redis,完成健康检查后再逐步启动其余服务。
  3. 人工确认环节:所有自动化完成后,由管理员亲自确认,确认无误后才删除旧版本,这一步相当于 “最终批准”。

在实际分析中,Celery Worker 在初始化后会立即接收并执行任务,导致 CPU 瞬间被压垮。虽然降低 Celery 的并发数能够缓解,但会牺牲异步任务的处理速度,这并非我们想要的。

于是,我想到在重新部署时,通过短暂暂停旧的 Celery 实例来释放 CPU,随后再部署新代码,从而实现真正的无停机。


3. 核心代码一览



完整代码可在我的 GitHub 仓库 查看。下面展示几段关键实现。

① 使用 Docker Compose 实现项目隔离

通过 docker compose -p 参数,即使使用同一份 compose 文件,也能生成名为 bluegreen 的独立项目(命名空间)。

dc() {
  # 动态指定项目名称(-p)实现环境隔离
  docker compose -f "$COMPOSE_FILE" "$@"
}

② 严格的健康检查(Health Check)

只有确认新版本完全可用后,才会切流量。

health_check() {
  # 对指定端口发起 10 次 200 OK 检查
  if curl -fsSIL --max-time "$HEALTH_TIMEOUT" "$url"; then
    ok "Health check passed"
    return 0
  fi
}

③ 失败时快速回滚

如果新版本出现问题,立即恢复旧版服务,避免用户感知到故障。这里使用 stop 而非 rm -f,可以在紧急情况下快速重新启动旧版的 Celery Worker 与 Beat,抢回 CPU。


4. 运营技巧:“确认后删除”的艺术

脚本的最后一步不会自动删除旧版,而是向管理员展示提示信息:

“部署已成功。请自行登录检查,确认无误后复制下面的命令清理旧版。”

这道安全阀能够防止自动化遗漏的 1% 人为错误。


5. 结语

在受限资源下实现最佳效率的思考都凝聚在这套脚本里。虽然代码并不华丽,但我已经在实际项目中使用并深感满意,也相信其他开发者会有相同的需求。 希望这套脚本能帮助到同样面临资源瓶颈的中小型开发者们。

相关链接: