You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

让Docker成为标准UNIX组件:信号与输出转发问题求助

Docker包装器的信号传递与标准输出适配问题

问题背景

Docker对SIGPIPE的处理存在缺陷,执行以下命令时:

$ docker run --rm ubuntu yes | head
y
y
y
y
y
y
y
y
y
y
write /dev/stdout: broken pipe
$ 

不仅会额外输出write /dev/stdout: broken pipe,更关键的是Docker容器内的进程不会自动终止。

为解决标准输出问题,我尝试用FIFO挂载作为输出中转,编写了如下脚本:

fifo=`basename $0`.$$
mkfifo $fifo
cat $fifo &
docker run --rm -v$PWD/$fifo:$PWD/$fifo ubuntu sh -c "$* >$PWD/$fifo"
rm $fifo

但新问题出现:按下Ctrl-C无法终止容器,shell不会将SIGINT转发给前台的Docker进程。

于是我添加了信号捕获逻辑:

trap 'kill $!' INT TERM
fifo=`basename $0`.$$
mkfifo $fifo
cat $fifo &
docker run --rm -v$PWD/$fifo:$PWD/$fifo ubuntu sh -c "$* >$PWD/$fifo" &
wait
rm $fifo

结果按下Ctrl-C后脚本退出,但Docker进程和cat仍在持续输出,完全达不到预期。

核心需求

适配UNIX工作流,用于Docker化应用的基准测试:当某实例输出特定行数后终止它,并向其他所有实例发送SIGTERM终止进程。

解决方案

1. 修复信号传递与FIFO清理问题

修改脚本,确保捕获信号时能终止所有相关进程(Docker容器、cat进程),并清理FIFO:

#!/bin/bash

# 定义清理函数,终止所有子进程并删除FIFO
cleanup() {
    # 终止Docker容器(通过docker stop,比kill更可靠)
    if [ -n "$DOCKER_CONTAINER_ID" ]; then
        docker stop "$DOCKER_CONTAINER_ID" >/dev/null 2>&1
    fi
    # 终止cat进程
    if [ -n "$CAT_PID" ]; then
        kill "$CAT_PID" >/dev/null 2>&1
    fi
    # 删除FIFO
    if [ -e "$fifo" ]; then
        rm -f "$fifo"
    fi
    exit 0
}

# 捕获INT、TERM信号触发清理
trap cleanup INT TERM

# 创建FIFO
fifo="$(basename "$0").$$"
mkfifo "$fifo"

# 启动cat转发输出,记录PID
cat "$fifo" &
CAT_PID=$!

# 启动Docker容器,记录容器ID和后台进程PID
cidfile="/tmp/docker_cid_$$"
docker run --rm --cidfile "$cidfile" -v"$PWD/$fifo:$PWD/$fifo" ubuntu sh -c "$* >$PWD/$fifo" &
DOCKER_PID=$!
# 读取容器ID
DOCKER_CONTAINER_ID=$(cat "$cidfile")
rm -f "$cidfile"

# 等待Docker进程结束,之后自动触发清理
wait "$DOCKER_PID"
cleanup

2. 实现“输出特定行数后终止实例+发送SIGTERM到其他实例”

针对基准测试的需求,扩展脚本监控输出行数,达到阈值时终止当前容器,并向其他指定容器发送SIGTERM:

#!/bin/bash

# 配置:目标输出行数
TARGET_LINES=10
# 其他需要终止的容器ID列表(可通过参数传入或提前获取)
OTHER_CONTAINERS=("container_id_1" "container_id_2")

cleanup() {
    # 终止当前容器
    if [ -n "$DOCKER_CONTAINER_ID" ]; then
        docker stop "$DOCKER_CONTAINER_ID" >/dev/null 2>&1
    fi
    # 终止cat和监控进程
    if [ -n "$CAT_PID" ]; then
        kill "$CAT_PID" >/dev/null 2>&1
    fi
    if [ -n "$MONITOR_PID" ]; then
        kill "$MONITOR_PID" >/dev/null 2>&1
    fi
    rm -f "$fifo"
    exit 0
}

trap cleanup INT TERM

fifo="$(basename "$0").$$"
mkfifo "$fifo"

# 启动监控进程,统计输出行数,达到阈值时触发终止
(
    line_count=0
    while read -r line; do
        echo "$line"
        ((line_count++))
        if [ "$line_count" -ge "$TARGET_LINES" ]; then
            # 终止当前容器
            docker stop "$DOCKER_CONTAINER_ID" >/dev/null 2>&1
            # 向其他容器发送SIGTERM
            for cid in "${OTHER_CONTAINERS[@]}"; do
                docker kill --signal=SIGTERM "$cid" >/dev/null 2>&1
            done
            cleanup
        fi
    done < "$fifo"
) &
MONITOR_PID=$!

cidfile="/tmp/docker_cid_$$"
docker run --rm --cidfile "$cidfile" -v"$PWD/$fifo:$PWD/$fifo" ubuntu sh -c "$* >$PWD/$fifo" &
DOCKER_PID=$!
DOCKER_CONTAINER_ID=$(cat "$cidfile")
rm -f "$cidfile"

wait "$DOCKER_PID"
cleanup

关键改进点

  • 使用docker stop和docker kill --signal=SIGTERM替代直接kill,更可靠地控制Docker容器
  • 记录所有相关进程的PID(cat、监控进程、Docker容器ID),确保信号能传递到所有组件
  • 统一的cleanup函数处理所有终止和资源清理逻辑
  • 监控输出行数的子进程,达到阈值时自动触发终止流程

内容的提问来源于stack exchange,提问作者Patrick Coppock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:05:18