图 1:分层架构总览
╔══════════════════════════════════════════════════════════════════╗
║ 用户层 ║
║ docker run -it ubuntu bash ← 你在终端敲的命令 ║
╚══════════════════════════════════════════════════════════════════╝
│ HTTP POST
▼
╔══════════════════════════════════════════════════════════════════╗
║ HTTP 路由层 daemon/server/router/ ║
║ container/container.go 路由表(URL → handler) ║
║ container/container_routes.go handler(解析请求 / 序列化响应) ║
║ container/backend.go Backend 接口("合同") ║
╚══════════════════════════════════════════════════════════════════╝
│ 调 Backend 接口方法
▼
╔══════════════════════════════════════════════════════════════════╗
║ 业务层 daemon/<op>.g o ★ 本文档讲的就是这一层 ★ ║
║ create.go ContainerCreate → containerCreate → create ║
║ start.go ContainerStart → containerStart ║
║ stop.go ContainerStop → containerStop ║
║ ... ║
╚══════════════════════════════════════════════════════════════════╝
│ 调底层服务
▼
╔══════════════════════════════════════════════════════════════════╗
║ 底层服务 ║
║ daemon/container/ 容器对象 + 状态机 ║
║ daemon/images/ 镜像服务 ║
║ daemon/libnetwork/ 网络(libnetwork) ║
║ daemon/volume/ 卷服务 ║
║ daemon/containerd/ containerd 客户端 ║
╚══════════════════════════════════════════════════════════════════╝
│ gRPC / shim
▼
╔══════════════════════════════════════════════════════════════════╗
║ 运行时 ║
║ containerd 容器运行时守护进程 ║
║ └─ containerd-shim 进程级 supervisor ║
║ └─ runc OCI runtime(fork 容器进程) ║
║ └─ Linux kernel(namespace + cgroup) ║
╚══════════════════════════════════════════════════════════════════╝
图 2:docker run 的两阶段调用链
docker run 在协议层是两个独立的 HTTP 请求:先 create,再 start。
┌──────────────────────────────────────────────────────────────────────┐
│ 阶段 1:docker create(POST /containers/create) │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ CLI docker run ──► POST /v1.xx/containers/create │
│ │ │
│ ▼ │
│ router/container/container.go::initRoutes │
│ NewPostRoute("/containers/create", c.postContainersCreate) │
│ │ │
│ ▼ │
│ router/container/container_routes.go::postContainersCreate │
│ 解析 JSON body → ContainerCreateConfig │
│ 调 backend.ContainerCreate(ctx, config) │
│ │ │
│ ▼ │
│ daemon/create.go::ContainerCreate │
│ 加 OTel span、平台校验、默认 RestartPolicy │
│ 调 daemon.containerCreate(...) │
│ │ │
│ ▼ │
│ daemon/create.go::containerCreate │
│ verifyContainerSettings │
│ imageService.GetImage(找镜像) │
│ validateNetworkingConfig │
│ 调 daemon.create(...) │
│ │ │
│ ▼ │
│ daemon/create.go::create │
│ ① newContainer 新建 *Container │
│ ② createContainerFS 准备 rootfs │
│ ③ setSecurityOptions SELinux/AppArmor │
│ ④ registerLinks 处理 --link │
│ ⑤ registerMountPoints 注册挂载点 │
│ ⑥ imageService.CreateLayer 创建可写层 │
│ ⑦ register 注册到 daemon.containers │
│ ⑧ LogContainerEvent(ActionCreate) │
│ │ │
│ ▼ │
│ 返回 {Id: "abc123...", Warnings: []} │
│ │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ 阶段 2:docker start(POST /containers/{id}/start) │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ CLI docker run ──► POST /v1.xx/containers/abc123.../start │
│ │ │
│ ▼ │
│ router/container/container.go::initRoutes │
│ NewPostRoute("/containers/{name:.*}/start", postContainersStart)│
│ │ │
│ ▼ │
│ router/container/container_routes.go::postContainersStart │
│ 解析路径参数 name │
│ 调 backend.ContainerStart(ctx, name, "", "") │
│ │ │
│ ▼ │
│ daemon/start.go::ContainerStart │
│ daemon.GetContainer(name) │
│ validateState(ctr) 不能在 Dead/Paused 时启动 │
│ verifyContainerSettings 校验 hostConfig │
│ 调 daemon.containerStart(...) │
│ │ │
│ ▼ │
│ daemon/start.go::containerStart ★ 10 个阶段 ★ │
│ ① OTel span + 状态守卫 │
│ ② 失败兜底 defer │
│ ③ 准备运行环境 │
│ conditionalMountOnStart │
│ initializeNetworking ← libnetwork │
│ setupContainerDirs │
│ setupMounts │
│ ④ createSpec ← 把容器配置编译成 OCI runtime spec │
│ ⑤ restart-manager / AppArmor │
│ ⑥ libcontainerd.ReplaceContainer ← 在 containerd 注册 │
│ ⑦ ctr.NewTask ← 创建 task(fork shim 进程,但还没运行) │
│ ⑧ initializeCreatedTask ← 注入 cgroup / namespace │
│ ⑨ tsk.Start ★★★ 真正 fork 出容器进程 ★★★ │
│ ⑩ 状态更新 + 健康监控 + 事件上报 │
│ SetRunning(Pid, StartedAt) │
│ initHealthMonitor │
│ CheckpointTo(持久化) │
│ LogContainerEvent(ActionStart) │
│ │ │
│ ▼ │
│ 返回 204 No Content │
│ │
└──────────────────────────────────────────────────────────────────────┘
图 3:daemon/<op>.go 的代码骨架
每个 <op>.go 都长这样,记住这个骨架就能快速读懂任何文件:
package daemon
import (...)
// ╔════════════════════════════════════════════════════════════╗
// ║ 公开 API(大写开头) ║
// ║ 实现 Backend 接口,被 router 调用 ║
// ║ 职责:参数校验 + 查容器 + 状态判断 + 调内部实现 ║
// ╚════════════════════════════════════════════════════════════╝
func (daemon *Daemon) ContainerXxx(ctx context.Context, name string, ...) (..., error) {
// 1. 找容器(按 全ID / 名字 / 短ID前缀)
ctr, err := daemon.GetContainer(name)
if err != nil {
return ..., err
}
// 2. 状态机校验(用 errdefs 返回标准 typed error)
if !ctr.State.IsRunning() {
return ..., errdefs.Conflict(errors.New("..."))
}
// 3. 调内部小写实现
return daemon.containerXxx(ctx, ctr, ...)
}
// ╔════════════════════════════════════════════════════════════╗
// ║ 内部实现(小写开头) ║
// ║ 真正干活的地方,可被 daemon 内部其他流程复用 ║
// ║ 职责:加锁 → 调底层 → 改状态 → 持久化 → 发事件 ║
// ╚════════════════════════════════════════════════════════════╝
func (daemon *Daemon) containerXxx(ctx context.Context, ctr *container.Container, ...) (retErr error) {
// 1. OTel 追踪 span(可选)
ctx, span := otel.Tracer("").Start(ctx, "daemon.containerXxx")
defer func() {
otelutil.RecordStatus(span, retErr)
span.End()
}()
// 2. 容器级锁
ctr.Lock()
defer ctr.Unlock()
// 3. 失败回滚 defer(可选,用于资源清理)
defer func() {
if retErr != nil {
// 清理已分配的资源...
}
}()
// 4. 业务逻辑:调底层服务
tsk, err := ctr.GetRunningTask()
if err != nil {
return err
}
if err := tsk.Xxx(ctx); err != nil {
return err
}
// 5. 改状态
ctr.State.Xxx = true
// 6. 持久化到 config.v2.json
if err := ctr.CheckpointTo(ctx, daemon.containersReplica); err != nil {
log.G(ctx).WithError(err).Warn("could not save container to disk")
}
// 7. 上报事件(docker events 能看到)
daemon.LogContainerEvent(ctr, events.ActionXxx)
return nil
}
图 4:容器状态机
ContainerCreate
│
▼
┌───────────┐
│ created │ (State.Running=false, State.Paused=false)
└───────────┘
│ ContainerStart
▼
┌───────────┐
┌────────►│ running │◄────────┐
│ └───────────┘ │
│ │ │ │
│ ContainerStop│ │ContainerPause
│ │ │ │
│ ▼ ▼ │
│ ┌───────────┐ │
│ │ stopped │ │
│ └───────────┘ │
│ │ │
│ │ ContainerStart │ ContainerUnpause
│ │ (重启) │
└──────────────┴─────────────────┘
│
│ ContainerRm
▼
┌───────────┐
│ dead │ (State.Dead=true, 已从 daemon.containers 删除)
└───────────┘
特殊状态:
- restarting: 正在 restart(containerStop + containerStart 中间态)
- exited: stopped 的子状态,表示进程已退出(有 ExitCode)
- paused: running 的子状态(进程还活着,只是被冻结)
图 5:错误类型 → HTTP 状态码映射
daemon 代码里返回 router 转换后 客户端看到
───────────────────────────── ────────────────── ──────────────
errdefs.InvalidParameter(err) → 400 Bad Request → 参数错误
errdefs.NotFound(err) → 404 Not Found → 资源不存在
errdefs.Conflict(err) → 409 Conflict → 状态冲突
errdefs.NotModified(err) → 304 Not Modified → 已经是目标状态
errdefs.System(err) → 500 Internal → daemon 内部错误
errdefs.Forbidden(err) → 403 Forbidden → 操作不被允许
errdefs.Unauthorized(err) → 401 Unauthorized → 未认证
新手记法:在 daemon 代码里看到 errdefs.Xxx(err),就知道这条错误最终会被 router 翻译成对应的 HTTP 状态码。
图 6:daemon 持有的核心组件
daemon/daemon.go 第 100 行的 Daemon 结构体字段(节选):
type Daemon struct {
id string // daemon 实例 ID
repository string // /var/lib/docker/containers
containers container.Store // 内存中的容器表(ID → *Container)
containersReplica *container.ViewDB // 容器快照数据库(支持按前缀/名字查询)
execCommands *container.ExecStore // exec 命令存储
imageService ImageService // 镜像服务
configStore atomic.Pointer[configStore] // 配置(atomic 实现无锁读)
statsCollector *stats.Collector // 资源指标采集
defaultLogConfig containertypes.LogConfig // 默认日志配置
registryService *registry.Service // 镜像仓库
EventsService *events.Events // 事件总线
netController *libnetwork.Controller // 网络控制器
volumes *volumesservice.VolumesService // 卷服务
root string // /var/lib/docker
shutdown bool // 是否在关闭中
containerdClient *containerd.Client // containerd gRPC 客户端
containerd libcontainerdtypes.Client // containerd 抽象层
cluster Cluster // Swarm 集群
linkIndex *linkIndex // --link 索引
linkIndex *linkIndex
pruneRunning atomic.Bool // prune 并发保护
CDICache *cdi.Cache // CDI 设备缓存
// ...
}
新手记法:看到 daemon 代码里 daemon.xxx,就来这里查 xxx 是什么。
图 7:Backend 接口的子接口分组
┌──────────────────┐
│ Backend │ daemon.Daemon 实现了这个接口
└────────┬─────────┘
│
┌────────────────┼─────────────────┬──────────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
┌────────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────┐ ┌──────────┐
│stateBackend│ │monitorBackend│ │execBackend │ │copyBackend│ │attachBack│
│ │ │ │ │ │ │ │ │ end │
│ Create │ │ Inspect │ │ ExecCreate │ │ Archive │ │ Attach │
│ Start │ │ Containers │ │ ExecStart │ │ Extract │ │ │
│ Stop │ │ Logs │ │ ExecInspect│ │ Stat │ │ │
│ Kill │ │ Stats │ │ ExecResize │ │ Export │ │ │
│ Pause │ │ Top │ │ ExecExists │ │ │ │ │
│ Unpause │ │ Changes │ │ │ │ │ │ │
│ Restart │ │ │ │ │ │ │ │ │
│ Rm │ │ │ │ │ │ │ │ │
│ Rename │ │ │ │ │ │ │ │ │
│ Update │ │ │ │ │ │ │ │ │
│ Wait │ │ │ │ │ │ │ │ │
│ Resize │ │ │ │ │ │ │ │ │
└────────────┘ └──────────────┘ └────────────┘ └──────────┘ └──────────┘
│
▼
┌────────────┐ ┌──────────────┐ ┌──────────────────┐
│systemBack │ │commitBackend │ │sysInfoProvider │
│ │ │ │ │ │
│ Prune │ │CreateImage │ │ RawSysInfo │
│ │ │FromContainer │ │ │
└────────────┘ └──────────────┘ └──────────────────┘
图 8:常见 docker 命令 → daemon 文件速查
| docker 命令 | HTTP 方法 + URL | handler | daemon 文件 |
|---|---|---|---|
docker create | POST /containers/create | postContainersCreate | create.go |
docker start | POST /containers/{id}/start | postContainersStart | start.go |
docker stop | POST /containers/{id}/stop | postContainersStop | stop.go |
docker restart | POST /containers/{id}/restart | postContainersRestart | restart.go |
docker kill | POST /containers/{id}/kill | postContainersKill | kill.go |
docker pause | POST /containers/{id}/pause | postContainersPause | pause.go |
docker unpause | POST /containers/{id}/unpause | postContainersUnpause | unpause.go |
docker rm | DELETE /containers/{id} | deleteContainers | delete.go |
docker wait | POST /containers/{id}/wait | postContainersWait | wait.go |
docker ps | GET /containers/json | getContainersJSON | list.go |
docker inspect | GET /containers/{id}/json | getContainersByName | inspect.go |
docker logs | GET /containers/{id}/logs | getContainersLogs | logs.go |
docker stats | GET /containers/{id}/stats | getContainersStats | stats.go |
docker top | GET /containers/{id}/top | getContainersTop | top.go |
docker diff | GET /containers/{id}/changes | getContainersChanges | changes.go |
docker attach | POST /containers/{id}/attach | postContainersAttach | attach.go |
docker exec | POST /containers/{id}/exec + POST /exec/{id}/start | postContainerExecCreate + postContainerExecStart | exec.go |
docker cp(宿主机→容器) | PUT /containers/{id}/archive | putContainersArchive | archive.go |
docker cp(容器→宿主机) | GET /containers/{id}/archive | getContainersArchive | archive.go |
docker export | GET /containers/{id}/export | getContainersExport | export.go |
docker commit | POST /commit | postCommit | commit.go |
docker rename | POST /containers/{id}/rename | postContainerRename | rename.go |
docker update | POST /containers/{id}/update | postContainerUpdate | update.go |
docker container prune | POST /containers/prune | postContainersPrune | prune.go |
docker resize(TTY) | POST /containers/{id}/resize | postContainersResize | resize.go |
图 9:一次 docker stop 的完整时序
CLI router daemon/<op>.go container containerd
│ │ │ │ │
│ POST /stop │ │ │ │
├────────────────►│ │ │ │
│ │ postContainersStop │ │ │
│ ├─────────────────────►│ │ │
│ │ │ ContainerStop │ │
│ │ ├────────────────────►│ │
│ │ │ GetContainer │ │
│ │ │◄───────────────────┤ │
│ │ │ IsRunning? │ │
│ │ │ no → 304 NotMod │ │
│ │ │ yes ↓ │ │
│ │ │ containerStop │ │
│ │ │ 1. 取 stopSignal │ │
│ │ │ 2. killWithSignal ├─────────────────►│
│ │ │ │ task.Kill(sig) │
│ │ │ 3. State.Wait(ctx,│ │
│ │ │ NotRunning) │ │
│ │ │ ◄── exit ─────┤ │
│ │ │ 4. 超时?→ Kill() ├─────────────────►│
│ │ │ │ task.Kill(SIGKILL)
│ │ │ 5. LogContainer │ │
│ │ │ Event(Stop) │ │
│ │ 204 No Content │ │ │
│ │◄─────────────────────┤ │ │
│ 204 │ │ │ │
│◄────────────────┤ │ │ │