컨테이너 런타임 문제 해결


이 문서에서는 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 버전으로 업그레이드하세요.

배열이 사전 이스케이프 처리되지 않은 CMD 또는 ENTRYPOINT 명령줄이 있는 컨테이너 이미지가 containerd를 사용하는 Windows 노드 풀에서 실패함

이 문제는 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 버전으로 업그레이드하세요.

존재하지 않는 경로 또는 Linux와 비슷한(슬래시) 경로가 있는 컨테이너 이미지 볼륨이 containerd를 사용하는 Windows 노드 풀에서 실패함

이 문제는 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 엔드포인트는 Kubernetes 시스템 구성요소 측정항목에 설명된 대로 Prometheus 측정항목을 제공합니다. 해당 엔드포인트에 의존하는 측정항목 수집기를 설치하면 다음 문제가 나타날 수 있습니다.

  • 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(예를 들어 v0.42.0-containerd-cri)인 최신 cAdvisor 출시 버전을 찾습니다.
  2. cAdvisor Kubernetes Daemonset의 단계를 따라 데몬 세트를 만듭니다.
  3. 설치된 측정항목 수집기가 Prometheus 컨테이너 측정항목을 온전히 제공하는 cAdvisor /metrics 엔드포인트를 사용하도록 지정합니다.

대안

  1. 컨테이너 측정항목을 온전히 제공하는 Cloud Monitoring으로 모니터링 솔루션을 마이그레이션합니다.
  2. 엔드포인트가 /stats/summaryKubelet 요약 API에서 측정항목을 수집합니다.

GKE Windows에서 컨테이너 런타임이 다시 시작된 후 연결 기반 작업이 올바르게 작동하지 않음

영향을 받는 GKE 버전: 1.21 ~ 1.21.5-gke.1802, 1.22 ~ 1.22.3-gke.700

containerd 런타임(버전 1.5.4 및 1.5.7-gke.0)을 사용하는 Windows Server 노드 풀을 실행 중인 GKE 클러스터에서 컨테이너 런타임이 강제로 다시 시작되면 실행 중인 기존 컨테이너에 대한 연결 작업에서 IO를 다시 바인딩할 수 없는 문제가 발생할 수 있습니다. 이 문제가 발생하면 API 호출은 실패하지는 않지만 데이터 송수신이 불가능합니다. 여기에는 연결 및 로그 CLI, 클러스터 API 서버를 통한 API의 데이터가 포함됩니다.

솔루션

이 문제를 해결하려면 최신 GKE 출시 버전을 사용하여 패치가 적용된 컨테이너 런타임 버전(1.5.7-gke.1)으로 업그레이드하세요.

포드에 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 누출 문제가 발생할 수 있고 한 노드에서 모든 포드 IP를 소진할 수 있습니다. 영향을 받는 노드에서 예약된 포드는 다음과 비슷한 오류 메시지를 표시합니다.

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 이미지의 동작과 다릅니다. 포드에 대해 정의된 실행 프로브가 dockershim 이미지에서 선언된 Kubernetes timeoutSeconds 임곗값을 초과하면 프로브 실패로 처리됩니다. containerd 이미지에서 선언된 timeoutSeconds 임곗값 후에 반환된 프로브 결과는 무시됩니다.

솔루션

GKE에서 기능 게이트 ExecProbeTimeoutfalse로 설정되며 변경할 수 없습니다. 이 문제를 해결하려면 영향을 받는 모든 실행 프로브의 timeoutSeconds 임곗값을 늘리거나 제한 시간 기능을 프로브 로직의 일부로 구현하세요.

비공개 레지스트리 문제 해결

이 섹션에서는 containerd의 비공개 레지스트리 구성에 대한 문제 해결 정보를 제공합니다.

x509 오류가 발생하면서 이미지 가져오기 실패: 알 수 없는 기관에서 서명한 인증서

이 문제는 GKE에서 특정 비공개 레지스트리 도메인의 인증서를 찾을 수 없는 경우에 발생합니다. 다음 쿼리를 사용하여 Cloud Logging에서 이 오류를 확인할 수 있습니다.

  1. Google Cloud 콘솔의 로그 탐색기 페이지로 이동합니다.

    로그 탐색기로 이동

  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 필드의 보안 비밀 경로가 올바른지 확인합니다.
  3. 클러스터의 IAM 서비스 계정에 보안 비밀에 액세스할 수 있는 올바른 권한이 있는지 확인합니다.
  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 구성 파일을 사용하여 Secret Manager에 저장한 인증서를 사용하여 비공개 레지스트리에 액세스하도록 GKE에 지시할 수 있습니다. 자세한 내용은 비공개 CA 인증서로 비공개 레지스트리에 액세스를 참조하세요.

권한이 있는 DaemonSet을 구성하여 containerd 구성 수정

표준 클러스터의 경우 다음 단계를 따르세요. 권한이 있는 컨테이너는 보안 위험이므로 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: registry.k8s.io/startup-script:v2
              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 값을 DOMAIN_NAME:PORT 형식의 로컬 HTTP 레지스트리 주소로 바꿉니다. 예를 들면 다음과 같습니다.

    containers:
    - name: startup-script
      ...
      env:
      - name: ADDRESS
        value: "example.com:5000"
    
  2. DaemonSet를 배포합니다.

    kubectl apply -f insecure-registry-ds.yaml
    

DaemonSet는 모든 노드의 containerd 구성에 안전하지 않은 레지스트리를 추가합니다.

containerd는 권한이 있는 포드에 대한 모든 기기 매핑을 무시함

영향을 받는 GKE 버전: 모두

권한이 있는 Kubernetes 포드의 경우, 컨테이너 런타임은 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에서 containerd-shim-runc-v2 프로세스를 삭제할 수 없어 프로세스가 유출될 수 있습니다. 노드에서 유출이 발생하면 노드에서 containerd-shim-runc-v2 프로세스가 노드의 포드 수보다 더 많이 표시됩니다. 추가 PID와 함께 메모리 및 CPU 사용량이 증가할 수도 있습니다. 자세한 내용은 GitHub 문제 높은 IO 압력으로 인한 shim 누출 수정을 참조하세요.

이 문제를 해결하려면 다음 버전 이상으로 노드를 업그레이드하세요.

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

containerd를 실행하는 포드에는 IPv6 주소 계열이 사용 설정됨

영향을 받는 GKE 버전: 1.18, 1.19, 1.20.0 ~ 1.20.9

containerd로 실행되는 포드에는 IPv6 이미지 계열이 사용 설정됩니다. dockershim 이미지는 모든 포드에서 IPv6를 사용 중지하지만 containerd 이미지는 그러지 않습니다. 예를 들어 localhost는 IPv6 주소 ::1을 먼저 확인합니다. 일반적으로는 문제가 되지 않지만, 경우에 따라 예상치 못한 동작이 발생할 수 있습니다.

솔루션

이 문제를 해결하려면 127.0.0.1 같은 IPv4 주소를 명시적으로 사용하거나 두 주소 계열에서 모두 작동하도록 포드에서 실행 중인 애플리케이션을 구성하세요.

노드 자동 프로비저닝은 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가 사용 설정된 노드 VM의 docker0 인터페이스에서 사용됩니다. 이 범위로 송수신되는 트래픽이 올바르게 라우팅되지 않을 수 있습니다. 예를 들어 포드가 172.17/16 내에 있는 IP 주소를 사용하여 VPN 연결 호스트에 연결하지 못할 수 있습니다.

GPU 측정항목이 수집되지 않음

영향을 받는 GKE 버전: 1.18.0 ~ 1.18.18

GKE 1.18.18 이전 버전에서 containerd를 런타임으로 사용하면 GPU 사용량 측정항목이 수집되지 않습니다.

솔루션

이 문제를 해결하려면 클러스터를 GKE 버전 1.18.18 이상으로 업그레이드하세요.

config.mediaTypeapplication/octet-stream으로 설정된 이미지는 containerd에서 사용 불가

영향을 받는 GKE 버전: 모두

config.mediaType"application/octet-stream"으로 설정된 이미지는 containerd에서 사용할 수 없습니다. 자세한 내용은 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 구성을 설치하는 경우
  • Google Cloud 콘솔에서 노드가 오류 상태일 때 포드를 만들기 위해 DaemonSet 컨트롤러 명령어를 가로채는 커스텀 웹훅에 오류가 있는 경우 이렇게 하면 GKE에서 netd 또는 calico-node 포드를 만들 수 없습니다. 오류가 지속되는 동안 netd 또는 calico-node 포드가 성공적으로 시작되면 지원팀에 문의하세요.

솔루션

이 문제를 해결하려면 다음 솔루션을 시도해 보세요.

  • GKE가 CNI 구성 설치를 완료할 때까지 기다립니다.
  • 다음 안내에 따라 잘못 구성된 웹훅을 삭제합니다.

    1. 웹훅을 나열합니다.

          $ kubectl get mutatingwebhookconfigurations
          $ kubectl get validatingwebhookconfigurations
      
    2. 잘못 구성된 웹훅을 삭제합니다.

          kubectl delete mutatingwebhookconfigurations WEBHOOK_NAME
          kubectl delete validatingwebhookconfigurations WEBHOOK_NAME
      

    WEBHOOK_NAME을 삭제하려는 구성이 잘못된 웹훅의 이름으로 바꿉니다.

  • 시스템 포드를 무시하도록 웹훅을 구성합니다.

다음 단계

추가 지원이 필요하면 Cloud Customer Care에 문의하세요.