Docker CLI 深入解析:docker kill 命令如何向容器主进程发送信号并强制终止 容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载docker kill是 Docker CLI 中用于强制终止一个或多个运行中容器的核心命令。本文以 docker-ce 仓库中该命令的 man 手册页描述components/cli/man/src/container/kill.md为核心骨架结合 CLI、客户端、API 路由与守护进程daemon的完整源码链路深入讲解docker kill的默认SIGKILL行为、--signal自定义信号机制、底层实现原理与边界行为读完即可掌握该命令从敲下回车到容器主进程收到信号的每一步并能准确处理自定义信号、停止容器、重启策略等实战场景。从 man 手册页看 docker kill 的定位在 docker-ce 仓库中components/cli/man/src/container/kill.md 是docker container kill命令手册页man page的 Long Description 来源其原文为The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal.这段文字精准概括了命令的核心语义每个指定容器内部的主进程PID 1将被发送SIGKILL或者发送通过--signal选项指定的任意信号。它是整个命令行为的总纲后续所有源码分析都围绕这两句话展开。这段描述并非孤立存在的文案而是通过 man 页面生成器被真正注入到命令定义中。查看 components/cli/man/generate.go 中的loadLongDescription函数可以发现生成器遍历 cobra 命令树按man/src/命令名.md的路径约定读取同名 Markdown 文件并将内容赋给cmd.Long最终由doc.GenManTreeFromOpts输出为.1.md手册页。也就是说kill.md的这行描述就是最终docker kill手册页里那段详细说明的母本。同时仓库中的 components/cli/man/src/kill.md 还揭示了命令的别名关系docker kill是docker container kill的别名Alias两者指向同一个命令实现。命令语法与完整参数说明命令的完整用法来自 components/cli/docs/reference/commandline/kill.md 与命令行实现为Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...] Kill one or more running containers Options: --help Print usage -s, --signal string Signal to send to the container (default KILL)参数说明默认值CONTAINER一个或多个容器可通过容器 ID、ID 前缀或名称引用必填至少一个-s, --signal string发送给容器的信号可以是SIGNAME形式如SIGINT也可以是内核系统调用表中对应的无符号数字如2KILL即SIGKILL从 CLI 源码 components/cli/cli/command/container/kill.go 可以看到命令的完整定义type killOptions struct { signal string containers []string } cmd : cobra.Command{ Use: kill [OPTIONS] CONTAINER [CONTAINER...], Short: Kill one or more running containers, Args: cli.RequiresMinArgs(1), RunE: func(cmd *cobra.Command, args []string) error { opts.containers args return runKill(dockerCli, opts) }, } flags : cmd.Flags() flags.StringVarP(opts.signal, signal, s, KILL, Signal to send to the container)几个值得注意的细节cli.RequiresMinArgs(1)强制要求至少提供一个容器参数不带容器直接执行docker kill会立即报错--signal的默认值在 CLI 层就是字符串KILL也就是说即使你不显式指定信号KILL也会被下发到 API 层命令支持一次终止多个容器CONTAINER...这与 man 文档中each container specified的表述完全对应。默认行为向容器主进程发送 SIGKILL按照 man 文档的描述默认情况下容器主进程收到的是SIGKILL。SIGKILL信号值 9是 Unix/Linux 中无法被捕获、阻塞或忽略的强制终止信号内核直接终止目标进程进程自身没有任何清理机会——这正是docker kill与docker stop的本质区别stop会先发SIGTERM并等待宽限期grace period而kill是立即强杀。在 daemon 侧的源码 components/engine/daemon/kill.go 中这一逻辑被明确实现// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL wait()) if sig 0 || syscall.Signal(sig) syscall.SIGKILL { return daemon.Kill(container) } return daemon.killWithSignal(container, int(sig))即当信号为 0API 未指定信号或恰好是SIGKILL时走daemon.Kill路径其实现为先向容器进程发送SIGKILL随后调用container.Wait等待容器退出事件最多 10 秒若 10 秒内仍未收到退出事件则调用killProcessDirectly直接对容器 PID 执行unix.Kill(pid, 9)兜底强杀见 components/engine/daemon/container_operations_unix.go。这也解释了为什么即便容器进程陷入 D 状态等极端情况docker kill依然会尽力完成终止。自定义信号--signal 的完整用法man 文档指出除默认的SIGKILL外--signal可以指定任意系统信号。官方参考文档 components/cli/docs/reference/commandline/kill.md 给出了三个等价写法$ docker kill --signalSIGHUP my_container $ docker kill --signalHUP my_container $ docker kill --signal1 my_container也就是说信号既可以带SIG前缀SIGHUP也可以省略前缀HUP还可以直接用数字1。信号字符串究竟如何被解析成内核信号关键实现在依赖库 components/engine/vendor/github.com/moby/sys/signal/signal.go 的ParseSignal函数中func ParseSignal(rawSignal string) (syscall.Signal, error) { s, err : strconv.Atoi(rawSignal) if err nil { if s 0 { return -1, fmt.Errorf(invalid signal: %s, rawSignal) } return syscall.Signal(s), nil } signal, ok : SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), SIG)] if !ok { return -1, fmt.Errorf(invalid signal: %s, rawSignal) } return signal, nil }解析规则非常清晰输入是纯数字时直接转成syscall.Signal但数字 0 是非法信号因为 0 在 API 语义中代表不指定信号、走默认 SIGKILL会被拒绝并返回invalid signal: 0输入是字符串时先转大写、再去掉SIG前缀然后到SignalMap中查找——这就是SIGHUP、HUP等价的原因查不到如SIG42时返回invalid signal: SIG42属于InvalidParameter错误。docker kill的语义还有一层微妙之处--signal指定的信号不一定是终结性的。man 文档特别强调信号是否终止容器取决于容器主进程对信号的处理方式。例如SIGHUP在多数情况下是非终结信号容器收到后仍会继续运行SIGWINCH终端窗口尺寸变化同样不会让容器退出。这一点有集成测试直接佐证见后文。一次 docker kill 的完整调用链结合 man 文档主进程收到信号这一结果我们可以沿仓库源码把整条链路完整还原第 1 步CLI 并发调度。命令入口 components/cli/cli/command/container/kill.go 的runKill调用parallelOperation对多个容器并发执行操作并在全部完成后按输入顺序输出成功容器名、聚合所有错误。并发控制实现在 components/cli/cli/command/container/utils.go通过带缓冲的sem信号量将最大并发数限制为 50const defaultParallel int 50同时借助每个容器一个的结果通道保证输出顺序与用户输入一致。第 2 步API 客户端发请求。每个容器最终调用客户端方法ContainerKillcomponents/engine/client/container_kill.go构造signal查询参数并 POST 到/containers/id/kill端点。第 3 步API 路由解析。路由注册于 components/engine/api/server/router/container/container.go#L50POST /containers/{name:.*}/kill对应处理器postContainersKillcomponents/engine/api/server/router/container/container_routes.go#L240-L273。处理器从表单中读取signal参数并调用signal.ParseSignal解析若参数为空则信号保持 0代表走默认SIGKILL解析失败返回InvalidParameter错误。第 4 步API 版本兼容处理。处理器调用 daemon 后端ContainerKill(name, sig)后若容器已停止会收到冲突conflict类错误。这里存在一个版本兼容细节当 API 版本 ≥ 1.20 时向已停止容器执行 kill 会返回Cannot kill container错误而当 API 版本低于 1.20如 1.19时为保持向后兼容不返回错误直接返回204 No Content。第 5 步daemon 分派信号。核心逻辑回到 components/engine/daemon/kill.go 的ContainerKill先用signal.ValidSignalForPlatform校验信号在当前平台是否合法不合法时报The os daemon does not support signal n随后按前文所述分派到daemon.KillSIGKILL 路径或killWithSignal自定义信号路径。killWithSignal是自定义信号路径的关键其内部逻辑值得展开加锁后首先检查container.Running容器不在运行状态则返回is not running错误若容器配置了StopSignal且发送的信号恰好与该StopSignal一致且不是 SIGKILL则标记ExitOnNext并记录暂停状态示意监控循环在下一个事件周期退出——这意味着与容器StopSignal相同的 kill 信号会绕过后台重启策略集成测试对此有专门覆盖若容器正处于Restarting重启中状态则无需真的向进程发信号只要让监控循环在下一轮退出即可最终通过daemon.containerd.SignalProcess(ctx, c.ID, libcontainerdtypes.InitProcessName, sig)将信号真正投递给容器的 init 进程即 PID 1 / 主进程成功后记录事件daemon.LogContainerEventWithAttributes(container, kill, attributes)其中 attributes 携带了实际信号编号。至此主进程收到信号在源码层面得到了完整闭环验证。实战注意事项与边界行为1. shell 形式 ENTRYPOINT/CMD 不传递信号man 文档引用了一个非常关键的注意事项ENTRYPOINT和CMD在shell形式下作为/bin/sh -c的子进程运行而sh -c不会透传信号。这意味着此时真正的可执行文件并不是容器的 PID 1因而收不到 Unix 信号。也就是说如果你用CMD [/bin/sh, -c, exec myapp]之外的写法如CMD echo hello信号会被/bin/sh这一层吃掉docker kill --signalSIGTERM my_container可能无法优雅地通知到你的应用进程只有 PID 1sh退出容器才会终止。实战中应优先使用 exec 形式确保应用成为 PID 1。2. 对已停止容器执行 kill 的行为差异如调用链一节所述默认 API 版本下对已停止容器执行docker kill会返回类似... is not running的错误而使用旧版 API1.19 及以下时不会报错保持向后兼容。这条规则与路由源码中versions.GreaterThanOrEqualTo(version, 1.20) || !isStopped的判断完全一致。3. 无效信号会被直接拒绝signal 0或signal SIG42这类无效输入会在解析阶段直接失败容器保持运行状态不受影响。4. 容器重启策略与 StopSignal 的相互作用当容器配置了--restartalways且设置了自定义StopSignal时kill 行为会产生差异发送与StopSignal相同的信号如TERM会让容器退出且不再被重启策略拉起发送不同信号如对StopSignalCONT的容器发TERM则容器保持运行。5. 多容器批量 kill 与并发上限docker kill支持一次传入多个容器CLI 以最多 50 路并发向 daemon 发起请求但结果仍按用户输入顺序输出任一容器失败会聚合为整体错误成功容器名照常打印。集成测试行为的事实依据docker-ce 仓库在 components/engine/integration/container/kill_test.go 中提供了覆盖上述行为的端到端集成测试可以作为本文所有结论的可验证依据测试函数验证点TestKillContainerInvalidSignal对运行中容器发送0或SIG42均报invalid signal错误且容器保持 runningTestKillContainer不指定信号 → 容器 exited发送SIGWINCH非终结信号→ 容器仍 running发送SIGTERM→ 容器 exitedTestKillWithStopSignalAndRestartPoliciesStopSignalTERM killTERM→ 退出且重启策略不生效StopSignalCONT killTERM→ 保持 runningTestKillStoppedContainer对已停止容器 killSIGKILL→ 报错包含is not runningTestKillStoppedContainerAPIPre120使用 API 1.19 对已停止容器 kill → 不报错向后兼容TestKillDifferentUserContainer对以其他用户身份运行的容器 killSIGKILL→ 正常退出其中TestKillContainer的non killing signal用例在 Windows 平台被跳过skipOs: windows因为 Windows 不支持SIGWINCH这也侧面印证了信号语义的平台相关性。小结从 man 手册页的一句话出发通过 docker-ce 仓库的源码与测试我们完整还原了docker kill的全貌默认行为向每个指定容器的 PID 1 主进程发送SIGKILL不可捕获、立即强杀若 10 秒内未退出还有unix.Kill(pid, 9)兜底--signal支持SIGNAME、NAME、数字三种写法非终结信号如SIGHUP、SIGWINCH不会让容器退出数字 0 与未知信号会被拒绝完整链路CLI 并发调度50 路→POST /containers/{name}/kill→ 路由解析与版本兼容 → daemon 按信号分派 → 经 containerdSignalProcess投递给 init 进程注意事项shell 形式启动的应用收不到信号、停止容器在 API 1.20 下报错、kill 信号与StopSignal相同时会绕过重启策略。理解这些细节后无论你是排查容器为什么没被优雅终止、kill 之后容器又活了还是想利用自定义信号实现进程热重载如docker kill -s HUP container都能准确预判docker kill的实际行为。赞分享容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载相关推荐Docker CLI 中 docker kill 命令详解向容器发送信号的实现原理与实战Docker CLI 中 docker kill 命令详解向容器发送信号的实现原理与实战 本文围绕 docker kill 子命令展开该命令用于向一个或多个CLI开发工具Docker Compose CLI kill 命令详解强制停止服务容器、信号控制与孤儿容器清理Docker Compose CLI kill 命令详解强制停止服务容器、信号控制与孤儿容器清理 docker compose kill 用于向 Compos云原生容器编排DevOpsCLIDocker CLI docker kill 命令详解信号发送原理、自定义信号与源码实现Docker CLI docker kill 命令详解信号发送原理、自定义信号与源码实现 导读 docker kill 是 Docker CLI 中最常用的容CLI开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考