문제해결

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

Kubernetes 리소스 디버깅

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

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

kubectl 명령어를 찾을 수 없음

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

sudo gcloud components update kubectl

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

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

export PATH=$PATH:/usr/local/share/google/google-cloud-sdk/bin/

마지막으로 다음 명령어를 실행하여 업데이트된 .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 명령 중 실행되지 않는 명령이 있다면 마스터가 노드와 함께 SSH 터널을 열지 못하기 때문일 수 있습니다. 다음과 같은 가능한 원인을 확인하세요.

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

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

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

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

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

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

  3. 네트워크의 방화벽 규칙이 마스터에 대한 SSH 액세스를 허용하지 않습니다.

    모든 Compute Engine 네트워크는 모든 IP 주소의 SSH 액세스를 허용하는(물론 유효한 비공개 키가 필요합니다) 'default-allow-ssh'라는 방화벽 규칙을 사용하여 만들어집니다. 또한 GKE는 특별히 클러스터의 마스터 IP에서 클러스터의 노드로의 SSH 액세스를 허용하는 gke-<cluster_name>-<random-characters>-ssh 형식의 SSH 규칙을 각 클러스터에 대해 삽입합니다. 이러한 규칙 중 아무것도 존재하지 않으면 마스터는 SSH 터널을 열 수 없습니다.

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

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

    프로젝트의 'ssh-keys' 메타데이터 항목이 32KiB의 크기 제한에 가까워지면 GKE는 SSH 터널을 열기 위해 자체 SSH 키를 추가할 수 없습니다. gcloud compute project-info describe [--project=PROJECT]를 실행하여 프로젝트의 메타데이터를 표시한 다음 ssh-key 목록의 길이를 확인할 수 있습니다.

    이를 수정하려면 더 이상 필요하지 않은 일부 SSH 키를 삭제하세요.

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

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

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

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

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

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

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

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

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

    gcloud container clusters describe cluster-name

    gcloud 명령줄 도구의 출력에서 'monitoringService'가 'monitoring.googleapis.com'으로 표시되고, Developers Console에서 Cloud Monitoring이 사용 설정되어 있어야 합니다.

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

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

    새 클러스터의 측정항목이 Stackdriver Monitoring에 표시되기까지 최대 1시간 정도 걸릴 수 있습니다.

  3. 클러스터에서 실행되는 heapster가 'kube-system' 네임스페이스에 있나요?

    클러스터에 리소스가 부족하기 때문에 이 포드가 작업 부하를 예약하지 못할 수 있습니다. kubectl get pods --namespace=kube-system을 호출하고 이름에 heapster가 포함된 포드를 찾아서 Heapster가 실행 중인지 확인합니다.

  4. 클러스터의 마스터가 노드와 통신할 수 있나요?

    Stackdriver Monitoring이 작동하려면 마스터가 노드와 통신해야 합니다. 이것은 kubectl logs [POD-NAME]을 실행하여 확인할 수 있습니다. 이 명령어가 오류를 반환하는 경우, SSH 터널이 문제의 원인일 수 있습니다. 이 섹션을 참조하세요.

Stackdriver Logging 에이전트와 관련된 문제가 있는 경우, 해당 문제해결 문서를 참조하세요.

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

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

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

gcloud auth login

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

Compute Engine 및 Kubernetes Engine 서비스 계정이 삭제되거나 편집되었습니다.

Compute Engine 또는 Kubernetes Engine API를 사용 설정하면 서비스 계정이 생성되고 프로젝트에서의 수정 권한이 주어집니다. 언제든지 권한을 수정하거나 계정을 모두 제거하거나 API 사용을 중지하면 클러스터 생성 및 모든 관리 기능이 실패합니다.

Google Kubernetes Engine 서비스 계정의 이름은 다음과 같습니다.

service-[PROJECT_NUMBER]@container-engine-robot.iam.gserviceaccount.com

여기에서 [PROJECT_NUMBER]프로젝트 번호입니다.

이 문제를 해결하려면 Kubernetes Engine API를 다시 사용 설정해야 합니다. 그러면 서비스 계정과 권한이 올바로 복원됩니다.

  1. API 및 서비스 페이지로 이동합니다.
  2. 프로젝트를 선택합니다.
  3. __API 및 서비스 사용__을 클릭합니다.
  4. GKE를 검색한 다음 검색 결과에서 API를 선택합니다.
  5. __사용__을 클릭합니다. 이전에 API를 사용 설정했다면 먼저 API를 사용 중지했다가 다시 사용 설정해야 합니다. API와 관련 서비스가 사용 설정되려면 몇 분 정도 걸릴 수 있습니다.

또는 gcloud 명령줄 도구를 사용합니다.

gcloud services enable container.googleapis.com

1.9.x 이상에서 1.8.x(및 이전) 자동 방화벽 규칙 복제

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

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

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

gcloud container clusters describe [CLUSTER_NAME] --format=get"(network)"

그런 다음 컨테이너에 사용되는 클러스터의 IPv4 CIDR을 가져옵니다.

gcloud container clusters describe [CLUSTER_NAME] --format=get"(clusterIpv4Cidr)"

마지막으로 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는 서비스 계정에게 클러스터 리소스를 관리할 권한을 부여하는 IAM 역할입니다. 서비스 계정에서 이 역할 결합을 제거하면 기본 서비스 계정이 프로젝트에서 결합 해제되어 애플리케이션 배포와 기타 클러스터 작업 수행이 불가능해질 수 있습니다.

gcloud projects get-iam-policy [PROJECT_ID]를 실행하거나 Google Cloud Platform 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

배포된 작업 부하 문제해결

작업 부하의 포드에 문제가 있으면 GKE가 오류를 반환합니다. kubectl 명령줄 도구 또는 Google Cloud Platform Console을 사용하여 포드의 상태를 확인할 수 있습니다.

kubectl

클러스터에서 실행 중인 모든 포드를 보려면 다음 명령어를 실행합니다.

kubectl get pods

출력:

NAME            READY   STATUS              RESTARTS    AGE
[POD_NAME]      0/1     CrashLoopBackOff    23          8d

특정 포드에 대한 자세한 정보를 가져오려면 다음을 실행합니다.

kubectl describe pod [POD_NAME]

콘솔

다음 단계를 수행합니다.

  1. GCP Console에서 GKE 작업 부하 대시보드로 이동합니다.

    GKE 작업 부하 대시보드로 이동

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

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

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

CrashLoopBackOff

CrashLoopBackOff는 컨테이너가 다시 시작 후 반복적으로 비정상 종료됨을 나타냅니다. 컨테이너는 여러 이유로 비정상 종료될 수 있으며, 포드의 로그를 확인하면 근본 원인을 해결하는 데 도움이 될 수 있습니다.

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

kubectl 명령줄 도구 또는 GCP Console을 사용하여 포드의 컨테이너가 비정상 종료되는 이유를 찾을 수 있습니다.

kubectl

클러스터에서 실행 중인 모든 포드를 보려면 다음 명령어를 실행합니다.

kubectl get pods

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

포드의 로그를 가져오려면 다음을 실행합니다.

kubectl logs [POD_NAME]

여기서 [POD_NAME]은 문제가 있는 포드의 이름입니다.

포드 컨테이너의 이전 인스턴스에 대한 로그가 있으면 -p 플래그를 전달하여 가져올 수도 있습니다.

콘솔

다음 단계를 수행합니다.

  1. GCP Console에서 GKE 작업 부하 대시보드로 이동합니다.

    GKE 작업 부하 대시보드로 이동

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

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

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

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

containers: [CONTAINER_NAME]: last state: exit code 필드의 kubectl describe pod [POD_NAME]의 출력에서 찾을 수 있습니다.

  • 종료 코드가 1이라면 애플리케이션의 비정상 종료 때문에 컨테이너가 비정상 종료된 것입니다.
  • 종료 코드가 0이면 앱이 실행된 시간을 확인합니다. 애플리케이션의 주 프로세스가 종료되면 컨테이너가 종료됩니다. 앱의 실행이 아주 빨리 끝나면 컨테이너가 계속 다시 시작될 수 있습니다.

실행 중인 컨테이너에 연결

포드에 셸을 엽니다.

kubectl exec -it [POD_NAME] -- /bin/bash

포드에 컨테이너가 둘 이상인 경우 -c [CONTAINER_NAME]을 추가합니다.

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

ImagePullBackOff 및 ErrImagePull

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

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

kubectl

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

kubectl describe pod [POD_NAME]

콘솔

다음 단계를 수행합니다.

  1. GCP 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]을 실행합니다.
      이 방법이 효과가 있다면 포드에서 ImagePullSecrets을 지정해야 할 수도 있습니다. 포드는 자체 네임스페이스에 있는 이미지 가져오기 보안 비밀만을 참조할 수 있으므로 이 프로세스는 네임스페이스당 한 번 수행해야 합니다.

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

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

예약할 수 없는 포드

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

리소스 부족

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

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

MatchNodeSelector

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

이를 확인하려면 spec: nodeSelector에서 포드 사양의 nodeSelector 필드에 정의된 라벨을 확인합니다.

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

kubectl get nodes --show-labels

노드에 라벨을 첨부하려면 다음을 사용하세요.

kubectl label nodes [NODE_NAME] [LABEL_KEY]=[LABEL_VALUE]

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

PodToleratesNodeTaints

PodToleratesNodeTaints는 현재 노드 taint를 허용하는 노드가 없기 때문에 포드를 어떤 노드에도 예약할 수 없음을 나타냅니다.

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

kubectl describe nodes [NODE_NAME]

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

나열된 효과가 NoSchedule이면 일치하는 내결함성이 없는 한 해당 노드에서 포드를 예약할 수 없습니다.

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

kubectl taint nodes [NODE_NAME] key:NoSchedule-

PodFitsHostPorts

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

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

최소 가용성 없음

노드에 적절한 리소스가 있지만 여전히 Does not have minimum availability 메시지가 표시되면, 포드 상태를 확인합니다. 상태가 SchedulingDisabled 또는 Cordoned 상태이면, 노드가 새 포드를 예약할 수 없습니다. 노드 상태를 확인하려면 다음 안내를 따르세요.

kubectl

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

kubectl get nodes

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

kubectl uncordon [NODE_NAME]

콘솔

다음 단계를 수행합니다.

  1. GCP Console에서 GKE 작업 부하 대시보드로 이동합니다.

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

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

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

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

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

연결 문제

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

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

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

ip address show cbr0

작동 중지되었으면, 다음과 같이 작동합니다.

sudo ip link set cbr0 up

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

arp -an

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

선택 노드의 포드에 최소 연결이 포함된 경우, 먼저 도구 상자 컨테이너에서 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가 1460 이하로 올바르게 설정되었는지 확인합니다.

ip address show cbr0

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

간헐적인 연결 오류

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

nf_conntrack: table full, dropping packet

간헐적인 문제가 conntrack 소진으로 인해 발생한다는 것을 확인할 수 있는 경우에는 클러스터 크기를 느리거나(작업 부하 수 및 노드당 플로우 수 감소), GCP 지원팀에 연락하여 conntrack 테이블 용량 증가에 대한 안내를 받습니다.

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

컨테이너 로그에 따라 애플리케이션이 바인딩하려는 포트가 이미 예약되었기 때문에 포드의 컨테이너를 시작할 수 없습니다. 컨테이너에 비정상 종료 루프가 발생 중입니다. Stackdriver 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은 오래된 컨테이너 상태를 모르기 때문에, 새 프로세스로 새 컨테이너를 시작하려고 시도합니다. 그 결과 이미 포드와 연결된 네트워크 네임스페이스에 추가될 때, 포트에 바인딩할 수 없습니다.

이 문제를 진단하기 위해서는 포드의 UUID인 .metadata.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

또한 노드에서 다음 명령의 출력을 가져옵니다.

  • docker ps -a
  • ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]

이 포드에서 실행 중인 프로세스를 확인합니다. cgroup 네임스페이스의 UUID에는 포드의 UUID가 포함되기 때문에, ps 출력에서 포드 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

이 목록에서는 docker ps에도 표시되는 컨테이너 ID를 볼 수 있습니다.

이 경우에는 다음 단계를 수행하세요.

  • docker-containerd-shim 276e173b0846e24b704d4 - 일시 중지
  • docker-containerd-shim ab4c7762f5abf40951770 - sleep을 포함한 sh(sleep-ctr)
  • docker-containerd-shim 44e76e50e5ef4156fd5d3 - nginx(echoserver-ctr)

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를 실행합니다. 포드에 대해 모든 컨테이너 프로세스의 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
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Kubernetes Engine 문서