2. Docker 核心架构回顾:Docker Daemon、Containerd、Runc、客户端与服务端通信模型

在深入后端鉴权拦截机制之前,必须重新梳理 Docker 的底层架构。Docker 并非一个单体二进制,而是一组遵循 OCI(Open Container Initiative)标准的模块化组件协作系统。理解这些组件之间的通信边界与职责划分,是定位鉴权失败、连接超时、权限拒绝等故障的前提。

2.1 整体架构分层

Docker 引擎从用户视角到内核视角可划分为四层:

层级 组件 职责 通信方式
客户端层 docker CLI / SDK 解析用户命令,构造 RESTful 请求 HTTP(S) / Unix Socket
管理守护进程层 dockerd (Docker Daemon) API 鉴权、镜像管理、容器编排、网络存储管理 gRPC / REST
容器运行时层 containerd 容器生命周期管理(创建、启动、停止)、镜像拉取与存储 gRPC (通过 CRI)
低级运行时层 runc 与内核交互,通过 cgroups/namespace 创建隔离进程 OCI Bundle / exec 调用
故障排查要点: 鉴权拦截通常发生在 客户端 → dockerd 的 TLS 握手阶段,以及 dockerd → containerd 的 gRPC 凭证交换阶段。runc 层不参与网络鉴权,但若其权限配置错误(如 AppArmor/SELinux),会导致容器启动后立即退出,误判为鉴权失败。

2.2 客户端与服务端通信模型

Docker 采用典型的 C/S 架构。客户端通过三种方式与服务端建立连接:

  1. Unix Socket(默认):unix:///var/run/docker.sock,仅限本地 root 用户或 docker 组用户访问,无网络鉴权开销。
  2. TCP Socket(远程访问):tcp://host:2375(未加密)或 tcp://host:2376(TLS 加密),必须配置证书鉴权。
  3. SSH 隧道:ssh://user@host,依赖 SSH 鉴权,Docker 本身不参与。

当使用 TCP 远程访问时,完整的请求鉴权流程如下:

  • 客户端发起 TCP 连接,进行 TLS 握手(若启用)。
  • 服务端验证客户端证书的 CA 签名与 Common Name。
  • 握手成功后,客户端发送 HTTP 请求(如 POST /containers/create)。
  • dockerd 的 HTTP 路由层解析请求,调用 AuthZ 插件(若配置)进行授权检查。
  • 通过后,请求被转发至 containerd 或内部模块处理。
常见故障: 客户端证书过期或未配置 --tlscacert 会导致 certificate signed by unknown authority 错误。此时请求在 TLS 层即被拒绝,不会进入 AuthZ 插件阶段。
客户端与服务端通信鉴权流程 docker CLI TLS 握手 dockerd HTTP 路由解析 AuthZ 插件检查 转发至 containerd 说明: TLS 握手失败时,请求在到达 dockerd 之前即被拒绝,日志中无 AuthZ 相关记录。

2.3 Docker Daemon 内部组件协作

dockerd 并非直接调用 runc,而是通过 containerd 作为中间层。这种解耦设计使得 dockerd 可以热升级而不影响运行中的容器。内部协作流程如下:

  • dockerd 收到创建容器请求后,通过 gRPC 调用 containerd 的 CreateContainer 接口。
  • containerd 将请求转换为 OCI 规范格式,生成 config.json 和 rootfs,并调用 runc create。
  • runc 根据 OCI Bundle 配置 cgroups、namespace,最终通过 clone() 系统调用启动容器进程。
鉴权拦截点: dockerd 与 containerd 之间的 gRPC 通信默认使用本地 Unix Socket(/run/containerd/containerd.sock),不经过网络,因此不存在 TLS 鉴权。但若 containerd 被配置为远程模式(如 Kubernetes 中的 CRI 远程运行时),则需要在 gRPC 层配置 mTLS。
Docker Daemon 内部组件协作与鉴权边界 dockerd API 鉴权 / 镜像管理 containerd 容器生命周期 / 镜像存储 runc cgroups / namespace gRPC (本地 Socket) OCI Bundle 鉴权边界说明: 1. 客户端 → dockerd:TLS 双向认证 + AuthZ 插件(网络边界) 2. dockerd → containerd:本地 Socket,默认无鉴权(进程边界) 3. containerd → runc:OCI 标准调用,无网络鉴权(内核边界) 故障排查提示: 若 dockerd 日志显示 failed to connect to containerd,检查 /run/containerd/containerd.sock 是否存在及权限是否为 660。 使用 ctr 命令可直接与 containerd 交互,绕过 dockerd 鉴权层,用于隔离故障。

2.4 关键通信路径故障排查清单

当遇到“权限拒绝”或“连接超时”类错误时,按以下路径逐层排查:

故障现象 可能原因 排查命令 / 方法
docker ps 返回 permission denied 当前用户不在 docker 组,或 socket 权限错误 ls -l /var/run/docker.sock;groups $USER
docker -H tcp://host:2376 info 超时 TLS 证书未配置或端口未开放 openssl s_client -connect host:2376;检查防火墙
docker run 返回 unauthorized AuthZ 插件拒绝,或证书 CN 不匹配 查看 dockerd --debug 日志;检查 ~/.docker/config.json
容器创建成功但立即退出 runc 权限不足(SELinux/AppArmor) journalctl -u containerd;dmesg | grep -i denied
最佳实践: 在生产环境中,始终使用 dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376 启动,并配合 AuthZ 插件(如 authz-broker)实现细粒度访问控制。