
1. 排障运行时到底在排什么先搞清楚问题边界很多人一看到“Docker 里跑排障运行时”这几个字第一反应就是docker run一条命令把容器拉起来然后进去敲几个命令看看日志就完事了。我刚开始接触这块的时候也是这么想的结果被现实反复教育。排障运行时Troubleshooting Runtime本质上不是某一个具体的软件而是一类在容器内部常驻或按需启动的诊断环境它要解决的核心问题是当业务容器出现异常而你又不能直接在生产容器里随意安装工具、修改配置的时候如何用一个独立、可控、可复现的运行时去定位问题。这个场景在真实工作中出现的频率远比想象中高。比如你部署了一个基于 Docker 的微服务某个服务突然 CPU 飙高、内存泄漏、网络不通、健康检查一直失败这时候你需要的不是重启大法而是一套能在容器网络命名空间里直接观察、抓包、查进程、看文件描述符的排障环境。RootSeeker 这类工具的思路就是把这个排障运行时做成一个可以快速拉起的容器镜像配合 setup-cn 这样的初始化脚本把常用诊断工具、权限配置、网络探测能力一次性准备好。那为什么非要放在 Docker 里跑直接在宿主机上装一套工具不行吗这里有几个很现实的考量。第一宿主机环境往往和生产容器环境不一致你在宿主机上看到的网络栈、进程视图、文件系统挂载点和容器内部完全是两回事。第二很多排障操作需要进入目标容器的命名空间比如nsenter这要求排障运行时本身具备足够的权限并且和目标容器共享某些命名空间。第三容器化的排障运行时可以做到即用即弃不会污染宿主机环境也不会因为装了一堆工具导致依赖冲突。适合谁来参考这篇内容如果你已经在用 Docker 部署服务遇到过容器启动失败、网络不通、健康检查异常、权限报错这些问题并且希望有一套系统化的排查思路而不是靠猜那这篇就是写给你的。如果你刚接触 Docker还在折腾 docker desktop 安装、docker 安装教程这些基础问题那建议先把基础跑通再回来看排障部分不然容易越看越乱。2. 排障运行时的整体设计与选型思路2.1 为什么选择独立容器而不是侵入业务容器排障运行时最核心的设计决策就是排障工具到底放在哪里。有三种常见做法我逐一分析一下各自的利弊。第一种是直接把工具装进业务镜像里。这种做法看起来最方便docker exec进去就能用。但问题非常明显业务镜像会变得臃肿安全扫描会报一堆漏洞而且生产环境通常不允许你随便改镜像。更关键的是当容器已经崩溃到无法 exec 的时候你装在里面的工具也一起用不了。第二种是在宿主机上装全套工具然后通过docker exec或者nsenter进入容器。这个方案在单机环境下勉强可用但在多节点、多容器场景下就非常痛苦因为你得保证每台宿主机都装了相同版本的工具维护成本极高。第三种就是独立排障运行时容器。它的优势在于镜像可以预先构建好包含完整的诊断工具链启动时可以按需挂载目标容器的命名空间用完即删不留痕迹而且可以通过 Docker 网络直接访问目标容器的网络栈。RootSeeker 配合 setup-cn 脚本走的就是这条路子。我实测下来第三种方案在复杂环境下的排障效率是最高的。尤其是当你需要同时观察多个容器的网络流量或者进程状态时一个独立的排障运行时可以让你在一个终端里完成所有操作不用来回切换。2.2 镜像基础选型 Alpine 还是 Ubuntu排障运行时的基础镜像选型直接决定了你能用哪些工具、镜像有多大、启动有多快。常见的两个选择是 Alpine 和 Ubuntu。Alpine 的优势是体积小基础镜像只有几 MB启动快适合快速拉起。但它的坑也很明显默认使用 musl libc 而不是 glibc很多预编译的二进制工具在 Alpine 上跑不起来比如某些版本的tcpdump、strace或者厂商提供的诊断工具。你得花时间去找 Alpine 兼容的版本或者自己编译这在紧急排障的时候非常耽误事。Ubuntu 基础镜像体积大一些但兼容性好apt源里的工具齐全tcpdump、strace、netstat、ss、lsof、curl、dig这些常用工具一条命令就能装好。对于排障运行时来说我个人的偏好是 Ubuntu 或者 Debian slim 版本牺牲一点体积换取工具链的完整性和稳定性。毕竟排障的时候时间比磁盘空间值钱得多。RootSeeker 这类工具通常会提供一个预构建的镜像里面已经集成了常用诊断工具。如果你要自己构建建议基于ubuntu:22.04或者debian:bookworm-slim然后在 Dockerfile 里一次性把工具装齐。setup-cn 脚本的作用就是帮你处理国内网络环境下的镜像源配置和依赖安装避免因为网络问题卡在apt update这一步。2.3 权限模型CAP 能力与特权模式怎么选排障运行时需要的权限比普通容器多得多。你要抓包就得有NET_ADMIN和NET_RAW要查看其他进程就得有SYS_PTRACE要进入其他容器的命名空间就得有SYS_ADMIN。最省事的做法是直接--privileged但这在生产环境里是大忌等于把宿主机完全暴露给容器。我的建议是按需授予能力而不是一刀切用特权模式。下面这张表是我在实际项目中总结的权限对照你可以根据排障目标来选择排障操作所需 CAP 能力是否必须特权网络抓包 tcpdumpNET_ADMIN, NET_RAW否查看进程 straceSYS_PTRACE否进入目标容器命名空间 nsenterSYS_ADMIN, SYS_PTRACE否挂载文件系统SYS_ADMIN否修改网络配置NET_ADMIN否访问所有设备无特定 CAP是可以看到绝大多数排障操作都不需要完整的特权模式。用--cap-add精确授予能力既满足排障需求又不会把安全边界完全打破。这一点在合规要求比较严格的环境里尤其重要。注意即使不用特权模式排障运行时容器本身也应该运行在独立的网络命名空间或者 host 网络模式下具体取决于你要排查的是容器网络还是宿主机网络。这个选择后面会详细讲。3. 核心细节解析与实操要点3.1 健康检查为什么总是失败从退出码反推问题健康检查Health Check是 Docker 排障里最高频的问题来源之一。很多人写了HEALTHCHECK指令结果容器状态一直是unhealthy但业务本身跑得好好的。这时候你需要从健康检查的退出码和输出入手。Docker 健康检查的判定逻辑很简单命令退出码为 0 表示健康为 1 表示不健康为 2 表示保留状态。但问题往往出在检查命令本身。比如你用curl去检查一个 HTTP 接口但容器里根本没装curl那健康检查永远失败。或者你检查的端口是容器内部端口但检查命令跑在了错误的网络命名空间里。我遇到过一个典型案例一个基于 Docker 部署的 MySQL 8.0 服务健康检查用的是mysqladmin ping但检查命令里没有指定 socket 路径导致它尝试用 TCP 连接 localhost而 MySQL 配置里绑定的地址是127.0.0.1在容器网络里这个地址指向的是容器自身的 loopback理论上应该能通但实际上因为认证插件的问题一直返回失败。最后改成用mysqladmin ping -h 127.0.0.1 -u root -p$MYSQL_ROOT_PASSWORD才稳定通过。排查健康检查问题的步骤我一般是这样走的先看docker inspect里的Health字段找到最近几次检查的输出和退出码。手动在容器里执行同样的检查命令看是否复现。检查命令依赖的二进制是否存在which curl、which mysqladmin这些先确认。检查网络连通性ss -tlnp看端口是否在监听curl -v看握手过程。检查认证和权限很多健康检查失败其实是认证问题而不是网络问题。实操心得健康检查的interval、timeout、retries这三个参数不要用默认值。默认interval是 30 秒timeout是 30 秒retries是 3 次。对于启动慢的服务建议把start-period设长一点否则容器刚启动就被判定为不健康可能触发重启循环。3.2 网络不通的排查路径从命名空间到 iptablesDocker 网络不通是另一个高频排障场景。很多人一遇到网络问题就重启 Docker 服务这治标不治本。正确的排查路径应该是从容器网络命名空间开始逐层往外查。第一步确认容器是否真的在运行docker ps看状态。如果容器已经退出那网络问题可能是结果而不是原因先看退出日志。第二步进入容器的网络命名空间看网卡和路由。可以用docker exec进去执行ip addr和ip route也可以用排障运行时通过nsenter --net/proc/pid/ns/net进入。重点看有没有eth0IP 地址是否在预期网段默认路由是否指向网关。第三步检查 DNS 解析。cat /etc/resolv.conf看 DNS 服务器地址然后用dig或nslookup测试域名解析。Docker 默认的 DNS 是127.0.0.11如果这个地址不通所有域名解析都会失败。第四步检查 iptables 规则。Docker 会在宿主机上写大量 iptables 规则来实现端口映射和网络隔离。如果规则被其他软件覆盖或者清空容器网络就会异常。在宿主机上执行iptables -t nat -L -n看 NAT 规则iptables -L -n看 filter 规则。第五步检查目标服务是否在监听。有时候网络是通的但目标端口根本没起来。用ss -tlnp在目标容器里确认监听状态。我整理了一个网络排查速查表按顺序执行基本能覆盖 90% 的问题排查层级检查命令常见异常容器状态docker ps -a容器已退出网卡与 IPip addr无 eth0 或 IP 异常路由ip route无默认路由DNScat /etc/resolv.confDNS 地址不可达端口监听ss -tlnp端口未监听宿主机 NATiptables -t nat -L -n规则缺失目标连通curl -v 或 nc -zv连接超时或拒绝3.3 setup-cn 脚本到底做了什么初始化流程拆解setup-cn 这个脚本名字里的 cn 很明显是指国内环境适配。在国内网络环境下跑 Docker 排障运行时最大的痛点就是镜像拉取慢、apt 源慢、pip 源慢。setup-cn 的核心工作就是把这些源全部替换成国内可用的镜像源然后安装排障所需的工具链。一个典型的 setup-cn 脚本会做以下几件事备份原有的 apt sources.list然后替换成国内镜像源。执行apt update并安装基础工具curl、wget、iproute2、iputils-ping、dnsutils、net-tools、tcpdump、strace、lsof、procps。配置 pip 源如果需要 Python 工具替换成国内源。配置 Docker 镜像加速器如果脚本运行在宿主机上。设置时区和 locale避免日志时间戳混乱。创建必要的目录和权限配置。这个脚本的价值在于可复现。你不需要每次手动敲一堆命令也不需要在每台机器上重复配置。把它放在排障运行时的镜像构建阶段或者容器启动的 entrypoint 里就能保证每次拉起的排障环境都是一致的。注意替换 apt 源的时候一定要先备份原文件而且要注意不同 Ubuntu 版本的代号不一样jammy、focal、noble 对应的源地址不同。用错代号会导致apt update直接报 404。4. 实操过程与核心环节实现4.1 构建排障运行时镜像的完整 Dockerfile下面是我在实际项目中用的排障运行时 Dockerfile基于 Ubuntu 22.04集成了常用诊断工具并且通过 setup-cn 脚本处理国内源。你可以直接参考这个结构按需增删工具。FROM ubuntu:22.04 ENV DEBIAN_FRONTENDnoninteractive ENV TZAsia/Shanghai # 替换国内 apt 源 RUN sed -i s|http://archive.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g /etc/apt/sources.list \ sed -i s|http://security.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g /etc/apt/sources.list # 安装排障工具链 RUN apt-get update apt-get install -y --no-install-recommends \ curl \ wget \ iproute2 \ iputils-ping \ dnsutils \ net-tools \ tcpdump \ strace \ lsof \ procps \ less \ vim-tiny \ jq \ ca-certificates \ rm -rf /var/lib/apt/lists/* # 设置时区 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone # 复制 setup-cn 脚本 COPY setup-cn.sh /usr/local/bin/setup-cn.sh RUN chmod x /usr/local/bin/setup-cn.sh ENTRYPOINT [/bin/bash]这个 Dockerfile 有几个设计点值得说明。第一DEBIAN_FRONTENDnoninteractive是必须的否则 apt 安装过程中会弹出交互式配置界面导致构建卡住。第二--no-install-recommends可以减少不必要的依赖控制镜像体积。第三工具链的选择覆盖了网络、进程、文件、DNS 四个维度的排查需求。第四rm -rf /var/lib/apt/lists/*清理 apt 缓存进一步减小镜像。构建命令docker build -t troubleshoot-runtime:latest .构建完成后可以用docker images看一下镜像大小我这边实测大概在 180MB 左右对于排障用途来说完全可以接受。4.2 启动排障容器并接入目标网络命名空间镜像构建好之后关键是怎么让它接入目标容器的网络命名空间。有两种常用方式我分别说一下。第一种是让排障容器和目标容器共享网络命名空间。启动排障容器时加上--networkcontainer:目标容器名或ID这样排障容器就直接使用目标容器的网络栈ip addr看到的网卡、ss -tlnp看到的端口和目标容器完全一致。这种方式最简单直接适合排查网络和端口问题。docker run -it --rm \ --networkcontainer:my-app-container \ --cap-addNET_ADMIN \ --cap-addNET_RAW \ --cap-addSYS_PTRACE \ troubleshoot-runtime:latest第二种是通过nsenter进入目标容器的命名空间。这种方式更灵活可以分别进入网络、进程、挂载等不同的命名空间。首先找到目标容器的 PIDTARGET_PID$(docker inspect -f {{.State.Pid}} my-app-container)然后启动排障容器加上--pidhost和--privileged或者精确的 CAP在容器内部执行nsenter -t $TARGET_PID -n -p -m -- /bin/bash这样你就进入了目标容器的网络、进程和挂载命名空间看到的进程列表、网络接口、文件系统都是目标容器的视角。这种方式在排查进程和文件系统问题时特别有用。实操心得nsenter进入命名空间后ps命令看到的进程 PID 是宿主机视角的 PID不是容器内部的 PID。如果你要 kill 某个进程一定要确认 PID 的含义否则可能误杀宿主机上的其他进程。4.3 抓包与流量分析的实际操作网络问题排查到一定程度光看端口和路由是不够的必须抓包看实际流量。在排障运行时里用tcpdump抓包关键是要抓对网卡和过滤条件。先确认网卡名称在共享网络命名空间的排障容器里执行ip addr通常业务容器的网卡是eth0。然后执行抓包tcpdump -i eth0 -nn -s 0 -w /tmp/capture.pcap port 8080参数说明-i eth0指定网卡-nn不解析主机名和端口名避免 DNS 查询干扰-s 0抓完整包不截断-w写入文件方便后续分析port 8080是过滤条件。如果只是想快速看流量情况可以不加-w直接输出到终端tcpdump -i eth0 -nn -c 100 host 10.0.0.5 and port 3306这个命令抓 100 个来自或发往 10.0.0.5 的 3306 端口包。-c 100限制数量避免刷屏。抓到的 pcap 文件可以用tcpdump -r直接读也可以拉到本地用 Wireshark 分析。如果排障容器里没有 Wireshark可以用tcpdump -r /tmp/capture.pcap -nn先看个大概。注意抓包会产生大量数据尤其是在高流量环境下。一定要加过滤条件并且用-c限制包数量否则可能把容器磁盘写满。另外抓包需要NET_ADMIN和NET_RAW能力如果容器没有这两个能力tcpdump会直接报权限错误。4.4 进程与资源排查strace、lsof 和 top 的组合用法网络排查完之后如果问题出在进程本身比如 CPU 飙高、内存泄漏、文件描述符耗尽就需要用到进程排查工具。strace是追踪系统调用的利器。比如你怀疑某个进程卡在某个系统调用上可以这样用strace -p PID -f -T -tt -e tracenetwork-p指定进程-f追踪子进程-T显示每个调用耗时-tt显示时间戳-e tracenetwork只追踪网络相关调用。这样可以看到进程在哪个网络调用上卡住了。lsof用来查看进程打开的文件和网络连接。比如查看某个进程打开了哪些文件lsof -p PID查看某个端口被哪个进程占用lsof -i :8080查看所有 TCP 连接lsof -i tcptop和ps用来查看资源占用。在排障容器里如果共享了目标容器的 PID 命名空间top看到的就是目标容器的进程。我一般用top -b -n 1输出一次快照然后按 CPU 或内存排序找异常进程。这三个工具的组合用法是先用top找到异常进程 PID再用lsof看它打开了什么文件和连接最后用strace追踪它到底在干什么。这个流程走下来大部分进程级问题都能定位。5. 常见问题与排查技巧实录5.1 排障容器启动就退出entrypoint 和权限的坑排障容器启动后立刻退出docker ps -a看到状态是Exited (0)或者Exited (1)这是很常见的问题。原因通常有三个。第一个是 entrypoint 命令执行完就结束了。比如你的 Dockerfile 里ENTRYPOINT [/bin/bash]但启动时没有加-itbash 发现没有交互终端就直接退出了。解决办法是启动时加-it或者把 entrypoint 改成tail -f /dev/null这种常驻命令。第二个是权限问题。排障容器需要的能力没有授予导致启动脚本里的某个命令失败脚本退出。比如tcpdump需要NET_RAW没有这个能力时启动脚本如果直接调用tcpdump就会失败退出。解决办法是检查启动脚本把非必要的初始化命令改成容错模式或者补全所需能力。第三个是挂载路径不存在。如果你用-v挂载了宿主机的某个路径但路径不存在Docker 会创建一个空目录但如果挂载的是文件而文件不存在就会报错退出。解决办法是启动前确认挂载路径存在。5.2 镜像拉取慢或失败镜像源配置的正确姿势国内环境拉取 Docker 镜像慢是老大难问题。docker pull卡住或者超时通常是因为默认的 Docker Hub 在国内访问不稳定。解决办法是配置镜像加速器。在 Linux 上编辑/etc/docker/daemon.json{ registry-mirrors: [ https://mirror.ccs.tencentyun.com, https://docker.mirrors.ustc.edu.cn ] }然后重启 Docker 服务sudo systemctl daemon-reload sudo systemctl restart docker在 Windows 上如果是 Docker Desktop可以在设置里的 Docker Engine 配置中加上同样的registry-mirrors字段然后重启 Docker Desktop。注意镜像加速器地址会变化有些公开的加速器可能已经停止服务。配置之前最好先确认地址可用或者使用云服务商提供的专属加速器地址。另外配置多个加速器时Docker 会按顺序尝试第一个失败才会试第二个。5.3 权限错误怎么解决从报错信息定位 CAP 缺失排障过程中最常见的权限错误就是Operation not permitted和Permission denied。这两个报错含义不同排查方向也不同。Operation not permitted通常是 CAP 能力不足。比如tcpdump报这个错就是缺NET_RAW或NET_ADMIN。strace报这个错就是缺SYS_PTRACE。解决办法是在启动容器时用--cap-add补上对应能力。Permission denied通常是文件系统权限或者用户权限问题。比如挂载的目录属主是 root但容器里以非 root 用户运行就会报这个错。解决办法是调整挂载目录权限或者在容器里用 root 用户运行。我整理了一个权限错误速查表报错信息常见原因解决办法Operation not permittedCAP 能力不足--cap-add 补全能力Permission denied文件权限或用户不匹配调整目录权限或切换用户No such file or directory路径不存在或二进制缺失确认路径和工具是否安装Cannot allocate memory内存限制或 ulimit 限制调整 --memory 或 ulimitNetwork is unreachable路由或网络命名空间问题检查 ip route 和网络模式5.4 排障容器和目标容器网络不通命名空间隔离的陷阱有时候排障容器启动正常但就是访问不了目标容器的网络。这种情况多半是命名空间隔离导致的。如果排障容器没有使用--networkcontainer:目标容器那它就在自己的网络命名空间里看到的 IP 和目标容器完全不同。这时候你用curl去访问目标容器的服务必须用目标容器在 Docker 网络里的 IP 或者服务名而不是localhost。解决办法有两个一是启动排障容器时直接共享目标容器的网络命名空间二是把排障容器加入目标容器所在的 Docker 网络然后用服务名或容器 IP 访问。docker run -it --rm \ --networkmy-app-network \ troubleshoot-runtime:latest这样排障容器就和目标容器在同一个自定义网络里可以通过容器名直接访问。实操心得Docker 默认的 bridge 网络不支持自动 DNS 解析容器名只有自定义网络才支持。所以如果你用--networkbridge就只能用 IP 访问容器名解析不了。建议排障时统一用自定义网络。6. 工具选型与效率提升的几个关键决策6.1 RootSeeker 这类工具解决了什么实际问题RootSeeker 这个名字本身就说明了它的定位寻找根因。它不是一个单一工具而是一套排障运行时的封装把前面提到的镜像构建、权限配置、网络接入、工具链安装这些步骤打包成一个可复用的方案。它解决的实际问题有三个。第一是标准化团队里每个人排障用的工具和环境不一致导致排查结果无法复现。RootSeeker 提供统一的运行时保证每个人看到的诊断信息一致。第二是效率不用每次从零构建排障环境一条命令拉起就能用。第三是知识沉淀把常见排障路径和检查项固化到工具里新人也能按图索骥。如果你不想引入额外的工具完全可以基于前面给的 Dockerfile 自己构建一套。核心思路是一样的预构建镜像、按需授予权限、共享目标命名空间、集成常用工具。6.2 排障运行时的资源限制与安全边界排障运行时权限大如果不加限制本身就可能成为安全隐患。我的建议是给它设置资源限制并且限定使用范围。资源限制方面启动时加上--memory和--cpusdocker run -it --rm \ --memory512m \ --cpus1 \ --networkcontainer:my-app-container \ --cap-addNET_ADMIN \ --cap-addNET_RAW \ troubleshoot-runtime:latest这样即使排障容器里的某个命令失控也不会把宿主机资源耗尽。安全边界方面排障运行时应该只在需要的时候启动用完立即删除--rm参数。不要把它长期运行在生产环境里。另外排障容器的镜像应该定期更新修补基础镜像里的安全漏洞。6.3 从排障到预防把检查项固化到健康检查里排障的最高境界是不需要排障。很多问题如果提前在健康检查里覆盖到就能在故障发生前发现。我的做法是把排障时常用的检查项逐步固化到健康检查脚本里。比如网络连通性检查、关键端口监听检查、依赖服务可达性检查、磁盘空间检查。这些检查项在排障时是诊断手段在平时就是预警机制。举个例子一个 Web 服务的健康检查脚本可以这样写#!/bin/bash # 检查自身端口 ss -tlnp | grep -q :8080 || exit 1 # 检查依赖的数据库 nc -zv db-host 3306 || exit 1 # 检查磁盘空间 df -h /data | awk NR2 {if ($50 90) exit 1} || exit 1 # 检查 HTTP 接口 curl -sf http://localhost:8080/health || exit 1 exit 0这个脚本覆盖了端口、依赖、磁盘、接口四个维度任何一个出问题都会让容器变成unhealthy配合监控告警就能提前介入。注意健康检查脚本里的命令必须是容器里真实存在的。写脚本之前先用which确认一下否则健康检查永远失败反而制造噪音。7. 我踩过的几个坑和最后的经验分享第一个坑是过度依赖--privileged。刚开始图省事所有排障容器都加特权模式结果有一次排障容器里的脚本误操作影响了宿主机网络导致整个节点上的容器都断了。后来改成按需授予 CAP虽然启动命令长了一点但安全边界清晰多了。第二个坑是忽略了时区。排障容器默认 UTC 时间业务容器是东八区看日志的时候时间戳对不上排查时序问题非常痛苦。后来在 Dockerfile 里统一设置了TZAsia/Shanghai这个问题就再也没出现过。第三个坑是抓包文件太大。有一次排查一个高流量服务tcpdump没加-c限制跑了十分钟把容器磁盘写满了反而制造了新故障。后来养成习惯抓包必须加过滤条件和包数量限制并且先df -h确认磁盘空间。最后一个经验是排障运行时最好和业务容器使用相同的镜像基础。比如业务容器是 Ubuntu 22.04排障容器也用 Ubuntu 22.04这样工具链和库版本一致排查动态链接库问题时不会因为环境差异产生误判。这个细节很多人不注意但在排查一些底层问题时非常关键。