返回博客

改运维脚本前先 ssh 确认容器名——别让「模板意图」覆盖「服务器现实」

某天排查日志采集器,发现监控面板里只剩两个模块在上报,另外两个模块的数据莫名消失了。翻开日志采集脚本一看,容器名列表赫然写着 app-dev-admin-api——但 docker-compose.yml 模板明明用了变量 container_name: app-{{CUSTOMER_CODE}}-admin-api,而 .env 里的逻辑环境标识符叫 env=prod……看起来应该是 app-prod-admin-api 才对,感觉是历史遗留的写法写错了。

于是照着模板「修正」了这个名字,部署。结果:原来上报正常的两个模块也消失了。

现象

日志采集器脚本遍历一份硬编码的容器名列表,逐个用 docker logs 拉取日志并推送到日志汇聚服务。改完名字部署后:

[shipper] skip app-prod-admin-api (docker logs error: No such container)
[shipper] skip app-prod-nginx (docker logs error: No such container)

原本工作的 app-dev-nginxapp-dev-gitea 也没了——因为在「修正」的同时,一并把前缀从 dev 改成了 prod

根因

repo 里的模板编码的是「部署意图」,服务器上的容器名记录的是「历史事实」。这两者可以永久漂移,且没有任何机制会报警。

具体到这个案例:该服务器是很早之前以 CUSTOMER_CODE=dev 部署的,所以所有容器的前缀都是 app-dev-*。后来 .env 里的逻辑标识符改成了 prod,但容器从未重建,真实前缀一直停在 dev

脚本里那个「看起来错的」app-dev-admin-api 其实是完全正确的——它和服务器现实对齐。而按模板推断出来的 app-prod-admin-api 才是幻觉。

坑:用 repo 模板推断运行时名称,不验证服务器

docker-compose.ymlcontainer_name: app-{{VAR}}-* 给人「VAR 就是当前环境标识符」的强烈暗示。但已部署的服务器上,VAR 的渲染值取决于那次部署时传入的值,而不是现在 .env 里写的值。

历史漂移场景:

  • 两次部署之间 .env 改过 VAR 的值
  • 有人手动 docker rename 过容器
  • 模板本身在初次部署后才引入 VAR 占位符

结论:现有脚本里的名字如果与服务器一致(即使和模板不一致),它就是对的。不要「修正」它。

正解

改任何引用容器名、systemd unit 名、k8s deployment 名的运维脚本之前,先在目标服务器上跑一次列表命令,用真实结果对照脚本里的值。

第一步:拿到服务器实际运行时名称

# Docker
ssh user@server 'docker ps --format "{{.Names}}\t{{.Status}}"'
 
# Kubernetes
kubectl get pods,deploy,svc -n <namespace> -o wide
 
# Systemd
ssh user@server 'systemctl list-units --type=service --no-pager | grep <pattern>'

第二步:对照脚本现有值

| 脚本里写的 | 服务器实际 | 结论 | |-----------|-----------|------| | app-dev-admin-api | app-dev-admin-api (Up 6h) | 一致,不要动 | | app-dev-nginx | app-dev-nginx (Up 6h) | 一致,不要动 |

如果现有脚本和服务器一致,即便它和模板的「预期命名」不符,保留原脚本,加注释解释历史原因才是正确的动作。

第三步:如果真的需要改(如增加多环境支持),参数化而非硬替换

# 默认值对齐服务器现实;其他环境通过 env-file 覆盖
CONTAINERS=(${CONTAINERS:-app-dev-admin-api app-dev-nginx gitea})
 
# 用 sed 提取稳定的短名,避免前缀漂移污染 metric label
short=$(printf '%s' "$c" | sed -E 's/^app-[a-z0-9]+-//')
# app-dev-admin-api  → admin-api
# app-prod-admin-api → admin-api(同样适用)
正解:ssh 先看,再改,最后加注释解释漂移

改脚本前先 ssh user@server 'docker ps',用命令输出作为事实基准。若脚本名与服务器一致,在脚本里加一行注释说明历史原因(如 # 历史以 CUSTOMER_CODE=dev 部署,逻辑环境已改名但容器未重建),然后收手。不要在「顺手修一下命名不一致」这个冲动下引入生产回归。

附:docker logs 无输出 ≠ 容器名还是错的

修完名字后采集器仍然没数据,不要立刻怀疑名字还是错的。先检查容器是否有日志输出:

ssh user@server "docker logs --since=1h <container> 2>&1 | wc -l"

若容器健康但输出 0,说明应用根本没往 stdout 写日志(pm2/pino 直接写文件、docker-compose.yml 设了 logging.driver: none 等),这是另一个独立问题,不是容器名问题。

一句话外卖

repo 里的部署模板是「意图」,服务器上的容器名是「历史」——改运维脚本前先 docker ps,用服务器事实校准,而不是用模板推断覆盖服务器现实。