改运维脚本前先 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-nginx、app-dev-gitea 也没了——因为在「修正」的同时,一并把前缀从 dev 改成了 prod。
根因
repo 里的模板编码的是「部署意图」,服务器上的容器名记录的是「历史事实」。这两者可以永久漂移,且没有任何机制会报警。
具体到这个案例:该服务器是很早之前以 CUSTOMER_CODE=dev 部署的,所以所有容器的前缀都是 app-dev-*。后来 .env 里的逻辑标识符改成了 prod,但容器从未重建,真实前缀一直停在 dev。
脚本里那个「看起来错的」app-dev-admin-api 其实是完全正确的——它和服务器现实对齐。而按模板推断出来的 app-prod-admin-api 才是幻觉。
docker-compose.yml 里 container_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 user@server 'docker ps',用命令输出作为事实基准。若脚本名与服务器一致,在脚本里加一行注释说明历史原因(如 # 历史以 CUSTOMER_CODE=dev 部署,逻辑环境已改名但容器未重建),然后收手。不要在「顺手修一下命名不一致」这个冲动下引入生产回归。
修完名字后采集器仍然没数据,不要立刻怀疑名字还是错的。先检查容器是否有日志输出:
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,用服务器事实校准,而不是用模板推断覆盖服务器现实。