4. 镜像拉取流程剖析:kubelet → containerd → registry 的完整链路、网络路径与 DNS 解析关键点
镜像拉取是集群生命周期中最频繁的操作之一,也是多集群场景下故障的高发区。当 Cilium 网络策略生效时,kubelet 到 containerd 再到远端 registry 的每一跳都可能因策略、路由或 DNS 解析异常而中断。本节从进程级调用链、网络数据流和 DNS 解析三个维度拆解完整链路。
4.1 进程级调用链:kubelet → containerd → registry
kubelet 并不直接与 registry 通信,而是通过 CRI(Container Runtime Interface)调用容器运行时。在大多数生产集群中,运行时为 containerd,其内部又通过 cri-plugin 和 image service 完成镜像层下载。完整调用链如下:
| 步骤 | 组件 | 动作 | 关键接口/协议 |
|---|---|---|---|
| 1 | kubelet | 根据 PodSpec 中的 image 字段,调用 CRI PullImage() |
gRPC over Unix Socket/run/containerd/containerd.sock |
| 2 | containerd cri-plugin |
解析镜像名称(含 registry 地址),检查本地缓存 | 本地元数据存储 (boltDB / 文件系统) |
| 3 | containerd image service |
若缓存未命中,发起 HTTPS 请求到远端 registry | HTTP/1.1 或 HTTP/2 默认端口 443 |
| 4 | containerd snapshotter |
下载 manifest 和各层 blob,解压到本地快照目录 | overlayfs / devicemapper |
| 5 | containerd cri-plugin |
返回 PullImageResponse 给 kubelet |
gRPC over Unix Socket |
排障要点: 步骤 1 和 5 走本地 Unix Socket,不受网络策略影响;步骤 3 是唯一跨主机网络跳,也是 Cilium 网络策略、DNS 解析和路由配置的生效点。
4.2 网络路径:从 containerd 到 registry 的完整数据流
当 containerd 发起 HTTPS 请求到 registry 时,数据包在节点内和集群网络中经过以下路径:
- containerd 进程 → 本地 lo 或 eth0:containerd 作为用户态进程,通过系统调用
connect()发送 TCP SYN。 - 节点内核路由决策:根据目标 IP(registry 解析结果)查路由表,决定下一跳是直连网段还是网关。
- Cilium eBPF 钩子:在
tc ingress/egress或XDP层,Cilium 执行网络策略匹配和负载均衡(如果 registry 通过 ClusterIP Service 暴露)。 - 物理网络 / 隧道封装:跨节点流量可能经过 VXLAN、Geneve 或直接路由。
- 公网 / 专线到达 registry:最终到达镜像仓库的负载均衡器或 CDN 边缘。
多集群场景: 如果 registry 部署在另一个集群(如通过 Submariner 或 Cilium ClusterMesh 暴露 Service),数据流会额外经过集群间网关和隧道,此时 Cilium 的
toFQDN 策略和 Egress Gateway 成为关键排障点。
4.3 DNS 解析关键点
containerd 在发起 HTTPS 请求前,必须先解析镜像仓库的域名。DNS 解析的成败直接影响拉取速度与成功率。在多集群和 Cilium 环境下,以下三个环节最容易出问题:
| 环节 | 默认行为 | Cilium 影响 | 排障命令 |
|---|---|---|---|
| 1. 节点级 DNS 配置 | /etc/resolv.conf 指向集群 DNS(如 CoreDNS) |
Cilium 可劫持 DNS 流量做 toFQDN 策略匹配 |
cat /etc/resolv.confnslookup registry.example.com |
| 2. CoreDNS 解析 | 先查集群内 Service,再转发上游 | Cilium 可能阻断非白名单的外部 DNS 请求 | kubectl logs -n kube-system -l k8s-app=kube-dns |
| 3. containerd 内置 DNS 缓存 | containerd 1.6+ 默认启用 resolv.conf 热加载 |
缓存过期时间与 Cilium DNS 策略 TTL 可能不一致 | ctr images pull --debug ... |
常见故障模式: Cilium 的
toFQDN 策略要求显式放行 registry 域名。如果策略中只写了 docker.io 但镜像实际拉取自 registry-1.docker.io,DNS 解析成功但 TCP 连接会被 eBPF 策略拒绝,表现为 ImagePullBackOff 且 containerd 日志中出现 connection refused。
4.4 完整链路故障排查清单
当镜像拉取失败时,按以下顺序逐层排查:
- 检查 kubelet 日志:
journalctl -u kubelet | grep PullImage,确认是否发出 CRI 调用。 - 检查 containerd 日志:
crictl pull <image> --debug,观察是否进入网络请求阶段。 - 验证 DNS 解析:在节点上
dig registry.example.com,确认返回 IP 且不被 Cilium 策略丢弃。 - 验证网络连通性:
curl -v https://registry.example.com/v2/,确认 TLS 握手成功。 - 检查 Cilium 策略:
cilium endpoint list查看 containerd 所在 Pod 的策略命中计数,确认toFQDN规则匹配。 - 检查 Cilium DNS 代理:
cilium bpf fqdn list查看缓存的 DNS 记录是否过期或缺失。
4.5 关键排障命令速查
| 目标 | 命令 | 预期输出 |
|---|---|---|
| 查看 containerd 拉取日志 | crictl pull --debug nginx:latest 2>&1 | grep -i error |
无 error 或明确的 HTTP 错误码 |
| 验证 DNS 解析 | dig +short registry.example.com @$(kubectl get svc -n kube-system kube-dns -o jsonpath='{.spec.clusterIP}') |
返回 A 记录 IP |
| 检查 Cilium DNS 缓存 | cilium bpf fqdn list |
显示域名与 IP 映射,TTL 未过期 |
| 检查 Cilium 策略命中 | cilium endpoint list → 找到 containerd 所在 Pod 的 endpoint ID → cilium endpoint get <id> | jq '.policy' |
"ingress": {"enforcing": true} 且无拒绝计数 |
| 抓包验证 TCP 连接 | tcpdump -i any -nn host <registry-ip> and port 443 |
看到 SYN → SYN-ACK → ACK 三次握手 |
最佳实践: 在多集群环境中,建议将常用镜像仓库域名加入 Cilium 的
toFQDN 策略白名单,并设置合理的 TTL(建议 300s 以上),避免 DNS 缓存频繁过期导致策略重新评估延迟。同时开启 Cilium 的 DNS 代理日志:cilium config set dns-proxy-log-level debug。