docker容器的stdin/stdout/stderr由daemon在启动时统一接管并桥接,非容器内重定向实现;stdout/stderr默认写入json日志文件,stdin保持打开供attach或exec使用;tty决定终端控制能力;logs读历史、attach实时接管主进程i/o、exec启动新会话;shell重定向会切断docker日志捕获,正确做法是应用直写stdout/stderr或软链至/dev/stdout。

Docker 容器的标准输入(stdin)、标准输出(stdout)和标准错误(stderr)流,并不是靠“重定向命令”在容器内部实现的,而是由 Docker daemon 在容器启动时统一接管并桥接的——本质是宿主机与容器进程之间的一套 I/O 通道映射机制。
容器启动时的 I/O 初始化
当你执行 docker run,Docker daemon 会为容器创建一个新的进程(如 python app.py),同时为其分配一个伪终端(tty)或非交互式 stdin/stdout/stderr 文件描述符。这些描述符默认连接到 Docker 的日志驱动和 I/O 管理子系统:
- stdout 和 stderr 被写入容器日志缓冲区(默认存为 JSON 格式文件:
/var/lib/docker/containers/<id>/<id>-json.log</id></id>) - stdin 默认保持打开,供
docker attach或docker exec -i输入使用 - 是否启用 tty(-t 参数)决定终端控制能力:无 -t 时,部分程序(如
readline)可能拒绝读取 stdin
宿主机侧的 I/O 接入方式
外部访问容器输出或向其输入,有三种主流路径,各自作用层级不同:
- docker logs:只读取已写入日志文件的内容(即历史 stdout/stderr),不触发实时流,也不影响容器进程本身
-
docker attach:直接复用容器主进程的原始 stdin/stdout/stderr 文件描述符,实现“实时接管”。输入会广播给所有附加终端,退出需用
Ctrl+P Ctrl+Q -
docker exec -i/-t:启动新进程(如
/bin/sh),其 stdin/stdout/stderr 被 Docker 桥接到宿主机终端 —— 这是新会话,不影响主进程,也无法替代主进程的输入流
避免破坏 I/O 通路的常见误区
很多用户试图在 CMD 或启动脚本里做 shell 重定向(如 python app.py > log.txt 2>&1),这反而会切断 Docker 对 stdout/stderr 的捕获:
- 使用 exec 形式 CMD(
["python", "app.py"])时,shell 重定向语法完全无效,参数会被当作字符串传给 Python - 即使改用 shell 形式(
CMD python app.py > log.txt),日志就只写进容器内文件,docker logs将返回空,集中采集器也收不到 - 正确做法是让应用直接向 stdout/stderr 打印,或把服务日志软链到
/dev/stdout(如 Nginx、Supervisor 的典型配置)
日志流向的可配置出口
Docker 不处理日志投递,但提供插拔式日志驱动,让 stdout/stderr 流自动发往不同后端:
-
--log-driver=fluentd --log-opt fluentd-address=...:直连 Fluentd 集群 -
--log-driver=syslog --log-opt syslog-address=udp://...:对接 rsyslog -
--log-driver=awslogs:推送到 CloudWatch Logs - 默认
json-file驱动支持轮转:--log-opt max-size=10m --log-opt max-file=5











