문제해결

Google Kubernetes Engine(GKE) 사용 중에 문제가 발생할 경우 유용하게 활용할 수 있는 문제해결 단계를 알아봅니다.

Kubernetes 리소스 디버깅

클러스터와 관련된 문제가 발생하는 경우, Kubernetes 문서의 클러스터 문제해결을 참조하세요.

애플리케이션, Pod 또는 컨트롤러 객체에 문제가 있는 경우 애플리케이션 문제 해결을 참조하세요.

kubectl 명령어를 찾을 수 없음

  1. 다음 명령어를 실행하여 kubectl 바이너리를 설치합니다.

    sudo gcloud components update kubectl
    
  2. 설치 프로그램에서 $PATH 환경 변수를 수정하라는 메시지가 표시되면 '예'라고 답합니다. 이 변수를 수정하면 전체 파일 경로를 입력하지 않고 kubectl 명령어를 사용할 수 있습니다.

    또는 다음 줄을 ~/.bashrc(macOS에서는 ~/.bash_profile 또는 셸이 환경 변수를 저장하는 위치)에 추가합니다.

    export PATH=$PATH:/usr/local/share/google/google-cloud-sdk/bin/
    
  3. 다음 명령어를 실행하여 업데이트된 .bashrc(또는 .bash_profile) 파일을 로드합니다.

    source ~/.bashrc
    

kubectl 명령어가 'connection refused' 오류 반환

다음 명령어로 클러스터 컨텍스트를 설정합니다.

gcloud container clusters get-credentials cluster-name

cluster-name에 입력할 내용을 모르겠으면 다음 명령어를 사용하여 클러스터를 나열합니다.

gcloud container clusters list

kubectl 명령어가 'failed to negotiate an api version' 오류 반환

kubectl에 사용자 인증 정보가 있는지 확인합니다.

gcloud auth application-default login

kubectl logs, attach, exec, port-forward 명령어가 응답하지 않음

이러한 명령어는 클러스터의 제어 영역(마스터)이 클러스터 내 노드와 통신할 수 있어야 작동합니다. 하지만 제어 영역이 클러스터 노드와 동일한 Compute Engine 네트워크에 있지 않기 때문에 보안 통신을 사용 설정하기 위해 SSH 터널이 사용됩니다.

GKE는 Compute Engine 프로젝트 메타데이터에 SSH 공개 키 파일을 저장합니다. Google 제공 이미지를 사용하는 모든 Compute Engine VM은 VM의 승인된 사용자 목록에 추가할 SSH 키를 위해 프로젝트의 공통 메타데이터와 인스턴스의 메타데이터를 정기적으로 확인합니다. 또한 GKE는 제어 영역의 IP 주소에서 클러스터의 각 노드로 SSH 액세스를 허용하는 방화벽 규칙을 Compute Engine 네트워크에 추가합니다.

위의 kubectl 명령어가 실행되지 않으면 API 서버가 노드에 SSH 터널을 열지 못할 수 있습니다. 가능한 원인을 확인합니다.

  1. 클러스터에 노드가 없습니다.

    클러스터의 노드 수를 0으로 축소했다면 SSH 터널이 작동하지 않습니다.

    이를 수정하려면 적어도 노드가 하나가 되도록 클러스터의 크기를 조절합니다.

  2. 클러스터의 포드가 종료 중 상태로 멈춰서 더 이상 존재하지 않는 노드를 클러스터에서 삭제할 수 없습니다.

    이것은 Kubernetes 버전 1.1에만 해당되는 문제지만 클러스터의 반복적 크기 조절로 인해 발생할 수 있습니다.

    이 문제를 해결하려면 몇 분 이상 종료 중 상태에 있는 포드를 삭제하세요. 그러면 제어 영역에서 이전 노드가 제거되고 새 노드로 대체됩니다.

  3. 네트워크의 방화벽 규칙은 제어 영역에 대해 SSH 액세스를 허용하지 않습니다.

    모든 Compute Engine 네트워크는 모든 IP 주소에서 SSH 액세스를 허용하는(물론 유효한 비공개 키 필요) default-allow-ssh라는 방화벽 규칙으로 생성됩니다. 또한 GKE는 특별히 클러스터의 제어 영역에서 클러스터의 노드로 SSH 액세스를 허용하는 gke-cluster-name-random-characters-ssh 형식의 SSH 규칙을 각 공개 클러스터에 삽입합니다. 이러한 규칙 중 어느 규칙이라도 존재하지 않으면 제어 영역이 SSH 터널을 열 수 없습니다.

    이 문제를 해결하려면 클러스터의 모든 노드에 있는 태그를 사용하여 제어 영역의 IP 주소에서 VM으로 액세스를 허용하는 방화벽 규칙을 다시 추가합니다.

  4. 프로젝트의 'ssh-keys' 공통 메타데이터 항목이 가득 찼습니다.

    'ssh-keys'라는 프로젝트의 메타데이터 항목이 최대 크기 제한에 가까워지면 GKE는 SSH 터널을 열기 위해 자체 SSH 키를 추가할 수 없습니다. 다음 명령어를 실행하면 프로젝트의 메타데이터를 표시할 수 있습니다.

    gcloud compute project-info describe [--project=PROJECT]
    

    그런 다음 ssh-key 목록의 길이를 확인할 수 있습니다.

    이 문제를 해결하려면 더 이상 필요 없는 일부 SSH 키를 삭제합니다.

  5. 클러스터의 VM에서 'ssh-keys' 키로 메타데이터 필드를 설정했습니다.

    VM의 노드 에이전트는 프로젝트 전체 SSH 키보다 인스턴스별 ssh-keys를 선호합니다. 따라서 특별히 클러스터 노드에 SSH 키를 설정한 경우, 프로젝트 메타데이터에 있는 제어 영역의 SSH 키가 노드에 사용되지 않습니다. 확인하려면 gcloud compute instances describe <VM-name>을 실행하고 메타데이터에서 'ssh-keys' 필드를 찾습니다.

    이를 수정하려면 인스턴스 메타데이터에서 인스턴스별 SSH 키를 삭제합니다.

이러한 기능은 클러스터의 정상 작동에는 필요하지 않습니다. 클러스터의 네트워크를 모든 외부 액세스로부터 잠그려고 하면 이러한 기능이 작동하지 않습니다.

노드 버전이 제어 영역 버전과 호환되지 않음

클러스터의 제어 영역이 실행 중인 Kubernetes 버전을 확인한 다음 클러스터의 노드 풀이 실행 중인 Kubernetes 버전을 확인하세요. 클러스터의 노드 풀 중 제어 영역보다 오래된 부 버전이 2개 초과이면 클러스터에 문제가 발생할 수 있습니다.

GKE팀은 사용자를 대신하여 클러스터 제어 영역의 업그레이드를 주기적으로 수행합니다. 제어 영역은 안정적인 새 Kubernetes 버전으로 업그레이드됩니다. 기본적으로 클러스터 노드는 자동 업그레이드가 사용 설정되어 있으며, 사용 중지하지 않는 것이 좋습니다.

클러스터 노드에 자동 업그레이드가 사용 중지되어 있고 노드 풀 버전을 제어 영역과 호환되는 버전으로 수동으로 업그레이드하지 않으면 자동으로 업그레이드되는 제어 영역과 노드는 결국 호환되지 않게 됩니다. 클러스터의 제어 영역과 노드 간의 비호환성으로 인해 예기치 않은 문제가 발생할 수 있습니다.

Kubernetes 버전 및 버전 차이 지원 정책은 제어 영역보다 최대 2개 부 버전이 낮은 노드와 제어 영역의 호환을 보장합니다. 예를 들어 Kubernetes 1.19 제어 영역은 Kubernetes 1.19, 1.18, 1.17 노드와 호환됩니다. 이 문제를 해결하려면 노드 풀 버전을 제어 영역과 호환되는 버전으로 수동으로 업그레이드하세요.

영향을 받는 노드에서 실행되는 워크로드가 업그레이드 프로세스로 인해 중단되는 것이 우려되는 경우 여러 머신 유형에 워크로드 마이그레이션 가이드의 워크로드 마이그레이션 섹션에 있는 단계를 따르세요. 이 단계를 통해 새 노드 풀을 만든 다음 이전 노드 풀을 차단하고 드레이닝하여 원활하게 마이그레이션 할 수 있습니다.

Cloud Monitoring에 클러스터의 측정항목이 표시되지 않음

프로젝트에서 Cloud Monitoring APICloud Logging API를 활성화했고 Cloud Monitoring에서 프로젝트를 볼 수 있는지 확인합니다.

문제가 계속되면 다음과 같은 가능한 원인을 확인하세요.

  1. 클러스터에서 모니터링을 사용 설정했는지 확인합니다.

    기본적으로 Google Cloud Console과 gcloud 명령줄 도구에서 생성된 클러스터에는 Monitoring이 사용 설정되지만 다음 명령어를 실행하거나 Cloud Console에서 클러스터 세부정보를 클릭하여 사용 설정 여부를 확인할 수 있습니다.

    gcloud container clusters describe cluster-name
    

    이 명령어의 출력에서 'monitoringService'가 'monitoring.googleapis.com'이고 Cloud Monitoring이 Cloud Console에서 사용 설정된 것으로 나타나야 합니다.

    모니터링이 사용 설정되어 있지 않으면 다음 명령어를 실행하여 사용 설정합니다.

    gcloud container clusters update cluster-name --monitoring-service=monitoring.googleapis.com
    
  2. 클러스터가 만들어진 지 또는 모니터링이 사용 설정된 지 얼마나 되었나요?

    새 클러스터의 측정항목이 Cloud Monitoring에 표시되는 데 최대 1시간이 걸릴 수 있습니다.

  3. 클러스터에서 실행되는 heapster 또는 gke-metrics-agent(OpenTelemetry Collector)가 "kube-system" 네임스페이스에 있나요?

    클러스터에 리소스가 부족하므로 이 Pod가 워크로드를 예약하지 못할 수 있습니다. kubectl get pods --namespace=kube-system을 호출하고 이름에 heapster 또는 gke-metrics-agent가 있는 Pod를 찾아 Heapster 또는 OpenTelemetry가 실행 중인지 확인합니다.

  4. 클러스터의 제어 영역이 노드와 통신할 수 있나요?

    Cloud Monitoring이 작동하려면 마스터가 노드와 통신해야 합니다. 다음 명령어를 실행하면 마스터가 노드와 통신하는지 확인할 수 있습니다.

    kubectl logs pod-name
    

    이 명령어가 오류를 반환하면 SSH 터널이 문제의 원인일 수 있습니다. 자세한 내용은 이 섹션을 참조하세요.

Cloud Logging 에이전트와 관련된 문제가 있으면 문제해결 문서를 참조하세요.

자세한 내용은 로깅 문서를 참조하세요.

오류 404: gcloud container 명령어를 호출할 때 리소스를 '찾을 수 없음'

gcloud 명령줄 도구를 다시 인증합니다.

gcloud auth login

오류 400/403: 계정에 수정 권한 없음

Compute Engine 기본 서비스 계정 또는 GKE와 연결된 서비스 계정이 수동으로 삭제되거나 수정되었습니다.

Compute Engine 또는 Kubernetes Engine API를 사용 설정하면 서비스 계정이 생성되고 프로젝트에 수정 권한이 부여됩니다. 특정 시점에 권한을 수정할 때 'Kubernetes Engine 서비스 에이전트' 역할을 삭제하거나 계정을 완전히 삭제하거나 API를 중지하면 클러스터 만들기 및 모든 관리 기능이 실패합니다.

Google Kubernetes Engine 서비스 계정의 이름은 다음과 같습니다. 여기서 project-number프로젝트 번호입니다.

service-project-number@container-engine-robot.iam.gserviceaccount.com

Google Kubernetes Engine 서비스 계정에서 Kubernetes Engine 서비스 에이전트 역할을 삭제한 경우 문제를 해결하려면 이를 다시 추가합니다. 그렇지 않으면 Kubernetes Engine API를 다시 사용 설정해야 합니다. 그러면 서비스 계정과 권한이 올바르게 복원됩니다. gcloud 도구 또는 Cloud Console에서 이 작업을 수행할 수 있습니다.

콘솔

  1. Cloud Console에서 API 및 서비스로 이동합니다.

    API 및 서비스 페이지

  2. 프로젝트를 선택합니다.

  3. API 및 서비스 사용 설정을 클릭합니다.

  4. Kubernetes를 검색한 후 검색 결과에서 API를 선택합니다.

  5. 사용 설정을 클릭합니다. 이전에 API를 사용 설정했으면 먼저 API를 중지했다가 다시 사용 설정해야 합니다. API와 관련 서비스가 사용 설정되는 데 몇 분 정도 걸릴 수 있습니다.

gcloud

gcloud 도구에서 다음 명령어를 실행합니다.

gcloud services enable container.googleapis.com

1.9.x 이상에서 1.8.x 이하 자동 방화벽 규칙 복제

클러스터가 Kubernetes 버전 1.9.x를 실행하는 경우, GKE 클러스터의 작업 부하가 클러스터 외부에 있지만 같은 네트워크에 있는 다른 Compute Engine VM과 통신을 시작하는 것을 허용하지 않도록 자동 방화벽 규칙이 변경되었습니다.

다음 단계를 수행하여 1.8.x(및 이전) 클러스터의 자동 방화벽 규칙 동작을 복제할 수 있습니다.

  1. 클러스터의 네트워크를 찾습니다.

    gcloud container clusters describe cluster-name --format=get"(network)"
    
  2. 컨테이너에 사용되는 클러스터의 IPv4 CIDR을 가져옵니다.

    gcloud container clusters describe cluster-name --format=get"(clusterIpv4Cidr)"
    
  3. CIDR을 소스 범위로 하여 네트워크의 방화벽 규칙을 만들고 모든 프로토콜을 허용합니다.

    gcloud compute firewall-rules create "cluster-name-to-all-vms-on-network" \
      --network="network" --source-ranges="cluster-ipv4-cidr" \
      --allow=tcp,udp,icmp,esp,ah,sctp
    

기본 서비스 계정을 GCP 프로젝트에 복원

GKE의 기본 서비스 계정인 container-engine-robot이 실수로 프로젝트에서 결합 해제될 수 있습니다. GKE Service Agent는 서비스 계정에 클러스터 리소스를 관리할 권한을 부여하는 ID 및 액세스 관리(IAM) 역할입니다. 서비스 계정에서 이 역할 결합을 제거하면 기본 서비스 계정이 프로젝트에서 결합 해제되어 애플리케이션 배포와 기타 클러스터 작업 수행이 불가능해질 수 있습니다.

gcloud 도구 또는 Cloud Console을 사용하여 프로젝트에서 서비스 계정이 삭제되었는지 확인할 수 있습니다.

gcloud

다음 명령어를 실행합니다.

gcloud projects get-iam-policy project-id

project-id를 프로젝트 ID로 바꿉니다.

Console

Cloud Console에서 IAM 및 관리자 페이지로 이동합니다.

명령어나 대시보드에 서비스 계정 중 container-engine-robot이 표시되지 않으면 서비스 계정이 결합 해제된 것입니다.

GKE Service Agent 역할 결합을 삭제했으면 다음 명령어를 실행하여 역할 결합을 복원합니다.

PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format "value(projectNumber)")
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
  --member "serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
  --role roles/container.serviceAgent

역할 결합이 부여되었는지 확인하려면 다음 명령어를 실행합니다.

gcloud projects get-iam-policy $PROJECT_ID

container.serviceAgent 역할과 함께 서비스 계정 이름이 표시되면 역할 결합이 부여된 것입니다. 예를 들면 다음과 같습니다.

- members:
  - serviceAccount:service-1234567890@container-engine-robot.iam.gserviceaccount.com
  role: roles/container.serviceAgent

Cloud KMS 키가 사용 중지된 경우

GKE의 기본 서비스 계정은 애플리케이션 수준의 보안 비밀 암호화에 사용 중지된 Cloud KMS 키를 사용할 수 없습니다.

중지된 키를 다시 사용 설정하려면 중지된 키 버전 사용 설정을 참조하세요.

노드 할당 가능을 사용 설정한 후 Pod가 대기 중 상태에서 멈춤

노드 할당 가능을 사용 설정한 후 Pod가 대기 중 상태에서 멈추는 문제가 발생하면 다음 사항에 유의하세요.

1.7.6 버전부터는 GKE에서 Docker 및 운영체제를 포함하여 Kubernetes 오버헤드에 사용할 CPU와 메모리를 예약합니다. Pod에서 각 머신 유형별로 예약 가능한 양에 대한 자세한 내용은 클러스터 아키텍처를 참조하세요.

업그레이드 후 Pod가 대기 중이면 다음을 수행하는 것이 좋습니다.

  1. Pod의 CPU 및 메모리 요청이 최대 사용량을 초과하지 않는지 확인합니다. GKE에서 오버헤드에 사용할 CPU와 메모리를 예약하는 반면 Pod는 이러한 리소스를 요청할 수 없습니다. Pod가 사용량 보다 많은 CPU 또는 메모리를 요청하면 다른 Pod가 이러한 리소스를 요청하지 못하게 되어 클러스터 사용률이 떨어질 수 있습니다. 자세한 내용은 리소스 요청이 있는 Pod 예약 방법을 참조하세요.

  2. 클러스터 크기를 조정해 봅니다. 자세한 내용은 클러스터 크기 조절을 참조하세요.

  3. 클러스터를 다운그레이드하여 이 변경사항을 되돌립니다. 자세한 내용은 클러스터 또는 노드 풀 수동 업그레이드를 참조하세요.

비공개 클러스터 노드가 생성되었지만 클러스터에 조인되지 않음

비공개 클러스터에서 사용 중인 VPC에서 커스텀 라우팅 및 타사 네트워크 어플라이언스를 사용할 때 기본 인터넷 게이트웨이 대신 기본 경로(0.0.0.0/0)가 리디렉션되는 경우가 많습니다. 제어 영역 연결 외에도 다음 대상에 연결할 수 있는지 확인해야 합니다.

  • *.googleapis.com
  • *.gcr.io
  • gcr.io

3개 도메인 모두에 대해 비공개 Google 액세스를 구성합니다. 이 권장사항을 따르면 새 노드가 인터넷 연결 트래픽 제한을 제한하면서 클러스터를 시작하고 조인할 수 있습니다.

배포된 워크로드 문제 해결

워크로드 Pod에 문제가 있으면 GKE가 오류를 반환합니다. kubectl 명령줄 도구 또는 Cloud Console을 사용하여 Pod 상태를 확인할 수 있습니다.

kubectl

클러스터에서 실행 중인 모든 Pod를 확인하려면 다음 명령어를 실행합니다.

kubectl get pods

다음과 같이 출력됩니다.

NAME       READY  STATUS             RESTARTS  AGE
pod-name   0/1    CrashLoopBackOff   23        8d

특정 Pod에 대한 자세한 내용을 보려면 다음 명령어를 실행합니다.

kubectl describe pod pod-name

pod-name을 원하는 Pod의 이름으로 바꿉니다.

콘솔

다음 단계를 수행합니다.

  1. Cloud Console에서 GKE 워크로드 대시보드로 이동합니다.

    GKE 워크로드 대시보드로 이동

  2. 원하는 작업 부하를 선택합니다. 개요 탭에 워크로드 상태가 표시됩니다.

  3. 관리형 Pod 섹션에서 오류 상태 메시지를 클릭합니다.

다음 섹션에서는 워크로드가 반환하는 몇 가지 일반적인 오류와 그 해결 방법을 설명합니다.

CrashLoopBackOff

CrashLoopBackOff는 컨테이너가 다시 시작 후 반복적으로 비정상 종료됨을 나타냅니다. 컨테이너는 다양한 원인으로 인해 비정상 종료될 수 있습니다. Pod의 로그를 확인하면 근본 원인을 해결할 수 있습니다.

기본적으로 비정상 종료된 컨테이너는 5분으로 제한된 지수 지연으로 다시 시작됩니다. spec: restartPolicy에서 restartPolicy 필드 배포의 Pod 사양을 설정하여 이 동작을 변경할 수 있습니다. 이 필드의 기본값은 Always입니다.

kubectl 명령줄 도구 또는 Cloud Console을 사용하여 Pod의 컨테이너가 비정상 종료되는 원인을 확인할 수 있습니다.

kubectl

클러스터에서 실행 중인 모든 Pod를 확인하려면 다음 명령어를 실행합니다.

kubectl get pods

CrashLoopBackOff 오류가 있는 Pod를 찾습니다.

Pod의 로그를 가져오려면 다음 명령어를 실행합니다.

kubectl logs pod-name

pod-name을 문제가 있는 Pod의 이름으로 바꿉니다.

-p 플래그를 전달하여 Pod 컨테이너의 이전 인스턴스 로그(있는 경우)를 가져올 수도 있습니다.

콘솔

다음 단계를 수행합니다.

  1. Cloud Console에서 GKE 워크로드 대시보드로 이동합니다.

    GKE 워크로드 대시보드로 이동

  2. 원하는 작업 부하를 선택합니다. 개요 탭에 워크로드 상태가 표시됩니다.

  3. 관리형 포드 섹션에서 문제가 있는 포드를 클릭합니다.

  4. 포드의 메뉴에서 로그 탭을 클릭합니다.

비정상 종료된 컨테이너의 '종료 코드' 확인

다음 태스크를 수행하여 종료 코드를 확인할 수 있습니다.

  1. 다음 명령어를 실행합니다.

    kubectl describe pod pod-name
    

    pod-name을 Pod의 이름으로 바꿉니다.

  2. containers: container-name: last state: exit code 필드 값을 검토합니다.

    • 종료 코드가 1이면 애플리케이션 비정상 종료로 인해 컨테이너가 비정상 종료된 것입니다.
    • 종료 코드가 0이면 앱이 실행된 시간을 확인합니다.

    애플리케이션의 기본 프로세스가 종료되면 컨테이너가 종료됩니다. 앱 실행이 아주 빨리 끝나면 컨테이너가 계속 다시 시작될 수 있습니다.

실행 중인 컨테이너에 연결

Pod에 대한 셸을 엽니다.

kubectl exec -it pod-name -- /bin/bash

pod에 컨테이너가 두 개 이상 있으면 -c container-name을 추가합니다.

이제 컨테이너에서 bash 명령어를 실행할 수 있습니다. 네트워크를 테스트하거나 애플리케이션에서 사용하는 파일 또는 데이터베이스에 액세스할 수 있는지 확인할 수 있습니다.

ImagePullBackOff 및 ErrImagePull

ImagePullBackOffErrImagePull은 컨테이너가 사용하는 이미지를 이미지 레지스트리에서 로드할 수 없음을 나타냅니다.

Cloud Console 또는 kubectl 명령줄 도구를 사용하여 이 문제를 확인할 수 있습니다.

kubectl

포드의 컨테이너 이미지에 대한 자세한 정보를 보려면 다음 명령어를 실행합니다.

kubectl describe pod pod-name

콘솔

다음 단계를 수행합니다.

  1. Cloud Console에서 GKE 워크로드 대시보드로 이동합니다.

    GKE 워크로드 대시보드로 이동

  2. 원하는 작업 부하를 선택합니다. 개요 탭에 워크로드 상태가 표시됩니다.

  3. 관리형 포드 섹션에서 문제가 있는 포드를 클릭합니다.

  4. 포드의 메뉴에서 이벤트 탭을 클릭합니다.

이미지를 찾을 수 없는 경우

이미지를 찾을 수 없는 경우, 다음을 수행합니다.

  1. 이미지의 이름이 올바른지 확인합니다.
  2. 이미지의 태그가 올바른지 확인합니다. (:latest를 사용하거나 태그를 사용하지 않으면 최신 이미지를 가져올 수 있습니다.)
  3. 이미지에 전체 레지스트리 경로가 있으면 사용 중인 Docker 레지스트리에 있는지 확인합니다. 이미지 이름만 제공하는 경우, Docker Hub 레지스트리를 확인합니다.
  4. Docker 이미지를 수동으로 가져옵니다.

    • SSH를 통해 노드에 연결합니다.

      예를 들어 us-central1-a 영역의 example-instance에 SSH로 연결하려면 다음을 실행합니다.

      gcloud compute ssh example-instance --zone us-central1-a
      
    • docker pull image-name를 실행합니다.

    이 방법이 효과적이면 Pod에서 ImagePullSecrets을 지정해야 할 수도 있습니다. 포드는 자체 네임스페이스에 있는 이미지 가져오기 보안 비밀만을 참조할 수 있으므로 이 프로세스는 네임스페이스당 한 번 수행해야 합니다.

'permission denied' 또는 'no pull access' 오류가 발생하면 로그인되어 있고 이미지에 대한 액세스 권한이 있는지 확인합니다.

비공개 레지스트리를 사용 중인 경우 이미지를 읽으려면 키가 필요할 수 있습니다.

이미지가 Container Registry에서 호스팅되면 노드 풀과 연결된 서비스 계정에 이미지가 포함된 Cloud Storage 버킷에 대한 읽기 액세스 권한이 필요합니다. 자세한 내용은 Container Registry 문서를 참조하세요.

예약할 수 없는 Pod

PodUnschedulable은 리소스 부족 또는 일부 구성 오류로 인해 Pod를 예약할 수 없음을 나타냅니다.

리소스 부족

CPU, 메모리 또는 다른 리소스 부족을 나타내는 오류가 발생할 수 있습니다. 예: '모든 조건자와 일치하는 노드를 사용할 수 없음: cpu 부족 (2)'은 2개의 노드에 포드의 요청 충족에 사용할 수 있는 CPU가 충분하지 않음을 나타냅니다.

기본 CPU 요청은 100m 또는 CPU의 10%(또는 코어 한 개)입니다. 더 많은 리소스 또는 더 적은 리소스를 요청하려면 spec: containers: resources: requests의 Pod 사양에 값을 지정합니다.

MatchNodeSelector

MatchNodeSelector는 Pod의 라벨 선택기와 일치하는 노드가 없음을 나타냅니다.

spec: nodeSelector에서 Pod 사양의 nodeSelector 필드에 지정된 라벨을 통해 이를 확인할 수 있습니다.

클러스터의 노드에 라벨이 어떻게 지정되어 있는지 확인하려면 다음 명령어를 실행합니다.

kubectl get nodes --show-labels

노드에 라벨을 연결하려면 다음 명령어를 실행합니다.

kubectl label nodes node-name label-key=label-value

다음을 바꿉니다.

  • node-name을 원하는 노드로 바꿉니다.
  • label-key를 라벨 키로 바꿉니다.
  • label-value를 라벨 값으로 바꿉니다.

자세한 내용은 포드를 노드에 할당을 참조하세요.

PodToleratesNodeTaint

PodToleratesNodeTaints는 현재 노드 taint를 허용하는 노드가 없으므로 Pod를 어떠한 노드에도 예약할 수 없음을 나타냅니다.

이 경우인지 확인하려면 다음 명령어를 실행합니다.

kubectl describe nodes node-name

출력에서 키-값 쌍과 예약 효과가 나열되는 Taints 필드를 확인합니다.

나열된 효과가 NoSchedule이면 일치하는 톨러레이션이 없는 한 노드에서 Pod를 예약할 수 없습니다.

이 문제를 해결하는 한 가지 방법은 taint를 제거하는 것입니다. 예를 들어 NoSchedule taint를 삭제하려면 다음 명령어를 실행합니다.

kubectl taint nodes node-name key:NoSchedule-

PodFitsHostPort

PodFitsHostPorts는 노드가 사용하려 하는 포트가 이미 사용 중임을 나타냅니다.

이 문제를 해결하려면 spec: containers: ports: hostPort에서 Pod 사양의 hostPort 값을 확인합니다. 이 값을 다른 포트로 변경해야 할 수도 있습니다.

최소 가용성 없음

노드에 적절한 리소스가 있지만 여전히 Does not have minimum availability 메시지가 표시되면 Pod 상태를 확인합니다. 상태가 SchedulingDisabled 또는 Cordoned이면 노드가 새 Pod를 예약할 수 없습니다. Cloud Console 또는 kubectl 명령줄 도구를 사용하여 노드 상태를 확인할 수 있습니다.

kubectl

노드 상태를 가져오려면 다음 명령어를 실행합니다.

kubectl get nodes

노드에서 예약을 사용 설정하려면 다음을 실행합니다.

kubectl uncordon node-name

콘솔

다음 단계를 수행합니다.

  1. Cloud Console에서 GKE 워크로드 대시보드로 이동합니다.

    GKE 클러스터 대시보드로 이동

  2. 원하는 클러스터를 선택합니다. 노드 탭에 노드와 노드 상태가 표시됩니다.

노드에서 예약을 사용 설정하려면 다음 단계를 수행합니다.

  1. 목록에서 원하는 노드를 클릭합니다.

  2. 노드 세부정보에서 차단 해제 버튼을 클릭합니다.

결합 해제된 PersistentVolumeClaim

Unbound PersistentVolumeClaims는 Pod가 결합되지 않은 PersistentVolumeClaim을 참조함을 나타냅니다. 이 오류는 PersistentVolume이 프로비저닝을 실패한 경우에 발생할 수 있습니다. PersistentVolumeClaim의 이벤트를 가져오고 실패를 검사하여 프로비저닝 실패를 확인할 수 있습니다.

이벤트를 가져오려면 다음 명령어를 실행합니다.

kubectl describe pvc statefulset-name-pvc-name-0

다음을 바꿉니다.

  • statefulset-name을 StatefulSet 객체의 이름으로 바꿉니다.
  • pvc-name을 PersistentVolumeClaim 객체의 이름으로 바꿉니다.

이는 PersistentVolume을 수동으로 사전 프로비저닝하고 PersistentVolumeClaim에 결합하는 중에 구성 오류가 있어도 발생할 수 있습니다. 볼륨을 다시 사전 프로비저닝할 수 있습니다.

연결 문제

네트워크 개요 설명에서 언급한 것처럼, 효율적인 문제 해결을 위해서는 네트워크 네임스페이스에서 노드의 루트 네임스페이스로 pod가 연결된 방법을 이해하는 것이 중요합니다. 아래의 설명에서는 특별히 언급하지 않는 한 클러스터가 Calico 대신 GKE의 기본 CNI를 사용한다고 가정합니다. 즉, 네트워크 정책이 적용되지 않습니다.

선택 노드의 포드에 가용성 없음

선택 노드의 Pod에 네트워크 연결이 없으면 Linux 브리지가 작동 중인지 확인합니다.

ip address show cbr0

Linux 브리지가 작동 중지되었으면 다음 방법으로 작동시킵니다.

sudo ip link set cbr0 up

노드가 cbr0에 연결된 Pod MAC 주소를 학습하는지 확인합니다.

arp -an

선택 노드의 Pod에 최소 연결이 포함됨

선택 노드의 Pod에 최소 연결이 포함된 경우 먼저 도구 상자 컨테이너에서 tcpdump를 실행하여 손실된 패킷이 있는지 여부를 확인해야 합니다.

sudo toolbox bash

아직 설치하지 않았으면 도구 상자에서 tcpdump를 설치합니다.

apt install -y tcpdump

cbr0에 대해 tcpdump를 실행합니다.

tcpdump -ni cbr0 host hostname and port port-number and [tcp|udp|icmp]

브리지에서 큰 패킷이 다운스트림으로 삭제된 것으로 나타나면(예: TCP 핸드셰이크가 완료되었지만 SSL hello가 수신되지 않음) Linux 브리지 MTU가 클러스터의 VPC 네트워크의 MTU로 제대로 설정되었는지 확인합니다.

ip address show cbr0

오버레이가 사용된 경우(예: Weave 또는 Flannel), 오버레이의 암호화 오버헤드를 수용하도록 이 MTU를 더 줄여야 합니다.

간헐적인 연결 오류

포드에 대한 연결은 iptable에 의해 전달됩니다. 흐름은 conntrack 테이블의 항목으로 추적되며, 노드당 작업 부하가 많은 경우, conntrack 테이블 소진이 오류로 나타날 수 있습니다. 이러한 문제는 노드의 직렬 콘솔에 로깅될 수 있습니다. 예를 들면 다음과 같습니다.

nf_conntrack: table full, dropping packet

간헐적인 문제가 conntrack 소진으로 인해 발생한다는 사실을 확인할 수 있으면 클러스터 크기를 늘리거나(따라서 노드당 워크로드 및 흐름의 수를 줄이거나) nf_conntrack_max를 늘릴 수 있습니다.

new_ct_max=$(awk '$1 == "MemTotal:" { printf "%d\n", $2/32; exit; }' /proc/meminfo)
sysctl -w net.netfilter.nf_conntrack_max="${new_ct_max:?}" \
  && echo "net.netfilter.nf_conntrack_max=${new_ct_max:?}" >> /etc/sysctl.conf

컨테이너에 대해 'bind: Address already in use'가 보고됨

컨테이너 로그에 따라 애플리케이션이 바인딩하려는 포트가 이미 예약되었기 때문에 포드의 컨테이너를 시작할 수 없습니다. 컨테이너에 비정상 종료 루프가 발생 합니다. 예를 들어 Cloud Logging에서 다음이 발생합니다.

resource.type="container"
textPayload:"bind: Address already in use"
resource.labels.container_name="redis"

2018-10-16 07:06:47.000 CEST 16 Oct 05:06:47.533 # Creating Server TCP listening socket *:60250: bind: Address already in use
2018-10-16 07:07:35.000 CEST 16 Oct 05:07:35.753 # Creating Server TCP listening socket *:60250: bind: Address already in use

Docker가 충돌할 때 일부 경우에는 실행 중인 컨테이너가 뒤쳐져서 오래된 상태가 될 수 있습니다. 프로세스는 포드에 할당된 네트워크 네임스페이스에서 계속 실행 중이며, 해당 포트에서 수신 대기 중입니다. Docker 및 kubelet은 오래된 컨테이너를 모르므로 새 프로세스로 새 컨테이너를 시작하려 합니다. 따라서 이미 Pod와 연결된 네트워크 네임스페이스에 추가될 때 포트에서 결합할 수 없습니다.

이 문제를 진단하려면 다음 안내를 따르세요.

  1. .metadata.uuid 필드에 Pod의 UUID가 필요합니다.

    kubectl get pod -o custom-columns="name:.metadata.name,UUID:.metadata.uid" ubuntu-6948dd5657-4gsgg
    
    name                      UUID
    ubuntu-6948dd5657-4gsgg   db9ed086-edba-11e8-bdd6-42010a800164
    
  2. 노드에서 다음 명령어의 출력을 가져옵니다.

    docker ps -a
    ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
    
  3. 이 Pod에서 실행 중인 프로세스를 확인합니다. cgroup 네임스페이스의 UUID에는 Pod의 UUID가 포함되어 있으므로 ps 출력에서 Pod UUID에 grep을 실행할 수 있습니다. 앞의 라인에도 grep을 수행하므로 docker-containerd-shim 프로세스가 컨테이너 ID를 인수에 지정하게 됩니다. 출력이 더 간단해지도록 cgroup 열의 나머지 부분을 잘라냅니다.

    # ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep -B 1 db9ed086-edba-11e8-bdd6-42010a800164 | sed s/'blkio:.*'/''/
    1283089     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 276e173b0846e24b704d4 12:
    1283107 1283089 Ss   sys_pause            4026532393         pause           /pause                                     12:
    1283150     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim ab4c7762f5abf40951770 12:
    1283169 1283150 Ss   do_wait              4026532393         sh              /bin/sh -c echo hello && sleep 6000000     12:
    1283185 1283169 S    hrtimer_nanosleep    4026532393           sleep           sleep 6000000                            12:
    1283244     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 44e76e50e5ef4156fd5d3 12:
    1283263 1283244 Ss   sigsuspend           4026532393         nginx           nginx: master process nginx -g daemon off; 12:
    1283282 1283263 S    ep_poll              4026532393           nginx           nginx: worker process
    
  4. 이 목록에서 docker ps에도 표시되는 컨테이너 ID를 확인할 수 있습니다.

    주요 내용은 다음과 같습니다.

    • docker-containerd-shim 276e173b0846e24b704d4 - 일시 중지
    • docker-containerd-shim ab4c7762f5abf40951770 - sleep을 포함한 sh(sleep-ctr)
    • docker-containerd-shim 44e76e50e5ef4156fd5d3 - nginx(echoserver-ctr)
  5. docker ps 출력에서 해당 부분을 확인합니다.

    # docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3'
    44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f   gcr.io/google_containers/echoserver@sha256:3e7b182372b398d97b747bbe6cb7595e5ffaaae9a62506c725656966d36643cc                   "nginx -g 'daemon off;'"                                                                                                                                                                                                                                                                                                                                                                     14 hours ago        Up 14 hours                             k8s_echoserver-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    ab4c7762f5abf40951770d3e247fa2559a2d1f8c8834e5412bdcec7df37f8475   ubuntu@sha256:acd85db6e4b18aafa7fcde5480872909bd8e6d5fbd4e5e790ecc09acc06a8b78                                                "/bin/sh -c 'echo hello && sleep 6000000'"                                                                                                                                                                                                                                                                                                                                                   14 hours ago        Up 14 hours                             k8s_sleep-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    276e173b0846e24b704d41cf4fbb950bfa5d0f59c304827349f4cf5091be3327   k8s.gcr.io/pause-amd64:3.1
    

    일반적인 경우에는 ps의 모든 컨테이너 ID가 docker ps에 표시됩니다. 표시되지 않은 항목이 있으면 이는 오래된 컨테이너이고, 이미 사용 중인 것으로 보고되는 TCP 포트에서 리슨 중인 docker-containerd-shim process의 하위 프로세스가 표시됩니다.

    이를 확인하려면 컨테이너의 네트워크 네임스페이스에서 netstat을 실행합니다. Pod에서 모든 컨테이너 프로세스의 pid(docker-containerd-shim 아님)를 가져옵니다.

    위 예시의 경우는 다음과 같습니다.

    • 1283107 - pause
    • 1283169 - sh
    • 1283185 - sleep
    • 1283263 - nginx master
    • 1283282 - nginx worker
    # nsenter -t 1283107 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # nsenter -t 1283169 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    

    또한 ip netns를 사용하여 netstat을 실행할 수도 있지만 Docker가 연결을 수행하고 있지 않으므로 프로세스의 네트워크 네임스페이스를 수동으로 연결해야 합니다.

    # ln -s /proc/1283169/ns/net /var/run/netns/1283169
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns list
    1283169 (id: 2)
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns exec 1283169 netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # rm /var/run/netns/1283169
    

완화 방법:

단기적인 완화 방법은 위에서 설명한 방법으로 비활성 프로세스를 식별하고 kill [PID] 명령어를 사용하여 프로세스를 종료하는 것입니다.

장기적인 완화 방법은 Docker가 충돌하는 원인을 식별하고 해결하는 것입니다. 가능한 이유는 다음과 같습니다.

  • 좀비 프로세스 누적으로 인한 PID 네임스페이스 부족
  • docker 버그
  • 리소스 압력/OOM