Docker 的组件由五层组件组成流水线来构建一个完整的容器系统每层组件只关注自己擅长的事情。角色组件干什么顾客Docker 客户端Docker CLI敲命令即点菜前台收银Docker 守护进程(docker daemon )接单、协调后厨、管理所有菜品后厨主管容器运行时 (containerd)菜品拆分成具体步骤厨师OCI运行时 (runc)真正做菜的人服务员容器垫片Containerd-shim上菜之后持续关注你这桌别让菜凉了为什么使用C/S架构当用户输入docker run nginx 发生了什么。你敲 docker run nginx → CLI 发请求给 dockerd → dockerd 通过 gRPC 转交 containerd → containerd 拉起 shim → shim 调用 runc → runc 调用 Linux 内核隔离技术创建并运行 Nginx 容器。这样做的好处是解耦。各个组件职责分离与良好的扩展性其核心组件并非单一进程而是协同工作的生态系统。流程关键点解析协议分层Client与Daemon间使用REST APIDaemon与containerd间使用gRPC这是为了兼顾外部接口的通用性和内部调用的高性能。职责转移dockerd负责高层管理但具体的容器创建任务通过gRPC委托给了containerd实现了架构解耦。进程托管runc创建容器后即退出由containerd-shim作为父进程托管容器这确保了即使dockerd重启容器也不会被意外终止。容器的完整的生命周期第1️⃣步你敲了命令docker run -d --name web -p 8080:80 nginxDocker CLI 会把这条命令解析成一个HTTP REST请求发送给dockerd第2️⃣步dockerd 做前台收银该做的事Dockerd 收到请求后需要做一系列协调工作镜像管理检查本地是否存在 nginx 镜像不存在则向 Registry 发起拉取完成镜像分层落地存储。存储准备规划 overlay2 文件系统结构调用 containerd snapshotter 创建 lowerdir、upperdir、workdir 目录为后续联合挂载做准备。网络准备创建 veth pair 虚拟网卡一端预置入容器网络命名空间、一端接入 docker0 网桥分配容器内网 IP配置 iptables NAT 端口转发规则实现-p 8080:80端口映射。配置整合合并镜像内置元数据ENV、CMD、EXPOSE与命令行启动参数整理容器运行配置模板。下发请求整理完毕全部容器配置通过 gRPC 协议下发创建任务给 containerd。关键点dockerd 本身不直接操作 Linux 内核创建容器它只是个总调度。第3️⃣步contarinerd 分发任务containerd 分发任务后厨主管统筹派活 containerd 通过 gRPC 接收到 dockerd 下发的容器创建请求执行内部任务拆解分发校验镜像元数据确认容器文件系统运行环境就绪为本次容器单独创建一个专属containerd-shim垫片进程把整理好的容器运行参数传递给 shim由 shim 作为中间代理去调用 runc规避 containerd 主进程与容器强耦合问题。补充作用后续即便 containerd 进程重启、崩溃容器也不会被终止由 shim 持续托管容器状态。第4️⃣步runc真正创建容器Runc 是整个链路最底层的一环调用Linux 内核系统调用来创建容器。创建Namespace隔离环境配置Cgroup实现资源管控设置rootfs(通过Overlay2 挂载)通过chroot切换容器根目录启动主进程完成容器启动工作后自身退出并将容器运行状态反馈给 containerd-shim第5️⃣步containerd-shim 接管containerd-shim成为容器 PID1 进程的父进程长期驻留后台托管整个容器生命周期。containerd-shim (PID: 12345)└── nginx master (PID: 1, 容器内的1号进程)└── nginx worker└── nginx worker隔离守护进程保容器不挂 shim 作为中间隔离层容器归属 shim 管理。即使 containerd 重启或崩溃容器依然正常运行不被杀死。回收子进程防止僵尸进程 自动调用wait()回收容器退出进程读取退出码上报给 containerd避免产生僵尸进程。托管容器 IO 流与交互 统一接管容器标准输入输出支撑docker logs查看日志、docker exec进入容器交互维持通信通路。转发控制信号 宿主机docker stop/kill信号先给到 shim由 shim 转发给容器主进程实现容器正常启停。实时上报容器状态 持续将运行、暂停、异常、退出等状态通过 gRPC 上报给 containerd最终同步到 dockerd保证docker ps状态准确。生命周期收尾 容器销毁终止后shim 完成清理工作并自动退出整个容器流程闭环结束。厨师 (runc) 做完菜下班服务员 (shim) 留在桌边全程照看饭菜容器不会因为后厨主管 (containerd) 离岗就撤掉客人呼叫添茶、撤盘操作容器、查看日志由服务员中转菜品吃完收盘容器停止服务员收尾离岗。镜像分层理解容器的OverlayFS联合文件系统Docker 的镜像不是一个巨大的文件而是一堆只读的薄层堆叠在一起。分层的目的是为了让项目在不同环境下复用。最佳实践建议优化Dockerfile将变动频率低的层放在前面如基础镜像FROM变动频率高的层放在后面如添加应用ADD以最大化利用层缓存加速构建。合并层在构建的最后阶段可以考虑使用多阶段构建或docker-squash工具减少层数以精简镜像大小。Overlay2联合文件系统是目前Liunx 主流的存储驱动。核心概念是lowerdir (只读层) 多容器公用镜像底层节约磁盘。Upperdir (读写层)容器专属修改层数据隔离删除容器仅删除该层Workdir (工作目录层) 写操作临时事务缓冲防止文件损坏用户不可见。Merged (合并视图)上下层叠加后统一目录读取自上而下查找修改原有文件触发写时复制改动最终落到 upperdir。书本叠纸比喻底层课本lowerdir是共用只读镜像专属草稿纸upperdir存放容器所有修改垫板workdir处理写时复制临时事务肉眼所见完整书本就是两层合并后的merged统一视图。配置示例/etc/docker/daemon.json{ storage-driver: overlay2, storage-opts: [ overlay2.override_kernel_checktrue ] }网络容器怎么和外界通信Docker 提供了多种网络模式当你运行一个容器而未指定网络时Docker会将其连接到默认的bridge网络对应网桥docker0。其内部实现如下宿主机外部网络eth0: 192.168.1.100 │ ┌────┴────┐ │ NAT │ ← iptables 规则 └────┬────┘ │ ┌────┴────┐ │ docker0 │ (172.17.0.1/16) └──┬───┬──┘ │ │ veth1 veth2 │ │ eth0 eth0 (容器A) (容器B) 172.17.0.2 172.17.0.3网络创建与通信流程Docker创建docker0虚拟网桥默认网段172.17.0.0/16。为每个容器创建一对veth设备一端放入容器的Network Namespace命名为eth0另一端连接到docker0。docker0 只是桥接模式容器的网关。为容器的eth0分配IP如172.17.0.2。通过iptables设置NAT规则实现容器访问外网和端口映射-p 8080:80。使用默认bridge网络时容器间只能通过IP通信。创建自定义网络可以启用容器名称发现。原理 Docker为每个自定义网络内置了一个DNS服务器。当容器app2解析app1时Docker DNS会返回app1在该网络中的IP地址。数据持久化为什么需要 Volume容器的可写层和容器的生命周期绑定了当容器删除释放了数据也会删除。而且即使容器还在可写层的性能也不如直接操作系统文件系统。生产环境最佳实践使用命名Volumedocker volume create app_data然后通过-v app_data:/app/data挂载。避免使用匿名Volume不利于管理和迁移。谨慎使用Bind Mount仅在需要直接编辑主机配置文件或进行开发调试时使用docker run -v /host/test:/container/test nginx。-v 宿主机路径容器路径。Bind Mount 绑定挂载用户指定宿主机已有路径直接映射到容器自由度高适合挂载本地代码与外部配置Docker Volume 数据卷Docker 自行管理存储空间通过 docker volume create 创建更适合业务数据持久化不用手动维护宿主机目录。内核基石Namespace与Cgroups容器隔离的本质是Linux内核中的N与C特性的组合使用。Namespace 名称空间: 给进程创造一个受限的视野使用了6种Namespace- PID Namespace: 它认为自己的PID是1- Mount Namespace: 独立的文件系统挂载点 /、/proc、/sys- Network Namespace: 独立的网络设备、IP、端口- UTS Namespace: 独立的主机名- IPC Namespace: 独立的信号量、消息队列、共享内存进程间通信- User Namespace: 独立的用户和组ID映射可选增强安全这不是虚拟机 容器进程和宿主机进程运行在同一个内核上只是通过 Namespace 限制了它能看到什么。Cgroups给进程上枷锁 解决的是进程组能用多少资源用于限制、统计和隔离进程组的资源CPU、内存、IO等。一句话总结Namespace 决定容器能看到什么Cgroups 决定容器能用多少OverlayFS 给容器一个独立的文件系统视图。三者组合就是容器。真实排查用架构知识定位问题问题1容器启动失败报错OCI runtime create failed思路OCI 运行创建失败说明到达了底层runc ,但是调用内核失败了。docker info 查看namespace、overlay 是否正常查看详细日志 docker --debug 查看containerd日志journalctl -u containerd。常见原因内核版本太低缺少内核模块磁盘满cgroup版本不兼容。问题2容器内域名解析失败思路进入容器-exec 查看域名配置文件是否配置正常/etc/resolv.conf;Ping 一下DNS服务器确认和服务器数据收发正常检查防火墙是否拦截了检查宿主机的DNS是否正常。