Docker中CMD启动进程时应让主进程直接输出到stdout/stderr,避免shell重定向;日志由Docker原生驱动统一收集,必要时用tee分流或日志代理实现文件落盘。

在 Dockerfile 中使用 CMD 启动进程时,标准输出(stdout)和标准错误(stderr)默认会由 Docker 守护进程捕获并写入容器日志(可通过 docker logs 查看)。但若在 CMD 中直接做 shell 重定向(如 > /var/log/app.log 2>&1),会导致 Docker 无法收集日志,失去原生日志管理能力。因此,关键原则是:**让主进程直接向 stdout/stderr 输出,不自行重定向到文件;日志收集交由 Docker 或外部工具统一处理。**
避免在 CMD 中做文件重定向
以下写法是典型错误:
CMD ["sh", "-c", "python app.py > /var/log/app.log 2>&1"]
这会使所有输出写入文件,Docker 日志为空,且日志轮转、权限、持久化等问题需手动解决。
正确做法是确保应用本身输出到终端(即 stdout/stderr):
- Python 应用中不要调用
open(...).write()写日志文件,改用print()或logging.basicConfig(level=logging.INFO, stream=sys.stdout) - Node.js 使用
console.log()和console.error() - Java Spring Boot 默认已将日志输出到 stdout(logback 配置中
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"></appender>)
利用 Docker 原生日志驱动统一收集
Docker 提供多种日志驱动(json-file、syslog、fluentd、gcplogs 等),无需修改应用即可对接集中式日志系统。
- 运行容器时指定驱动:
docker run --log-driver=fluentd --log-opt fluentd-address=172.17.0.1:24224 myapp - 在
/etc/docker/daemon.json中全局配置:{"log-driver": "awslogs", "log-opts": {"awslogs-region": "us-east-1", "awslogs-group": "/myapp/prod"}} - 对
json-file驱动可限制日志大小与数量:--log-opt max-size=10m --log-opt max-file=3
需要落盘日志时的合规方案
某些场景(如审计要求日志必须本地留存)需同时满足 Docker 日志采集 + 文件落盘。推荐两种方式:
-
使用
tee分流(简单轻量):CMD ["sh", "-c", "python app.py 2>&1 | tee -a /var/log/app.log"]
注意:需确保/var/log/app.log所在目录可写,且该方式不支持自动轮转 -
引入日志代理(生产推荐):
在容器中同时运行应用 +rsyslog或logrotate+tail -F监听日志文件,再将内容转发至 stdout 或远程目标
例如:启动脚本中tail -F /var/log/app.log && exec python app.py > /var/log/app.log 2>&1,但更稳妥的是用supervisord或s6-overlay管理多进程
验证与调试技巧
部署后快速确认日志是否可被 Docker 正确捕获:
- 执行
docker logs <container_id></container_id>,应实时看到应用输出 - 加
-f参数持续跟踪:docker logs -f <container_id></container_id> - 检查日志驱动:
docker inspect <container_id> | jq '.HostConfig.LogConfig'</container_id> - 若日志为空,进入容器执行
ps aux | grep -v grep,确认主进程是否真的在前台运行(Docker 要求 PID 1 进程不 daemonize)

















