排查容器运行时问题


本文档介绍了您在使用 Google Kubernetes Engine (GKE) 节点上的容器运行时时可能会遇到的常见问题的问题排查步骤。

如果您需要其他帮助,请与 Cloud Customer Care 联系。

在使用 Containerd 的 Windows 节点池上,包含简单盘符的装载路径失败

此问题已在 containerd 1.6.6 及更高版本中得到解决。

在启动容器时,运行使用低于 1.6.6 版的 containerd 运行时的 Windows Server 节点池的 GKE 集群可能会遇到以下错误:

failed to create containerd task : CreateComputeSystem : The parameter is incorrect : unknown

如需了解详情,请参阅 GitHub 问题 6589

解决方案

如需解决此问题,请将节点池升级到使用 containerd 运行时版本 1.6.6 或更高版本的最新 GKE 版本。

在使用 containerd 的 Windows 节点池上,具有非数组预转义 CMDENTRYPOINT 命令行的容器映像失败

此问题已在 containerd 1.6 及更高版本中得到解决。

在启动容器时,运行使用 containerd 运行时 1.5.X 的 Windows Server 节点池的 GKE 集群可能会遇到以下错误:

failed to start containerd task : hcs::System::CreateProcess : The system cannot find the file specified.: unknown

如需了解详情,请参阅 GitHub 问题 #5067GitHub 问题 #6300

解决方案

如需解决此问题,请将节点池升级到使用 containerd 运行时版本 1.6.6 或更高版本的最新 GKE 版本。

在使用 Containerd 的 Windows 节点池上,具有不存在的路径或 Linux 格式(正斜线)路径的容器映像卷失败

此问题已在 containerd 1.6 及更高版本中得到解决。

在启动容器时,运行使用 containerd 运行时 1.5.X 的 Windows Server 节点池的 GKE 集群可能会遇到以下错误:

failed to generate spec: failed to stat "<volume_path>": CreateFile : The system cannot find the path specified.

如需了解详情,请参阅 GitHub 问题 5671

解决方案

如需解决此问题,请将节点池升级到使用 containerd 运行时版本 1.6.x 或更高版本的最新 GKE 版本。

/etc/mtab:不存在此类文件或目录

Docker 容器运行时默认在容器内填充此符号链接,但 containerd 运行时不会填充。

如需了解详情,请参阅 GitHub 问题 2419

解决方案

要解决此问题,请在映像构建期间手动创建符号链接 /etc/mtab

ln -sf /proc/mounts /etc/mtab

映像拉取错误:不是目录

受影响的 GKE 版本:全部

使用 kaniko 构建映像时,可能无法使用 containerd 拉取该映像,并显示错误消息“不是目录”。如果映像以特殊方式构建,则以下情况下会发生此错误:当上一个命令删除一个目录,而下一个命令在该目录中重新创建相同的文件。

以下带有 npm 的 Dockerfile 示例说明了此问题。

RUN npm cache clean --force
RUN npm install

如需了解详情,请参阅 GitHub 问题 #4659

解决方案

如需解决此问题,请使用 docker build 构建映像,这不受此问题的影响。

如果 docker build 不适用于您,请将这些命令组合成一个。以下 Dockerfile 示例结合使用了 RUN npm cache clean --forceRUN npm install

RUN npm cache clean --force && npm install

某些文件系统指标缺失且指标格式不同

受影响的 GKE 版本:全部

Kubelet /metrics/cadvisor 端点提供 Prometheus 指标,如 Kubernetes 系统组件的指标中所述。如果您安装依赖于该端点的指标收集器,可能会看到以下问题:

  • Docker 节点上的指标格式为 k8s_<container-name>_<pod-name>_<namespace>_<pod-uid>_<restart-count>,但 containerd 节点上的指标格式为 <container-id>
  • containerd 节点上缺少一些文件系统指标,如下所示:

    container_fs_inodes_free
    container_fs_inodes_total
    container_fs_io_current
    container_fs_io_time_seconds_total
    container_fs_io_time_weighted_seconds_total
    container_fs_limit_bytes
    container_fs_read_seconds_total
    container_fs_reads_merged_total
    container_fs_sector_reads_total
    container_fs_sector_writes_total
    container_fs_usage_bytes
    container_fs_write_seconds_total
    container_fs_writes_merged_total
    

解决方案

您可以使用 cAdvisor 作为独立守护进程集来缓解此问题。

  1. 查找名称模式为 vX.Y.Z-containerd-cri 的最新 cAdvisor 版本(例如 v0.42.0-containerd-cri)。
  2. 按照 cAdvisor Kubernetes Daemonset 中的步骤创建守护进程集。
  3. 让已安装的指标收集器指向使用 cAdvisor /metrics 端点,该端点提供全套 Prometheus 容器指标

替代方案

  1. 将监控解决方案迁移到 Cloud Monitoring,后者提供了一整套容器指标。
  2. 使用端点 /stats/summaryKubelet 摘要 API 收集指标。

在 GKE Windows 上容器运行时重启后,基于挂接的操作将无法正常运行

受影响的 GKE 版本:1.21 到 1.21.5-gke.1802、1.22 到 1.22.3-gke.700

如果 GKE 集群运行使用 Containerd 运行时(版本 1.5.4 和 1.5.7-gke.0)的 Windows Server 节点池,则在容器运行时被强制重启时,可能会遇到问题:对现有运行容器的挂接操作无法再次绑定 IO。此问题不会导致 API 调用失败,但系统不会发送或接收数据。这包括通过集群 API 服务器挂接和记录 CLI 与 API 的数据。

解决方案

要解决此问题,请使用较新的 GKE 版本升级到修补后的容器运行时版本 (1.5.7-gke.1)。

Pod 显示 failed to allocate for range 0: no IP addresses available in range set 错误消息

受影响的 GKE 版本:1.24.6-gke.1500 或更早版本、1.23.14-gke.1800 或更早版本以及 1.22.16-gke.2000 或更早版本

运行使用 containerd 的节点池的 GKE 集群可能会遇到 IP 泄露问题,并耗尽节点上的所有 Pod IP。受影响的节点上安排的 Pod 会显示类似于以下内容的错误消息:

failed to allocate for range 0: no IP addresses available in range set: 10.48.131.1-10.48.131.62

如需详细了解此问题,请参阅 containerd GitHub 问题 #5438GitHub 问题 #5768

GKE Dataplane V2 中存在一个已知问题,可能会触发此问题。但是,此问题可能是由其他原因(包括 runc 卡住)触发的。

解决方案

如需解决此问题,请按照 GKE Dataplane V2 标准 GKE 集群的解决方法中所述的解决方法操作。

探测超出超时后的执行探测行为差异

受影响的 GKE 版本:全部

containerd 映像上的执行探测行为与 dockershim 映像上的行为不同。如果为 Pod 定义的执行探测超过 dockershim 映像上声明的 Kubernetes timeoutSeconds 阈值,则该探测会被视为失败。在 containerd 映像上,系统会忽略声明的 timeoutSeconds 阈值后返回的探测结果。

解决方案

在 GKE 中,特性门控 ExecProbeTimeout 设置为 false,并且无法更改。如需解决此问题,请提高所有受影响的执行探测的 timeoutSeconds 阈值,或在探测逻辑中实现超时功能。

排查私有注册表问题

本部分提供了 containerd 中的私有注册表配置的问题排查信息。

映像拉取失败并显示 x509 错误:证书由未知授权机构签名

如果 GKE 找不到特定私有注册表网域的证书,则会出现此问题。您可以使用以下查询在 Cloud Logging 中检查此错误:

  1. 进入 Google Cloud 控制台中的 Logs Explorer 页面。

    转到 Logs Explorer

  2. 请运行以下查询:

    ("Internal error pulling certificate" OR
    "Failed to get credentials from metadata server" OR
    "Failed to install certificate")
    

如需解决此问题,请尝试执行以下操作:

  1. 在 GKE Standard 中,打开位于以下路径的配置文件:

    /etc/containerd/hosts.d/DOMAIN/config.toml
    

    DOMAIN 替换为注册表的 FQDN。

  2. 验证您的配置文件是否包含正确的 FQDN。

  3. 验证配置文件的 secretURI 字段中的证书路径是否正确。

  4. 验证 Secret Manager 中是否存在证书。

证书不存在

如果 GKE 无法从 Secret Manager 拉取证书以在您的节点上配置 containerd,则会出现此问题。

如需解决此问题,请尝试执行以下操作:

  1. 确保受影响的节点在运行 Container-Optimized OS。不支持 Ubuntu 和 Windows 节点。
  2. 在配置文件中,确保 secretURI 字段中的 Secret 路径正确无误。
  3. 检查集群的 IAM 服务账号是否具有访问 Secret 的正确权限
  4. 检查集群是否具有 cloud-platform 访问权限范围。如需了解相关说明,请参阅检查访问权限范围

没有为本地网络 (10.0.0.0/8) 配置不安全的注册表选项

受影响的 GKE 版本:全部

在 containerd 映像上,没有为本地网络 10.0.0.0/8 配置不安全的注册表选项。如果使用不安全的私有仓库,您可能会看到以下错误:

pulling image: rpc error: code = Unknown desc = failed to pull and unpack image "IMAGE_NAME": failed to do request: Head "IMAGE_NAME": http: server gave HTTP response to HTTPS client

如需解决此问题,请尝试执行以下操作:

  • 使用 Artifact Registry
  • 如果您的使用场景支持此选项,请在私有注册表上配置 TLS。您可以使用 containerd 配置文件指示 GKE 使用存储在 Secret Manager 中的证书来访问私有注册表。如需查看相关说明,请参阅使用私有 CA 证书访问私有注册表

配置特权 DaemonSet 以修改 containerd 配置

对于 Standard 集群,请尝试以下步骤。此解决方法不适用于 Autopilot,因为特权容器存在安全风险。如果您的环境向互联网公开,则在部署此解决方案之前,请考虑您的风险容忍度。 在所有情况下,我们都强烈建议您为私有注册表配置 TLS,并改用 Secret Manager 选项。

  1. 请查看以下清单:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: insecure-registries
      namespace: default
      labels:
        k8s-app: insecure-registries
    spec:
      selector:
        matchLabels:
          name: insecure-registries
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            name: insecure-registries
        spec:
          nodeSelector:
            cloud.google.com/gke-container-runtime: "containerd"
          hostPID: true
          containers:
            - name: startup-script
              image: gcr.io/google-containers/startup-script:v1
              imagePullPolicy: Always
              securityContext:
                privileged: true
              env:
              - name: ADDRESS
                value: "REGISTRY_ADDRESS"
              - name: STARTUP_SCRIPT
                value: |
                  set -o errexit
                  set -o pipefail
                  set -o nounset
    
                  if [[ -z "$ADDRESS" || "$ADDRESS" == "REGISTRY_ADDRESS" ]]; then
                    echo "Error: Environment variable ADDRESS is not set in containers.spec.env"
                    exit 1
                  fi
    
                  echo "Allowlisting insecure registries..."
                  containerd_config="/etc/containerd/config.toml"
                  hostpath=$(sed -nr 's;  config_path = "([-/a-z0-9_.]+)";\1;p' "$containerd_config")
                  if [[ -z "$hostpath" ]]; then
                    echo "Node uses CRI config model V1 (deprecated), adding mirror under $containerd_config..."
                    grep -qxF '[plugins."io.containerd.grpc.v1.cri".registry.mirrors."'$ADDRESS'"]' "$containerd_config" || \
                      echo -e '[plugins."io.containerd.grpc.v1.cri".registry.mirrors."'$ADDRESS'"]\n  endpoint = ["http://'$ADDRESS'"]' >> "$containerd_config"
                  else
                    host_config_dir="$hostpath/$ADDRESS"
                    host_config_file="$host_config_dir/hosts.toml"
                    echo "Node uses CRI config model V2, adding mirror under $host_config_file..."
                    if [[ ! -e "$host_config_file" ]]; then
                      mkdir -p "$host_config_dir"
                      echo -e "server = \"https://$ADDRESS\"\n" > "$host_config_file"
                    fi
                    echo -e "[host.\"http://$ADDRESS\"]\n  capabilities = [\"pull\", \"resolve\"]\n" >> "$host_config_file"
                  fi
                  echo "Reloading systemd management configuration"
                  systemctl daemon-reload
                  echo "Restarting containerd..."
                  systemctl restart containerd

    .spec.containers.env 字段中,将 ADDRESS 变量的 REGISTRY_ADDRESS 值替换为本地 HTTP 仓库的地址,格式为 DOMAIN_NAME:PORT。例如,

    containers:
    - name: startup-script
      ...
      env:
      - name: ADDRESS
        value: "example.com:5000"
    
  2. 部署 DaemonSet:

    kubectl apply -f insecure-registry-ds.yaml
    

DaemonSet 会将不安全的仓库添加到每个节点上的 containerd 配置中。

containerd 忽略特权 Pod 的任何设备映射

受影响的 GKE 版本:全部

对于特权 Kubernetes Pod,容器运行时会忽略 volumeDevices.devicePath 传递给它的所有设备映射,改为让主机上的每台设备都可供 /dev 下的容器使用。

当节点面临 I/O 压力时,containerd 泄露 shim 进程

受影响的 GKE 版本:1.25.0 至 1.25.15-gke.1040000、1.26.0 至 1.26.10-gke.1030000、1.27.0 至 1.27.6-gke.1513000,以及 1.28.0 至 1.28.3-gke.1061000

当 GKE 节点面临 I/O 压力时,containerd 在删除 Pod 后可能无法删除 containerd-shim-runc-v2 进程,从而导致进程泄露。如果某个节点上发生泄露,您会看到节点上的 containerd-shim-runc-v2 进程多于该节点上的 Pod 数量。您可能还会看到内存和 CPU 用量增加以及额外的 PID。如需了解详情,请参阅 GitHub 问题修复由高 IO 压力导致的 shim 泄露

如需解决此问题,请将节点升级到以下版本或更高版本:

  • 1.25.15-gke.1040000
  • 1.26.10-gke.1030000
  • 1.27.6-gke.1513000
  • 1.28.3-gke.1061000

在运行 containerd 的 Pod 上启用 IPv6 地址系列

受影响的 GKE 版本:1.18、1.19、1.20.0 至 1.20.9

为使用 containerd 运行的 Pod 启用了 IPv6 映像系列。dockershim 映像会在所有 Pod 上停用 IPv6,而 containerd 映像不会。例如,localhost 会先解析为 IPv6 地址 ::1。这通常不会造成问题,但在某些情况下,这可能会导致意外行为。

解决方案

如需解决此问题,请明确使用 IPv4 地址(如 127.0.0.1),或将 Pod 中运行的应用配置为同时用于两个地址系列。

节点自动预配功能只会预配带有 Docker 节点池的 Container-Optimized OS

受影响的 GKE 版本:1.18、1.19、1.20.0 至 1.20.6-gke.1800

通过节点自动预配功能,您可以自动扩缩具有任何受支持的映像类型的节点池,但只能使用带有 Docker 的 Container-Optimized OS 映像类型创建新的节点池。

解决方案

要解决此问题,请将 GKE 集群升级到 1.20.6-gke.1800 版或更高版本。在这些 GKE 版本中,可以为集群设置默认映像类型。

172.17/16 IP 地址范围冲突

受影响的 GKE 版本:1.18.0 至 1.18.14

172.17/16 IP 地址范围会被启用了 containerd 的节点虚拟机上的 docker0 接口占用。向该范围发送或来自该范围的流量可能无法正确路由(例如,Pod 可能无法连接到 IP 地址在 172.17/16 中的 VPN 连接主机)。

未收集的 GPU 指标

受影响的 GKE 版本:1.18.0 至 1.18.18

在 GKE 1.18.18 版之前,如果将 containerd 用作运行时,系统不会收集 GPU 用量指标

解决方案

要解决此问题,请将集群升级到 GKE 1.18.18 版或更高版本。

不能在 containerd 中使用 config.mediaType 设置为 application/octet-stream 的映像

受影响的 GKE 版本:全部

不能在 containerd 中使用 config.mediaType 设置为 "application/octet-stream" 的映像。如需了解详情,请参阅 GitHub 问题 #4756。这些映像与 Open Container Initiative 规范不兼容,被视为不正确。这些映像可与 Docker 结合使用来提供向后兼容性,而在 containerd 中,这些映像不受支持。

症状和诊断

节点日志中的错误示例:

Error syncing pod <pod-uid> ("<pod-name>_<namespace>(<pod-uid>)"), skipping: failed to "StartContainer" for "<container-name>" with CreateContainerError: "failed to create containerd container: error unpacking image: failed to extract layer sha256:<some id>: failed to get reader from content store: content digest sha256:<some id>: not found"

映像清单通常可在托管它的注册表中找到。获得清单后,请检查 config.mediaType 以确定您是否遇到了此问题:

"mediaType": "application/octet-stream",

解决方案

由于 containerd 社区决定不支持此类映像,因此所有 containerd 版本都会受到影响,并且没有修复。您必须使用 Docker 1.11 或更高版本重新构建容器映像,并且必须确保 config.mediaType 字段未设置为 "application/octet-stream"

CNI 配置未初始化

受影响的 GKE 版本:全部

GKE 无法在升级、调整大小或其他操作期间创建节点。

症状和诊断

Google Cloud 控制台中的示例错误:

Error: "runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized".

此错误可能会在以下情况下发生:

  • 当 GKE 安装 CNI 配置时,在节点引导期间显示在日志文件中。
  • 如果拦截创建 Pod 的 DaemonSet 控制器命令的自定义网络钩子有错误,在 Google Cloud 控制台中显示为节点错误状态。这会阻止 GKE 创建 netdcalico-node Pod。如果当错误仍然存在时 netdcalico-node Pod 成功启动,请与支持团队联系。

解决方案

如需解决此问题,请尝试以下解决方案:

  • 等待 GKE 完成 CNI 配置的安装。
  • 移除所有配置错误的网络钩子。
  • 配置网络钩子以忽略系统 Pod

后续步骤

如果您需要其他帮助,请与 Cloud Customer Care 联系。