容器跑起来之后,最容易被忽略的 6 个运维细节
docker ps 显示容器为 Up,只能说明进程还在,不能说明服务可用、数据安全、日志可查、变更可控。
容器化降低了部署门槛,但运维问题并不会因此消失。很多故障不出在镜像构建,而是出在启动之后的细节。
1. 容器活着,不代表应用活着
这是最常见的误判。
进程没退出,容器状态就是 Up。但应用可能已经卡死、线程池阻塞、数据库连接耗尽,只是主进程还没退出。
健康检查至少要区分两件事:
- 进程是否还在
- 服务是否还能正常响应
如果镜像只定义启动命令,没有定义健康检查,编排系统通常只能判断“容器没死”,无法判断“服务是否可用”。
2. 日志输出了,不代表日志可用
把日志输出到标准输出是常见做法,但只做这一步通常不够。
问题通常集中在三个方面。
日志没有结构
如果日志全是自然语言,排查时很难过滤和聚合。至少应包含:
- 时间
- 级别
- 请求标识
日志没有轮转
如果日志仍然写入容器内文件,又没有清理策略,宿主机磁盘最终可能被写满。
日志只有错误,没有上下文
只有报错堆栈,缺少关键上下文时,问题定位会很慢。错误发生前的请求参数、依赖调用结果、重试信息,往往同样重要。
3. 数据卷挂上了,不代表数据安全
很多人会把“持久化”直接理解成“数据安全”,这是两个不同的问题。
数据卷只解决了:
- 容器删除后,数据不随容器一起消失
它并不直接解决:
- 备份
- 误删恢复
- 数据损坏恢复
- 迁移验证
这几个概念最好分开看:
- 持久化:数据不会随容器生命周期一起消失
- 备份:出问题后是否有可恢复副本
- 恢复演练:备份文件是否真的能恢复成功
如果只有数据卷,没有备份和恢复验证,数据安全仍然没有得到保证。
4. 镜像固定了,不代表依赖稳定
容器的可重复性依赖于版本是否真正固定。
常见问题有两个:
- 基础镜像使用
latest - 构建时拉取外部依赖,但没有锁定版本
这种情况下,今天和下周构建出的镜像可能并不相同。表面上用了容器,实际仍然存在环境漂移。
更稳妥的做法是:
- 基础镜像使用明确版本
- 依赖版本锁定
- 构建步骤尽量可重复
理想状态下,同一份构建配置重复执行,结果应保持一致。
5. 资源限制没设,问题就会扩散
容器默认会尽量使用可用资源。如果不加限制,单个服务的异常可能拖垮整台机器。
最容易被忽略的设置包括:
- 内存限制
- CPU 配额
- 文件句柄数
- 重启策略
容器不会天然带有清晰边界,边界需要显式设置。没有限制时,局部故障很容易扩散成宿主机级别的问题。
6. 配置拆出去了,不代表变更可控
把配置从镜像中拆出来是正确方向,但配置分散后,新的问题会出现:很难确认线上到底在使用哪一份配置。
至少要保证这些信息可追溯:
- 配置从哪里来
- 谁改过
- 改动何时生效
- 回滚时回到哪一版
有些故障并不是代码问题,而是配置变更没有留下足够记录。容器让变更更快,也让误操作更快。
最容易被跳过的是验证
很多文档会重点讲构建、编排、发布,但对验证写得不够。运维里真正关键的问题不是“是否配置过”,而是“是否验证过”。
可以从这些检查开始:
- 重启容器后,服务是否自动恢复
- 宿主机重启后,挂载和依赖是否仍然正常
- 磁盘接近满时,日志和数据会怎样表现
- 健康检查失败后,是否出现误重启或反复抖动
- 备份是否做过恢复验证
这些检查并不复杂,但是否有效需要实际验证;未经验证的结论都只能视为“未验证”。
如果后续继续展开,单机 Docker 环境下的基础可观测性会是一个更值得单独处理的话题。