Docker run 加 -t 后,标准错误混进标准输出文件
Docker 分配伪终端后,应用 stdout 和 stderr 会合并。移除 -t、按需保留 -i,并区分应用日志、客户端错误和已有容器的 TTY 配置。
docker run -t 运行的程序报了错,宿主命令末尾的 2>error.log 却没有收到这条错误;把标准输出写进 JSON 文件时,错误消息反而混了进去。去掉 -t 后,两路输出又能分开。
这个现象来自伪终端。启用 -t 后,容器程序的标准输出和标准错误接入同一个 TTY,Docker 得到的是合并后的输出流。宿主 shell 再做重定向时,已经没有足够的信息把应用原先的两路输出还原出来。
下面针对 Linux 容器与 Docker CLI 的输出采集。前提是应用本身分别写 stdout 和 stderr;如果应用把所有日志主动写到 stdout,去掉 TTY 也不会替它重新分类。
先确认命令是否分配了 TTY
检查实际执行的命令,包括脚本中的组合选项 -it。-i 保持标准输入开放,-t 分配伪终端,两者用途不同。需要通过管道输入数据的程序,可以保留 -i,不必同时开启 -t。
已有容器可以只读检查创建时的设置:
docker inspect --type container --format '{{.Config.Tty}}' app-container
app-container 是待查容器的名称。输出 true 说明该容器创建时分配了 TTY。检查 docker exec 的单次执行时,还要查看 exec 命令自己的 -t 参数,不能只凭容器创建配置推断这次 exec 的输出连接。
再对照应用有没有 2>&1、日志统一输出到 stdout 等设置。TTY 是一种合并来源,并不是所有日志混杂的唯一原因。
采集两路输出时,移除 -t
下面用不读取业务数据的命令展示差异。在本机新建的测试目录中使用未占用的文件名,避免 > 覆盖旧日志。示例镜像本地不存在时会发生拉取;拉取失败、daemon 连接错误等客户端诊断也可能进入 stderr,应与程序输出区分。
docker run --rm -t busybox:1.36 sh -c \
'printf "%s\n" app-output; printf "%s\n" app-error >&2' \
> tty-out.log 2> tty-err.log
docker run --rm busybox:1.36 sh -c \
'printf "%s\n" app-output; printf "%s\n" app-error >&2' \
> plain-out.log 2> plain-err.log
第一条命令中,app-output 和 app-error 都应进入 tty-out.log。第二条没有分配 TTY,app-output 应进入 plain-out.log,app-error 应进入 plain-err.log。这组标记用来区分程序的两个通道,不以错误文件是否完全为空来判断 Docker 是否正常。
检查文件:
cat ./tty-out.log ./tty-err.log
cat ./plain-out.log
cat ./plain-err.log
两次容器都在退出后由 --rm 自动删除,输出文件留在本机。不要把真实应用的凭据、环境变量或整份配置当作测试输出。
如果实际任务需要标准输入,将原来的 docker run -it ... 改成 docker run -i ...,并保留原有输入管道。只需要运行批处理而不输入数据时,可以同时省略这两个选项。真实部署参数应逐项保留,不能用上面的测试命令替换带有卷、网络或用户设置的业务命令。
已合并的输出不能靠宿主重定向拆开
为启用 TTY 的命令加 -a stderr,不会重新得到独立的应用错误通道。把宿主的 2>&1 移到另一个位置,也只是在调整 Docker CLI 当前输出的去向,无法恢复进入 TTY 前的流标记。
在容器内给应用加 2>/dev/null 能让错误不再进入 TTY,但这丢弃了错误信息,并没有实现“在宿主分别保存 stdout 和 stderr”。需要完整诊断时,不应把这种静默处理当成修复。
对于创建时就使用 TTY 的容器,docker logs 读取到的应用输出同样已经合并。修改收集端重定向不会让旧日志重新具有可靠的原始通道信息。若长期服务需要按流分类采集,应修改创建配置,在允许重建的维护窗口创建不分配 TTY 的容器;普通 restart 不会改变这项创建设置。
去掉 TTY 后,核对应用输出行为
有些程序通过终端检测决定是否着色、是否显示进度条,以及怎样缓冲输出。移除 -t 后,输出格式或出现时机可能改变。应使用应用自身支持的批处理、日志刷新或颜色选项处理这些变化,不要为了彩色日志重新加回会合并通道的 TTY。
若 stdout 被下游当作 JSON、CSV 等机器输入,验收时既要确认错误没有混入,也要确认正常输出没有控制字符或进度条。仅看到两份文件被创建还不够;用一次正常结果和一次可控的错误,分别核对输出内容及任务退出状态。
修改前保留原启动脚本或部署定义。需要回退时恢复对应选项;回退到 TTY 也会恢复合并输出的行为,不能继续沿用“两个文件就是两类日志”的假设。重建业务容器前仍应保留原镜像、持久卷和既有备份,本问题不需要清空容器数据或日志历史。


