认识容器:从一个 nginx 容器看透 Namespace 与 Cgroup
认识容器从一个 nginx 容器看透 Namespace 与 Cgroup系列开篇| 容器技术底层原理深度实操系列实验环境华为云 FlexusX (8vCPU/16GiB) · Ubuntu 24.04 Server · 内核 6.8.0-106-generic · Docker 29.1.3 · Cgroup v2本文所有命令输出均为真机实录。一、为什么容器问题的答案不在 docker 命令里如果你用容器的时间够长一定遇到过这些灵异事件容器里kill -9 11 号进程纹丝不动容器内存明明没用满进程却被 OOM Killer 干掉了加了 CPU 限制容器还是卡得像蜗牛改了/proc/sys/net下的参数重启容器就失效。翻遍 Docker 文档也找不到答案——因为容器不是虚拟机它只是 Linux 内核几种机制组合出来的进程包装。所有这些问题的根源都在内核的 Namespace、Cgroup、OverlayFS 里。一句话概括容器的本质容器 Namespace(隔离视图) Cgroup(限制资源) 联合文件系统(打包 rootfs)这篇开篇文章我们就用一个最普通的 nginx 容器把这三板斧逐一解剖给你看。二、容器进程就是宿主机上的普通进程先启动一个 nginx$dockerrun-d--nameintro-nginx-p8080:80 nginx 18a43df07a8cceeb88207c4493ad281d68bbcd53d2e14140e76c87544ee89a81 $dockerps--formattable {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}NAMES IMAGE STATUS PORTS intro-nginx nginx Up2seconds0.0.0.0:8080-80/tcp,[::]:8080-80/tcp很多人以为容器像虚拟机一样里面跑着一个操作系统。我们直接在宿主机上找到这个容器的主进程$PID$(dockerinspect intro-nginx--format{{.State.Pid}})$echo$PID18285$ps-opid,ppid,cmd-p18285PIDPPIDCMD1828518262nginx: master process nginx-gdaemon off;看到了吗所谓容器在宿主机看来就是 PID 18285 这个普普通通的 nginx 进程父进程是 containerd-shimPID 18262。没有虚拟化层没有 Guest OS就是一个进程。这是理解一切容器问题的第一性原理你排查容器问题本质上是在排查一个组Linux 进程的问题。三、第一板斧Namespace——让进程看不见彼此既然是普通进程为什么容器里看不到宿主机的其他进程、网卡、文件答案是 Namespace。用lsns看看 PID 18285 拥有哪些 namespace$ lsns-p18285NS TYPE NPROCS PIDUSERCOMMAND4026531834time2111root /sbin/init noibrs4026531837user2111root /sbin/init noibrs4026532496mnt918285root nginx: master process nginx-gdaemon off;4026532497uts918285root nginx: master process nginx-gdaemon off;4026532498ipc918285root nginx: master process nginx-gdaemon off;4026532499pid918285root nginx: master process nginx-gdaemon off;4026532500cgroup918285root nginx: master process nginx-gdaemon off;4026532501net918285root nginx: master process nginx-gdaemon off;这个输出信息量很大逐行拆解Namespace编号隔离了什么在容器里的体现mnt4026532496 (独立)挂载点/文件系统视图容器有自己的/看不到宿主机文件uts4026532497 (独立)主机名容器里 hostname 是容器 IDipc4026532498 (独立)System V IPC/消息队列容器间共享内存互不可见pid4026532499 (独立)进程号空间nginx 在容器里是 1 号进程cgroup4026532500 (独立)cgroup 根视图容器里看/proc/1/cgroup是0::/net4026532501 (独立)网卡/路由/iptables容器有自己的 eth0time4026531834 (共享)系统时钟与宿主机相同NPROCS211user4026531837 (共享)uid/gid 映射默认与宿主机共享这是安全模块的重点注意两个细节6 个 namespace 是独立的NPROCS9只有容器里的 9 个进程而time 和 user namespace 默认与宿主机共享NPROCS211全机进程。这就是为什么容器里改系统时间会影响宿主机、容器里的 root 默认就是宿主机 root——后面安全篇会专门讲这个坑。namespace 本质是内核里的一组数据结构进程的task_struct-nsproxy指向它们。clone()时传入CLONE_NEWPID等 flag 就能创建新 namespace——Docker 干的就是这件事。验证 PID Namespace容器内的1 号进程$dockerexecintro-nginxcat/proc/1/cgroup0::/在容器里nginx 自己就是 1 号进程而且它看到的 cgroup 路径是0::/根——但我们马上会在宿主机看到真相。验证 Net Namespace不进容器也能进入容器网络docker exec不是什么黑魔法nsenter就能手工进入任何 namespace$ nsenter-t18285-nip-4addr show eth02: eth0if9:BROADCAST,MULTICAST,UP,LOWER_UPmtu1500qdisc noqueue state UP inet172.17.0.3/16 brd172.17.255.255 scope global eth0-t 18285 -n表示进入目标进程的 net namespace。看到了容器的172.17.0.3而且注意eth0if9——容器的 eth0 其实是一个veth 设备对的一端另一端 if9 插在宿主机 docker0 网桥上网络篇会顺着这根网线完整排查一次网络不通问题。所以记住docker exec nsenter 进入全部 namespace 执行命令。当容器里没有调试工具时比如上面 nginx 镜像里连 ps 都没有sh: 1: ps: not foundnsenter只进入部分 namespace就能用宿主机的工具排查容器——这是容器排障最重要的技巧没有之一。四、第二板斧Cgroup——给进程戴上紧箍咒Namespace 管看不见Cgroup 管用不多。Ubuntu 24.04 默认使用Cgroup v2统一层级容器对应的 cgroup 目录在$cat/proc/18285/cgroup0::/system.slice/docker-18a43df07a8ccee...89a81.scope $ls/sys/fs/cgroup/system.slice/docker-18a43df07a8c*.scope/|head-12cgroup.controllers cgroup.events cgroup.freeze cgroup.kill cgroup.max.depth cgroup.max.descendants cgroup.pressure cgroup.procs cgroup.stat cgroup.subtree_control cgroup.threads cgroup.type刚才容器里看到的0::/和宿主机看到的/system.slice/docker-xxx.scope是同一个 cgroup 的两个视角——cgroup namespace 把路径根化了。这个容器没加任何资源限制所以$cat.../cpu.max max100000# 不限 CPU每 100ms 周期内可用时间无上限$cat.../memory.max max# 不限内存docker run --cpus1 -m 512m干的事情就是把这两个文件分别改成100000 100000和536870912。仅此而已——Docker 的资源限制参数全都是 cgroup 文件的搬运工。Cgroup v2 与老教程里 v1 最大的区别Cgroup v1Cgroup v2 (本系列环境)层级结构每个子系统一棵树 (/sys/fs/cgroup/cpu,/memory…)统一一棵树CPU 限制cpu.cfs_quota_us/cpu.cfs_period_uscpu.max一个文件两个值内存限制memory.limit_in_bytesmemory.max磁盘限速blkio.throttle.*io.max且支持 buffered IO 限速这个差异会贯穿整个系列——网上大量容器教程还停留在 v1照着敲会找不到文件。五、第三板斧联合文件系统——镜像分层的真相容器的 rootfs 从哪来看挂载$mount|grepoverlay|grep18a43df07a8c overlay on /var/lib/docker/rootfs/overlayfs/18a43df07a8c...typeoverlay(rw,relatime,lowerdir/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/25/fs:.../snapshots/24/fs:.../snapshots/23/fs:.../snapshots/22/fs:.../snapshots/21/fs:.../snapshots/20/fs:.../snapshots/15/fs:.../snapshots/13/fs,upperdir.../snapshots/26/fs,workdir.../snapshots/26/work,nouserxattr)三个关键角色容器看到的 / (merged 合并视图) ┌───────────────────────────────┐ │ upperdir (snapshots/26) │ ← 可写层容器所有写操作落在这 ├───────────────────────────────┤ │ lowerdir (snapshots/25) │ ← nginx 镜像第 8 层只读 │ lowerdir (snapshots/24) │ ← 第 7 层只读 │ ... │ │ lowerdir (snapshots/13) │ ← Debian 基础层只读 └───────────────────────────────┘nginx 镜像的 8 个 layer 是 8 个只读 lowerdir容器启动时在顶上加一层可写 upperdirOverlayFS 把它们叠成一个完整的根文件系统。10 个容器共享同一份镜像层每个只多一层薄薄的可写层——这就是容器比虚拟机轻的核心原因。一个值得注意的时代变化这台机器的 Docker 29.1.3 已经把镜像层交给containerd snapshotter管理路径在/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/docker inspect里甚至没有了经典的GraphDriver字段$dockerinspect intro-nginx--format{{json .GraphDriver.Data}}template parsing error: map has no entryforkeyGraphDriver如果你在新版本 Docker 上照老教程找/var/lib/docker/overlay2/找不到东西原因就在这。存储篇会手工mount -t overlay复现整个 Copy-on-Write 过程。最后验证服务本身当然是正常的$curl-s-o/dev/null-wHTTP %{http_code} in %{time_total}s\nhttp://127.0.0.1:8080 HTTP200in0.000629s六、把三板斧拼起来宿主机 Linux 内核 (6.8.0) ──────────────────────────────────────────────────── │ ├─ dockerd ── containerd ── containerd-shim (18262) │ │ clone(CLONE_NEWPID|NEWNS|NEWNET|...) │ ▼ │ nginx master (宿主机视角 PID 18285) │ nginx workers ×8 │ ├─ Namespace: pid/mnt/net/uts/ipc/cgroup 独立 │ time/user 与宿主机共享 (注意!) │ ├─ Cgroup v2: /system.slice/docker-id.scope │ cpu.max / memory.max / io.max ... │ └─ OverlayFS: lower(镜像8层,只读) upper(可写层) → merged rootfsdocker run 的本质解包镜像 → overlay 挂载 rootfs → clone 带 namespace flag 的进程 → 写 cgroup 文件 → chroot/pivot_root 到 merged 目录 → exec 你的 ENTRYPOINT。七、这个认知能帮你解决什么问题有了容器进程的心智模型本系列后面每一个问题都有了统一的排查框架问题现象对应内核机制系列文章kill 不掉 1 号进程PID namespace 内核信号特权进程篇僵尸进程堆积init 进程的 wait 责任进程篇CPU 限了还是慢CFS bandwidth throttle / D 状态进程篇容器被莫名杀死Memory Cgroup OOM内存篇内存总在临界点Page Cache 计入 memory.current内存篇写文件变慢OverlayFS copy-up存储篇磁盘被容器写满可写层无配额存储篇改内核参数不生效/proc/sys 的 netns 归属与只读挂载网络篇网络不通veth → 网桥 → iptables 链路网络篇privileged 滥用Capabilities安全篇排查路径永远是现象 → 找到宿主机上的进程 → 看它的 namespace/cgroup → 定位内核机制 → 解决。小结容器是宿主机上的普通进程不是轻量级虚拟机Namespace 隔离视图默认 6 独立 time/user 共享Cgroup 限制资源OverlayFS 提供分层 rootfsdocker inspect --format {{.State.Pid}}nsenter/sys/fs/cgroup是容器排障三件套Ubuntu 24.04 已是 Cgroup v2 containerd snapshotter 时代老教程的路径要更新了。思考题既然容器进程对宿主机可见那在宿主机上直接kill -9 18285会发生什么容器会退出吗重启策略会拉起它吗欢迎在评论区讨论答案在进程篇揭晓。本文是容器技术底层原理深度实操系列开篇全系列 16 篇覆盖进程、内存、存储、网络、安全五大模块与 perf/ftrace/eBPF 内核调试工具专题。

相关新闻

人脸识别在校园考勤系统的应用与优化

人脸识别在校园考勤系统的应用与优化

1. 项目背景与核心价值在大学校园信息化建设中,学生考勤管理一直是个让人头疼的问题。传统的手工点名不仅效率低下,还容易出现代签、漏签等情况。我在某高校信息化部门工作时,就经常接到教师反映考勤数据不准确的投诉。而请假和选课系统虽然已…

2026/7/25 15:28:23 阅读更多 →
eBPF 与 bpftrace:更深入地观测内核

eBPF 与 bpftrace:更深入地观测内核

eBPF 与 bpftrace:更深入地观测内核实验环境:Ubuntu 24.04 / 内核 6.8.0-106-generic / Cgroup v2 / 华为云 FlexusX 8C16G 本文所有命令输出均来自真实实验机,可直接复现。bpftrace 版本 v0.20.2,内核自带 BTF。一、引子&#xf…

2026/7/25 15:28:23 阅读更多 →
ONNX运行时优化生成式AI模型部署实践

ONNX运行时优化生成式AI模型部署实践

1. ONNX运行时在生成式AI中的应用全景 在生成式AI技术爆发的当下,模型跨平台部署已成为行业刚需。ONNX(Open Neural Network Exchange)作为中立的开放格式,正在成为连接模型训练与生产部署的"通用语言"。我亲历过多个从…

2026/7/25 15:28:23 阅读更多 →

最新新闻

如何为Nodejs后端服务配置Taotoken统一大模型调用接口

如何为Nodejs后端服务配置Taotoken统一大模型调用接口

如何为Nodejs后端服务配置Taotoken统一大模型调用接口 对于Node.js开发者而言,将大模型能力集成到后端服务中已成为常见需求。直接对接不同厂商的原生API往往意味着需要管理多个密钥、处理不同的调用格式,并在代码中维护复杂的切换逻辑。Taotoken平台提…

2026/7/25 15:41:29 阅读更多 →
告别手动下载:3步自动化安装Mac Boot Camp驱动

告别手动下载:3步自动化安装Mac Boot Camp驱动

告别手动下载:3步自动化安装Mac Boot Camp驱动 【免费下载链接】brigadier Fetch and install Boot Camp ESDs with ease. 项目地址: https://gitcode.com/gh_mirrors/bri/brigadier 还在为Mac电脑安装Windows驱动而烦恼吗?每次安装Boot Camp都需…

2026/7/25 15:41:29 阅读更多 →
初创团队如何利用Taotoken实现多模型API的成本统一管理

初创团队如何利用Taotoken实现多模型API的成本统一管理

初创团队如何利用Taotoken实现多模型API的成本统一管理 对于资源有限的初创团队而言,技术决策不仅要考虑模型效果,更要关注成本的可控性。当业务需要同时接入多个不同厂商的大模型时,随之而来的便是分散的API密钥、独立的计费账单和复杂的用…

2026/7/25 15:41:29 阅读更多 →
AMD Ryzen 电源管理终极调优:释放笔记本性能潜力的完整方案

AMD Ryzen 电源管理终极调优:释放笔记本性能潜力的完整方案

AMD Ryzen 电源管理终极调优:释放笔记本性能潜力的完整方案 【免费下载链接】RyzenAdj Adjust power management settings for Ryzen APUs 项目地址: https://gitcode.com/gh_mirrors/ry/RyzenAdj 你是否曾经觉得自己的AMD Ryzen笔记本性能被限制住了&#x…

2026/7/25 15:41:29 阅读更多 →
AI代码助手深度对比:Codex与Claude Code如何选?

AI代码助手深度对比:Codex与Claude Code如何选?

1. 先搞清楚它们到底是什么,以及为什么值得你花时间 如果你最近在技术社区、社交媒体或者同事的闲聊里,频繁听到“小龙虾”、“Codex”、“Claude Code”这些词,感觉一头雾水,那这篇文章就是为你准备的。别被这些花哨的名字吓到,它们本质上都是 AI驱动的代码助手 ,目标…

2026/7/25 15:41:29 阅读更多 →
日化美妆行业数智化转型:PLM系统与AI实验助手的应用

日化美妆行业数智化转型:PLM系统与AI实验助手的应用

1. 行业现状与核心痛点剖析日化美妆行业正经历着从传统研发模式向数字化研发的转型阵痛期。根据第三方机构调研数据显示,2023年行业新产品开发周期平均仍长达18-24个月,而消费者需求变化周期已缩短至6-8个月。这种"研发速度追不上市场变化"的矛…

2026/7/25 15:40:29 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/25 5:08:22 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/25 5:13:53 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻