765 字
2 分钟
Docker 容器启动流程:从镜像到运行
你执行 docker run nginx,一秒后容器就跑起来了。但这一秒里发生了什么?镜像是怎么变成运行中的进程的?为什么容器里的进程看不到宿主机上的其他进程?为什么一个容器只能用 2G 内存,就算宿主机有 64G?本文深入剖析 Docker 容器启动的完整流程。
Docker 架构概览
flowchart TB
subgraph 客户端
A[Docker CLI]
end
subgraph Docker Host
B[Docker Daemon] --> C[Containerd]
C --> D[Containerd-shim]
D --> E[runc]
E --> F[容器进程]
G[镜像存储] --> B
H[网络] --> B
I[卷] --> B
end
subgraph 内核
J[namespace]
K[cgroup]
L[UnionFS]
end
A -->|API| B
E --> J
E --> K
G --> L
一、Docker 组件架构
1.1 组件职责
| 组件 | 职责 |
|---|---|
| docker CLI | 命令行客户端 |
| dockerd | Docker 守护进程,API 入口 |
| containerd | 容器运行时管理 |
| containerd-shim | 容器生命周期管理 |
| runc | 实际创建容器的工具 |
1.2 调用链路
sequenceDiagram
participant C as docker CLI
participant D as dockerd
participant T as containerd
participant S as shim
participant R as runc
participant K as Kernel
C->>D: docker run nginx
D->>D: 解析参数
D->>T: 创建容器请求
T->>T: 准备 bundle
T->>S: 启动 shim
S->>R: runc create
R->>K: 创建 namespace
R->>K: 设置 cgroup
R->>K: 挂载文件系统
R-->>S: 容器创建完成
S->>R: runc start
S-->>T: 容器运行
T-->>D: 返回容器 ID
D-->>C: 显示输出
二、镜像加载
2.1 镜像结构
Docker 镜像由多个只读层组成:
flowchart TB
subgraph 镜像层
A[nginx:latest]
B[基础层: Debian]
C[中间层: 依赖]
D[应用层: nginx]
end
subgraph 容器层
E[可写层 R/W]
end
A --> B --> C --> D --> E
2.2 联合文件系统
# 查看镜像层docker image inspect nginx
# 输出"Layers": [ "sha256:abc123...", # 基础层 "sha256:def456...", # 中间层 "sha256:789abc..." # 应用层]UnionFS 工作原理:
flowchart LR
subgraph OverlayFS
A[upper<br/>可写层] --> D[合并视图]
B[lower1<br/>只读层] --> D
C[lower2<br/>只读层] --> D
end
E[写入文件] --> A
D --> F[读取文件]
2.3 镜像拉取流程
sequenceDiagram
participant C as Client
participant D as Docker Daemon
participant R as Registry
C->>D: docker pull nginx
D->>R: GET /v2/nginx/manifests/latest
R-->>D: 返回 manifest
D->>D: 解析 manifest,获取层列表
loop 每一层
D->>R: GET /v2/nginx/blobs/sha256:xxx
R-->>D: 返回层数据
D->>D: 解压并存储层
end
D-->>C: Pull complete
三、容器创建流程
3.1 docker run 执行流程
flowchart TB
A["docker run nginx"] --> B[检查本地镜像]
B --> C{镜像存在?}
C -->|否| D[拉取镜像]
C -->|是| E[创建容器配置]
D --> E
E --> F[生成容器 ID]
F --> G[创建文件系统层]
G --> H[准备 OCI bundle]
H --> I[调用 containerd]
I --> J[创建 namespace]
J --> K[设置 cgroup]
K --> L[挂载文件系统]
L --> M[执行入口命令]
M --> N[容器运行]
3.2 OCI Bundle
# OCI bundle 目录结构nginx-bundle/ config.json # 容器配置 rootfs/ # 根文件系统 bin/ etc/ usr/ ...config.json 示例:
{ "ociVersion": "1.0.2", "process": { "terminal": true, "user": { "uid": 0, "gid": 0 }, "args": ["nginx", "-g", "daemon off;"], "env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"], "cwd": "/" }, "root": { "path": "rootfs", "readonly": false }, "linux": { "uidMappings": [{ "containerID": 0, "hostID": 1000, "size": 1 }], "gidMappings": [{ "containerID": 0, "hostID": 1000, "size": 1 }], "namespaces": [ { "type": "pid" }, { "type": "network" }, { "type": "ipc" }, { "type": "uts" }, { "type": "mount" }, { "type": "user" } ], "resources": { "memory": { "limit": 536870912 }, "cpu": { "quota": 50000, "period": 100000 } } }}四、命名空间隔离
4.1 Namespace 类型
| 类型 | 隔离内容 | 内核版本 |
|---|---|---|
| PID | 进程 ID | 2.6.24 |
| NET | 网络栈 | 2.6.29 |
| IPC | System V IPC | 2.6.19 |
| MNT | 挂载点 | 2.4.19 |
| UTS | 主机名、域名 | 2.6.19 |
| USER | 用户、用户组 | 3.8 |
| CGROUP | Cgroup 根目录 | 4.6 |
4.2 PID Namespace
// 创建 PID namespace#define _GNU_SOURCE#include <sched.h>#include <unistd.h>
int child_func(void *arg) { printf("Child PID: %d\n", getpid()); // PID = 1 return 0;}
int main() { char stack[1024*1024];
pid_t pid = clone(child_func, stack + sizeof(stack), CLONE_NEWPID | SIGCHLD, NULL);
printf("Parent PID: %d, Child PID: %d\n", getpid(), pid); wait(NULL); return 0;}PID 映射:
flowchart TB
subgraph 主机 PID Namespace
A[PID 1000: docker run]
B[PID 1234: nginx 进程]
end
subgraph 容器 PID Namespace
C[PID 1: nginx 进程]
end
B -.->|映射| C
4.3 Network Namespace
flowchart TB
subgraph 主机网络
A[eth0: 192.168.1.100]
B[docker0: 172.17.0.1]
end
subgraph 容器网络 Namespace
C[eth0: 172.17.0.2]
D[lo: 127.0.0.1]
end
A --> B
B <-->|veth pair| C
# 查看容器网络 namespacedocker inspect nginx | jq '.[0].NetworkSettings.NetworkId'
# 使用 nsenter 进入网络 namespacedocker inspect nginx | jq '.[0].State.Pid'nsenter -t <PID> -n ip addr4.4 User Namespace
用户 ID 映射:
flowchart LR
subgraph 容器
A[root UID 0]
end
subgraph 主机
B[普通用户 UID 100000]
end
A -.->|映射| B
# 配置用户 namespace# /etc/subuid 和 /etc/subgiduser:100000:65536五、Cgroup 资源限制
5.1 Cgroup 子系统
| 子系统 | 功能 |
|---|---|
| cpu | CPU 时间分配 |
| cpuacct | CPU 使用统计 |
| cpuset | CPU 核心绑定 |
| memory | 内存限制 |
| blkio | 块设备 IO |
| devices | 设备访问控制 |
| freezer | 暂停/恢复进程 |
5.2 内存限制
# 运行容器时限制内存docker run -d --memory=512m --memory-swap=1g nginx
# 查看容器 cgroupdocker inspect nginx | jq '.[0].HostConfig.CgroupParent'
# 直接查看 cgroup 配置cat /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytescat /sys/fs/cgroup/memory/docker/<container_id>/memory.usage_in_bytes5.3 CPU 限制
# 限制 CPU 使用docker run -d --cpus=1.5 --cpu-shares=512 nginx
# 查看配置cat /sys/fs/cgroup/cpu/docker/<container_id>/cpu.cfs_quota_uscat /sys/fs/cgroup/cpu/docker/<container_id>/cpu.cfs_period_us5.4 Cgroup 实现原理
// 内核 cgroup 结构(简化)struct cgroup { struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; struct list_head sets; struct cgroup_root *root;};
// cgroup 子系统struct cgroup_subsys { struct cgroup_subsys_state *(*css_alloc)(struct cgroup *cgrp); int (*css_online)(struct cgroup_subsys_state *css); void (*css_offline)(struct cgroup_subsys_state *css); void (*css_free)(struct cgroup_subsys_state *css); // ...};六、容器文件系统
6.1 OverlayFS
Docker 默认使用 OverlayFS 作为存储驱动:
# 查看存储驱动docker info | grep "Storage Driver"
# 查看 OverlayFS 挂载mount | grep overlayOverlayFS 结构:
容器文件系统挂载点:/var/lib/docker/overlay2/<id>/merged/
组成: lowerdir (只读层,可以有多个) /var/lib/docker/overlay2/l1/diff /var/lib/docker/overlay2/l2/diff upperdir (可写层) /var/lib/docker/overlay2/<id>/diff workdir (工作目录) /var/lib/docker/overlay2/<id>/work6.2 写时复制(CoW)
sequenceDiagram
participant C as 容器进程
participant O as OverlayFS
participant L as Lower 层
participant U as Upper 层
C->>O: 读取 /etc/nginx/nginx.conf
O->>L: 从 lower 层读取
L-->>C: 返回内容
C->>O: 写入 /etc/nginx/nginx.conf
O->>O: CoW: 复制到 upper 层
O->>U: 写入 upper 层
U-->>C: 写入成功
C->>O: 再次读取
O->>U: 从 upper 层读取
U-->>C: 返回修改后内容
6.3 存储驱动对比
| 存储驱动 | 特点 | 适用场景 |
|---|---|---|
| overlay2 | 高性能,推荐 | Linux 4.x+ |
| aufs | 历史遗留 | 旧系统 |
| devicemapper | 块设备级 | 企业环境 |
| btrfs | 原生快照 | Btrfs 文件系统 |
| zfs | 高级特性 | ZFS 文件系统 |
七、网络配置
7.1 Docker 网络模式
| 模式 | 说明 |
|---|---|
| bridge | 默认模式,容器连接 docker0 网桥 |
| host | 共享主机网络栈 |
| none | 无网络 |
| container | 共享其他容器网络 |
| overlay | 跨主机网络 |
7.2 Bridge 网络流程
sequenceDiagram
participant D as Docker
participant N as netlink
participant B as docker0
participant V as veth pair
participant C as 容器
D->>N: 创建 veth pair
N->>V: veth0 (主机端)
N->>V: veth1 (容器端)
D->>B: 将 veth0 加入 docker0
D->>C: 将 veth1 移入容器网络 namespace
D->>C: 配置 IP 地址
D->>N: 设置路由规则
Note over B,C: 网络连通
7.3 网络配置示例
# 创建自定义网络docker network create --driver bridge --subnet=172.20.0.0/16 mynet
# 容器连接网络docker run -d --network=mynet --ip=172.20.0.10 nginx
# 查看网络docker network inspect mynet八、容器生命周期
8.1 状态转换
stateDiagram-v2
[*] --> Created: docker create
Created --> Running: docker start
Running --> Paused: docker pause
Paused --> Running: docker unpause
Running --> Stopped: docker stop
Stopped --> Running: docker start
Running --> Restarting: docker restart
Restarting --> Running
Stopped --> [*]: docker rm
Created --> [*]: docker rm
8.2 信号处理
# 发送信号到容器docker kill --signal=SIGTERM nginx
# 优雅停止(发送 SIGTERM,等待 10 秒)docker stop nginx
# 强制停止(发送 SIGKILL)docker kill nginxDockerfile 信号处理:
# 使用 exec 形式,让进程成为 PID 1CMD ["nginx", "-g", "daemon off;"]
# shell 形式会导致信号无法传递# CMD nginx -g "daemon off;" # 不推荐8.3 健康检查
# Dockerfile 健康检查HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost/ || exit 1# 查看健康状态docker inspect --format='{{.State.Health.Status}}' nginx九、安全机制
9.1 能力机制(Capabilities)
# 查看容器能力docker inspect nginx | jq '.[0].HostConfig.CapAdd'docker inspect nginx | jq '.[0].HostConfig.CapDrop'
# 添加/删除能力docker run --cap-add=NET_ADMIN --cap-drop=CHOWN nginx常用能力:
| 能力 | 说明 |
|---|---|
| CAP_NET_ADMIN | 网络配置 |
| CAP_SYS_ADMIN | 系统管理 |
| CAP_CHOWN | 修改文件所有者 |
| CAP_KILL | 发送信号 |
| CAP_SETUID/GID | 切换用户/组 |
9.2 Seccomp
# 使用自定义 seccomp 配置docker run --security-opt seccomp=profile.json nginx
# 禁用 seccomp(不推荐)docker run --security-opt seccomp=unconfined nginx9.3 AppArmor/SELinux
# AppArmor 配置docker run --security-opt apparmor=docker-default nginx
# SELinux 配置docker run --security-opt label=level:s0:c100,c200 nginx十、调试与分析
10.1 查看容器详情
# 容器详细信息docker inspect nginx
# 容器进程docker top nginx
# 容器资源使用docker stats nginx
# 容器日志docker logs -f nginx10.2 进入容器
# 使用 docker execdocker exec -it nginx /bin/bash
# 使用 nsenterPID=$(docker inspect -f '{{.State.Pid}}' nginx)nsenter -t $PID -m -u -i -n -p /bin/bash10.3 导出容器信息
# 导出容器文件系统docker export nginx > nginx.tar
# 导出镜像docker save nginx > nginx-image.tar
# 查看镜像层docker history nginx关键要点
- 镜像:分层存储、UnionFS(Union File System)、写时复制
- 隔离:namespace 实现进程、网络、用户等隔离
- 限制:cgroup(Control Group) 实现 CPU、内存、IO 限制
- 文件系统:OverlayFS 联合挂载
- 网络:veth pair + 网桥实现容器互联
- 安全:Capabilities、Seccomp、AppArmor
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
Docker 容器启动流程:从镜像到运行
https://blog.souloss.com/posts/principles/principles-docker-container-startup/ 部分信息可能已经过时
相关文章 智能推荐
1
容器完整流程:docker run 背后
容器运行时 当你执行 docker run nginx 时,背后发生了什么?本章完整追踪从 Docker CLI 到容器进程启动的每一步——镜像拉取、OCI Bundle 生成、shim 启动、runc 创建、Namespace/Cgroup/OverlayFS 配置、容器进程执行,让你对 docker run 的每一步都了如指掌。
2
容器全景:从 chroot 到 OCI
容器运行时 从 1979 年的 chroot 到 2020 年代的 OCI 标准,容器技术走过了四十年的演进之路。本章建立容器运行时的全景认知——三大内核基石(Namespace/Cgroup/OverlayFS)、OCI 规范体系、Docker/containerd/runc 的架构关系,让你在深入细节前先看到完整的地图。
3
系列导读
容器运行时 本系列从 Linux 内核的 Namespace、Cgroup、OverlayFS 出发,深入 OCI 规范、runc 源码、containerd 架构,再到容器安全、沙箱运行时、网络、存储、镜像构建、Wasm 容器,最后综合实战构建一个迷你容器运行时——从「会用 Docker」到「理解容器运行时的每一行代码」,每章配有可运行的代码示例与架构图,让你从容器用户进阶到容器运行时工程师。
4
Docker 从入门到实践
工具 系统介绍 Docker 容器技术的完整使用指南——从镜像管理、容器操作、数据卷、网络配置到 Dockerfile 最佳实践与 Docker Compose 服务编排,涵盖云原生时代容器化的核心技能。
5
为什么 Docker 使用分层镜像
技术科普 深入解析 Docker 镜像分层设计的原理,理解 UnionFS、OverlayFS 如何实现高效的镜像存储与分发。






