Docker排障容器运行指南:宿主机内核、权限与网络排查要点

Docker排障容器内核权限 本内容遵循CC 4.0 BY-SA版权协议
飞翔的十号 飞翔的十号 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要

在 Docker 里跑排障用的运行时容器,听起来就是把镜像 pull 下来 docker run 一下的事儿,但实际操作起来,十次里有七八次问题不是出在容器本身,而是出在宿主机那几个容易被忽略的点上。这里说的“排障运行时”,指的是你专门用来做故障排查的容器环境——比如带 sysdig、falco、bpftrace、perf、tcpdump 这类系统级诊断工具的镜像,或者是在容器里启动一个需要完整系统调用能力的语言运行时(像 JVM、Python 带 ctypes 的调试版本)。这类容器对宿主机的内核、权限、资源、网络要求都特别挑剔,稍有一个条件不满足,跑起来就是各种莫名其妙的报错:要么 permission denied,要么无法连接外部、要么拉个镜像就能卡半小时。这篇就把我这些年踩过的坑整理出来,讲讲跑这类容器之前,该先核对哪几个点。

1. 内核与权限:排障容器能不能看见真相

1.1 为什么普通容器里看不到系统全貌

容器跟虚拟机最大的区别是共享宿主机内核。你跑一个 ubuntu 容器,uname -r 看到的依然是宿主机的内核版本,而不是某个“容器内核”。这意味着容器里能调用的系统调用、能看到的 /proc、/sys 里的信息,完全取决于宿主机内核开放了什么。

排障类工具对这一点特别敏感。perf 要读硬件性能计数器,sysdig 要用 tracepoint 捕获系统调用,falco 要做内核级安全监控,这些都要访问内核暴露的接口。普通容器默认的 seccomp profile 会拦截一大批系统调用,比如 perf_event_open 这种,直接导致工具启动就报错。我见过一个同事在容器里跑 perf top,报 “Error: Perf session creation failed”,第一反应是镜像没装好,折腾了半天 apt 装依赖,最后才发现是 seccomp 把系统调用给挡了。

解决思路很简单:跑这种工具时不要用默认的 seccomp 配置。要么加 --security-opt seccomp=unconfined,要么用 --privileged。但这两个选择权重不一样,--privileged 是直接把所有 capabilities 都给容器,相当于 root 在宿主机上的权限,这对排障来说是省事,但也意味着容器如果被入侵,影响面很大。我个人的习惯是:像 sysdig、falco 这种需要大量系统调用的工具,直接用 --privileged;而像 tcpdump、ngrep 这类只需要 NET_RAW、NET_ADMIN 的,用 --cap-add 单独加就行,别动不动就上 --privileged。

1.2 内核模块、cgroup 版本、容器运行时这三个开关怎么核对

除了 seccomp,还有几个容易被忽略的内核层开关。第一个是内核模块。像 overlayfs、iptables、br_netfilter 这些模块如果没加载,Docker 网络和存储直接就废了。检查方法:

BASH
lsmod | grep br_netfilter
lsmod | grep overlay

如果没有输出,modprobe br_netfilter、modprobe overlay 手动加载一下,再写入 /etc/modules-load.d/ 让它开机自动加载。

第二个是 cgroup 版本。现在新装的系统大多是 cgroup v2,而 Docker 的默认配置可能还走的是 v1 兼容层。这会导致容器内的资源限制不生效,或者你在容器里看 /sys/fs/cgroup 时发现跟宿主机是同一个视角。跑排障工具时特别是要看 CPU 限制、内存压力的,必须确认 cgroup 版本一致。检查命令:

BASH
stat -fc %T /sys/fs/cgroup

输出 cgroup2fs 就是 v2,tmpfs 就是 v1。如果版本跟 Docker 不匹配,在 /etc/docker/daemon.json 里加 "exec-opts": ["native.cgroupdriver=systemd"] 调整。

第三个是你用的容器运行时本身。dockerd 默认用的 runc,但如果你装过 containerd、cri-o 或者 podman 的运行时,docker run 走的是不是同一套 runc 会影响容器的隔离行为。平时还好,真到了排障的时候,容器起不来说“failed to create task for container: failed to create shim task”,多半就是 runc 和内核版本不兼容,升级 runc 或者降级内核版本能解决。

1.3 时间同步与容器内时钟漂移

排障时经常要看日志时间戳,如果容器内时间和宿主机差了十几分钟,那排障就是在瞎搞。容器默认共享宿主机内核时钟,但有时候你在容器里跑了 NTP 服务或者手动改了时间,之后容器重启,时间反而漂了。检查方法就是进容器跑 date,跟宿主机对比,差太多就先 hwclock -s 同步硬件时钟,再 systemctl restart chronyd 或 ntpd。另外注意,排障容器里装的那些工具,很多依赖时间戳排序,比如 journalctl、sysdig -M,时间不同步会让你分析出来的结果完全错误。

2. 存储与资源:镜像拉取和容器启动的隐形杀手

2.1 /var/lib/docker 磁盘空间和 inode 检查

热词里很多人搜“docker镜像下载慢”“docker安装失败”,其实一半以上的原因不是网络,是磁盘满了。/var/lib/docker 是 Docker 默认的数据目录,镜像层、容器层、卷、日志全堆在这里。磁盘空间满会导致镜像拉取到一半失败、容器启动时报 no space left on device,但诡异的是有些工具比如 df -h 看还有几个 G,实际上 inode 已经耗尽了。

所以在跑排障运行时之前,我习惯先跑两条:

BASH
df -h /var/lib/docker
df -i /var/lib/docker

df -i 看的是 inode 使用率,如果超过 90%,哪怕磁盘空间还有,Docker 也没法写新文件了。这种情况常见于跑了很多小文件日志的容器,清理办法是找到 /var/lib/docker/containers 下超大的 *-json.log,用 truncate -s 0 清空,而不是直接 rm,否则 Docker 文件句柄还开着,磁盘空间不会释放。

提示:清理镜像别只 docker rmi 一堆没用的 dangling 镜像,docker system prune -a 会连所有没在用的镜像和构建缓存一起清掉,效果最明显。

2.2 内存、CPU、swap 限制对排障工具的影响

排障工具本身的内存占用往往不低。falco 跑起来基线就要几百 MB,sysdig 在抓取系统调用时,事件缓冲会随流量涨。如果你用 docker run -m 512m 去限制它,大概率跑几分钟就被 OOM Killer 干掉,docker inspect 看到的 State.OOMKilled 是 true,但你从容器日志里看不到什么明显报错。

我建议跑这类容器时不要限制内存,或者至少给到 2G 以上。CPU 限制也有讲究,--cpus=2 这种限制在高流量抓包时会丢包。真的需要限制的时候,优先用 --memory-reservation 做软限制,而不是 --memory 的硬限制。

另外 swap 也要注意。容器内的 swap 默认是继承宿主机的,但如果宿主机 vm.swappiness 很高,你的排障工具可能会频繁换页,导致性能分析出来的数据全是假象。排障时临时把 swappiness 调低:

BASH
sysctl vm.swappiness=10

2.3 镜像仓库连通性与下载慢的排查

“docker镜像下载慢”这个热搜词下面,大家问的基本都是怎么加速。但跑排障容器的时候,镜像下载慢还有另一个层面:你要用的那些排障工具镜像本身就很大,比如 sysdig/sysdig 大概快 1GB,带完整内核模块的更夸张。再加上国内拉 Docker Hub 本来就慢,超时大概率发生在层下载中途。

我的做法是:先确认网络能通 Docker Hub,curl -I https://registry-1.docker.io/v2/,看返回码;再配置 registry mirror。国内能用的镜像加速器现在不稳定,我自己一般直接用配置了镜像源的企业版 Harbor 或者系统自带的代理。配置方式是编辑 /etc/docker/daemon.json:

JSON
{
"registry-mirrors": ["https://你的加速器地址"],
"max-concurrent-downloads": 6
}

改完记得 systemctl daemon-reload && systemctl restart docker。还有个小技巧:如果某个镜像层一直下载失败,优先 docker pull 时加 --platform linux/amd64,能规避一部分架构不匹配导致的奇怪失败。

3. 网络连通性:容器里外都通才是排障前提

3.1 三种网络模式的适用场景与核对方法

跑排障容器时,网络模式的选择直接决定你“能不能看到想看的流量”。bridge 模式是默认的,容器通过 NAT 访问外部,外面访问容器需要端口映射。host 模式下容器直接用宿主机的网络栈,网卡、IP、端口全部共享,抓包工具特别适合用 host 模式,不然你在容器里 tcpdump 只能看到 bridge 网卡上的流量,看不到宿主机的 eth0。none 模式则完全没有网络,适合纯离线分析。

热词里有个“docker网络不通”,十次里有八次是 bridge 网络的 iptables 规则被清过了。Docker 安装时会往 iptables 里写一堆规则,如果你之后手动配了防火墙或者重启了 firewalld,这些规则可能就没了,容器连外网就连不上。排查方法:

BASH
iptables -L -n | grep DOCKER

如果这个链不存在,重启 Docker 能重建:systemctl restart docker。注意这个动作会影响所有在跑的容器,排障时机要选好。

3.2 容器内 DNS 和宿主机代理的坑

容器内的 /etc/resolv.conf 默认是继承宿主机配置的。但如果你宿主机配了 systemd-resolved,容器的 DNS 会被改写成 127.0.0.53,这在容器里是访问不到的,导致域名解析失败。判断办法:进容器 cat /etc/resolv.conf,看到 127.0.0.x 就手动指定 DNS:

BASH
docker run --dns 8.8.8.8 --dns 223.5.5.5 ...

另一个坑是宿主机有 HTTP 代理,容器里却没有配置。你宿主机能拉镜像,但容器里 curl 外网超时,多半是代理环境变量没传进去。跑容器时加 -e HTTP_PROXY=http://宿主机IP:端口,注意不能写 localhost,因为容器里的 localhost 是容器自己。这个坑我踩过很多次,排障工具要下载依赖包的时候尤其明显。

3.3 端口映射与宿主机防火墙的排查顺序

跑 Web 类的排障容器,比如用容器起一个 metabase 或 kodbox 做数据展示,映射端口不生效是很常见的。我的排查顺序是:

  1. 先 docker ps 看端口映射有没有显示 0.0.0.0:8080->80/tcp;
  2. 再 curl http://宿主机IP:8080 看通不通,通的话就是防火墙挡住外部访问;
  3. 不通就进容器 curl http://localhost:80,容器内通就是端口映射没生效,重启容器或删掉重建;
  4. 如果容器内都不通,看进程有没有起来,日志有没有报监听地址错误。

提示:容器内服务监听地址不要写 localhost,写成 0.0.0.0,否则端口映射到宿主机也访问不了。

4. 运行时状态与日志:容器起不来的三板斧

4.1 用 docker inspect 和 docker events 看启动失败根因

容器启动失败,最常见的就是直接 docker logs 看报错。但有些失败发生在容器启动之前,日志根本来不及产生。这时候 docker inspect 就是最直接的排查工具:

BASH
docker inspect <容器名> --format '{{.State.Status}} {{.State.Error}}'
docker inspect <容器名> --format '{{.State.OOMKilled}}'
docker inspect <容器名> --format '{{.HostConfig.Privileged}} {{.HostConfig.CapAdd}}'

上面三条命令分别看运行状态、是否被 OOM 杀掉、权限配置是否正确。docker events 也能用来实时观察容器生命周期里的报错事件,特别是那种启动后秒退的容器,docker events --filter container=xxx 能看到是 exec 失败还是清理阶段出错。

热词里说“docker服务启动失败”,这类问题大多是 daemon.json 配置写错了。检查方式很简单,先手动跑 dockerd --debug,看它到底在哪个环节崩,一般会有明确报错,比如日志路径不存在、iptables 版本不对之类。

4.2 日志驱动和日志轮转配置

排障容器最怕什么?最怕排到一半日志把磁盘塞满。Docker 默认的 json-file 日志驱动会把所有 stdout 都存到 /var/lib/docker/containers/<id>/*-json.log,不限制大小的话,一个跑了几天的容器日志能膨胀到几十 GB。

跑排障运行时,我强烈建议启动参数里带上日志限制:

BASH
docker run --log-driver json-file --log-opt max-size=50m --log-opt max-file=3 ...

也可以在 /etc/docker/daemon.json 里全局配置:

JSON
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}

这样容器日志最多占 150MB,排障跑完该分析的也早就拿到 stdout 了。另外注意,有些工具像 sysdig 的日志输出是二进制事件流,存到 json-file 里会被转义得乱七八糟,这种场景建议直接 docker run 时用 -v 把输出重定向到宿主机文件里,或者用 --log-driver none,自己管理输出。

4.3 容器内系统账号和 locale 导致的启动失败

有一种启动失败是排障容器特有的:镜像里自带的服务会在入口脚本里切换用户,比如 sysdig 镜像默认用 root 跑,但配了不同的 UID,挂载了宿主机目录之后权限就乱了。报错一般是 chown: invalid user 或者 Permission denied。

碰上这种问题,优先用 --user root 强制指定用户,或者在挂载卷时先 chown 宿主机目录:

BASH
chown -R 1000:1000 /宿主机/数据目录
docker run -v /宿主机/数据目录:/data:Z ...

:Z 选项在 SELinux 生效的系统上特别重要,不加上挂载的目录容器里根本读不了。热词里“docker权限错误怎么解决”问的就是这一类,权限报错别光盯着容器看,先确认宿主机目录权限和 SELinux 上下文。

还有一个容易忽略的是 locale。有的镜像精简到没装 locales,容器里跑中文程序或者某些日志分析工具会报 locale: Cannot set LC_ALL to zh_CN.UTF-8,然后直接退出。跑排障前最好是强制设置环境变量 -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8,省得临时装依赖。

5. 常见问题速查与排障顺序

5.1 高频故障速查表

症状 可能原因 快速排查命令 解决动作
容器启动报 permission denied seccomp/capability 限制 docker inspect 看 CapAdd 和 Privileged 加 --privileged 或 --cap-add SYS_ADMIN
镜像下载到一半失败 磁盘/inode 满或网络不稳 df -h && df -i 清日志、清理 dangling 镜像
容器内 DNS 解析失败 resolv.conf 指向 127.0.0.53 cat /etc/resolv.conf docker run --dns 8.8.8.8
容器内访问外网超时 代理未传入或 iptables 被清 iptables -L -n | grep DOCKER 加 -e HTTP_PROXY 或重启 docker
容器被 OOM 杀掉 内存限制太小 docker inspect --format '{{.State.OOMKilled}}' 去掉 -m 限制或加 --memory-swap
Docker 服务起不来 daemon.json 语法错误 dockerd --debug 修正 JSON 配置后 reload
工具报告内核模块缺失 overlay/br_netfilter 未加载 lsmod | grep br_netfilter modprobe br_netfilter
端口映射外部不通 防火墙或服务监听地址错误 curl http://127.0.0.1:端口 改监听 0.0.0.0,放行防火墙端口

这表里其实已经覆盖了大部分搜索热词里的问题,我每次跑排障容器之前,都会快速过一遍这些检查项,几分钟内能排除掉八成环境问题。

5.2 我建议的排障核对顺序

具体操作顺序我习惯这样走:

  1. 先看宿主机状态:磁盘、inode、时间、内核版本、cgroup 版本;
  2. 再确认 Docker 本身健康:docker info、docker ps、docker events 有没有异常;
  3. 然后检查权限配置:privileged、cap-add、seccomp、SELinux 挂载标签;
  4. 接着验证网络:宿主机上 curl 外网、容器内 curl 外网、DNS 是否正常;
  5. 最后启动容器:日志限制、挂载卷、环境变量一次性配好,别跑起来再改。

这套顺序看着繁琐,但能省掉很多来回拉扯的时间。有次我在容器里跑一个 SQL Server 镜像(热搜里那个 sql2008),一直报内存不足,折腾了两天,后来发现是宿主机 cgroup v2 和 Docker 的驱动配置不匹配,内存限额没生效导致。从那以后,我再也不跳过 cgroup 版本的检查。

5.3 最后的经验:排障容器设计要“一次配对”

踩了这么多坑之后,我最大的体会是:排障容器不能当普通容器对待,启动参数要一次性配齐。我自己的固定模板大概是这样的:

BASH
docker run -it --rm \
--privileged \
--pid=host \
--net=host \
--log-driver json-file --log-opt max-size=50m --log-opt max-file=3 \
-e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 \
-v /:/host:ro \
sysdig/sysdig:latest

--pid=host 能看到宿主机全部进程,--net=host 能直接抓宿主机网卡流量,-v /:/host:ro 只读挂载宿主机根目录,排障时分析问题不用再一层一层找关联了。有同事问我为什么跑个工具要这么重的权限,我的回答是:排障窗口就那几分钟,如果因为权限不够看不到关键信息,排查不彻底,后面线上出问题亏得更多。当然这仅限于你自己清楚镜像来源、并且排障完就销毁的临时容器,别用这种配置跑长期在线的业务容器。

最后再分享一个小技巧:把这条命令封装成一个 shell 函数或者脚本放在 ~/.bashrc 里,取名叫 kdebug。每次需要排障的时候就 kdebug 直接进容器,所有参数都不用重新敲。次数多了你会发现,真正花在排查环境问题上的时间,从原来的半小时降到了五分钟以内。剩下的时间,都花在分析问题本身上了。

相关推荐

基于Python+Open CV的手势识别算法设计

伴随着人工智能时代的到来,人机交互的领域也逐渐成为研究的一大方向;其中,手势识别是人机交互领域的一项关键技术,自此手势识别也迎来了一波高潮,近几年无论是在消费领域、电子领域、数码领域、家电领域甚至汽车领域上,都能或多或少的见到手势识别的身影。 手势识别在设计智能高效的人机界面方面具有至关重要的作用, 目前手势识别已应用到手语识别、智能监控、到虚拟现实等各个领域,手势识别的原理都是利用各种传感器(例如红外、摄像头等)对手部的形态进行捕捉并进行建模,形成一个模型信息的序列帧,而后将这些信息序列转换为机器能够识别的相对应指令(例如打开、切换菜单、移动等)来完成控制。 采用Python的集成开发环境Pycharm进行本次课程设计,在Pycharm中进行需要库(模块)的下载,调取电脑摄像头,按帧读取摄像头采集到的头像,形态学处理,图像旋转(由于摄像头采集到的图像是镜像,需要用cv2.flip将摄像头采集到的图像进行处理),选取图片中固定位置作为手势输入,用红线画出手势识别框,基于hsv的肤色检测,进行高斯滤波,找出轮廓,求出图像中手势的凹凸点,手指间角度求取。

努力学习前端的小陈

ROS-CV-ImageConverter:将ROS图像转换为OpenCV(cv :: Mat)类型的图像

ROS-CV图像转换器 依存关系 cv_bridge(ROS软件包)

Willis Wang

计算机视觉图像处理-CV2与图像插值算法

用最近邻插值与双线性插值原理完成图像缩放 最近邻插值,是指将目标图像中的点,对应到源图像中后,找到最相邻的整数点,作为插值后的输。缺点:使用最近邻插值算法处理图像放大时,在图象中可能出现明显的块状效应 双线性插值,在两个方向上做三次线性插值。使用双线性插值算法处理图像放大时,在图象中可能出现马赛克 代码 import cv2 img = cv2.imread(’./xiabang.jpg’, cv2.IMREAD_UNCHANGED) #缩放比例 scale_percent = 0.3 width = int(img.shape[1] * scale_percent) height = int

weixin_38697274

基于水平集CV模型的图像分割(Matlab代码)

基于水平集CV模型的图像分割(Matlab代码),全套代码

小宇QAQ

导向滤波(guide_filter)的python实现

导向滤波(guide_filter)的python实现,可以结合暗通道;理论实现比较好的图像去噪效果。

zachzhan_g
返回顶部
飞翔的十号
博客等级 码龄8年 37粉丝 8,249原创