docker-run-调用链全图解

图 1:分层架构总览

bash 复制代码
╔══════════════════════════════════════════════════════════════════╗
║  用户层                                                            ║
║    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。

bash 复制代码
┌──────────────────────────────────────────────────────────────────────┐
│  阶段 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 都长这样,记住这个骨架就能快速读懂任何文件:

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:容器状态机

ini 复制代码
                  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 状态码映射

scss 复制代码
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 结构体字段(节选):

go 复制代码
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 接口的子接口分组

scss 复制代码
                ┌──────────────────┐
                │     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 的完整时序

scss 复制代码
  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             │                      │                     │                  │
   │◄────────────────┤                      │                     │                  │
相关推荐
贰先生1 小时前
Xiuno BBS 审计之问题09:登录回跳 referer 未严格校验,存在开放重定向风险
后端
JarvanStack1 小时前
【无标题】
后端
贰先生1 小时前
Xiuno BBS 审计之问题10:用户密码不符合现代密码存储规范
后端
IT_陈寒1 小时前
SpringBoot自动配置的坑,我的Bean怎么不生效?
前端·人工智能·后端
CoderYanger2 小时前
视频切割脚本(Python版)
linux·开发语言·windows·后端·python·程序人生·职场和发展
程序员爱钓鱼2 小时前
Rust 数据类型详解:整数、浮点数、布尔、字符、元组和数组
后端·rust
geovindu2 小时前
go: Floyd-Warshall Algorithms
开发语言·后端·算法·golang
AC赳赳老秦2 小时前
Excel 动态仪表盘制作:用 OpenClaw 自动处理数据、生成交互式图表并实时更新仪表盘
大数据·服务器·后端·测试用例·excel·deepseek·openclaw
CoderYanger2 小时前
视频裁剪+缩放+自动添加水印脚本(Python版)
开发语言·后端·python·程序人生·职场和发展·音视频·学习方法