클러스터의 연결 문제 해결


이 페이지에서는 클러스터의 연결 문제를 해결하는 방법을 보여줍니다.

GKE에서 네트워크 패킷 캡처와 관련된 연결 문제

이 섹션에서는 연결 시간 초과, 연결 거부 오류, 예기치 않은 애플리케이션 동작과 같은 증상을 비롯하여 네트워크 패킷 캡처와 관련된 연결 문제를 해결하는 방법을 설명합니다. 이러한 연결 문제는 노드 수준 또는 포드 수준에서 발생할 수 있습니다.

클러스터 네트워크의 연결 문제는 대개 다음 카테고리에 속합니다.

  • 포드에 연결할 수 없음: 네트워크 구성이 잘못되어 클러스터 안팎에서 포드에 액세스할 수 없을 수 있습니다.
  • 서비스 중단: 서비스에 중단 또는 지연이 발생할 수 있습니다.
  • 포드 간 통신 문제: 포드가 서로 효과적으로 통신하지 못할 수 있습니다.

GKE 클러스터의 연결 문제는 다음과 같은 다양한 원인으로 인해 발생할 수 있습니다.

  • 네트워크 구성 오류: 잘못된 네트워크 정책, 방화벽 규칙 또는 라우팅 테이블
  • 애플리케이션 버그: 네트워크 상호작용에 영향을 미치는 애플리케이션 코드의 오류
  • 인프라 문제: 네트워크 정체, 하드웨어 오류 또는 리소스 제한

다음 섹션에서는 문제가 있는 노드 또는 포드의 문제를 해결하는 방법을 보여줍니다.

  1. 다음 명령어를 사용하여 문제가 있는 포드가 실행 중인 노드를 식별합니다.

    kubectl get pods POD_NAME -o=wide -n NAMESPACE
    

    다음을 바꿉니다.

    • POD_NAME을 포드 이름으로 바꿉니다.
    • NAMESPACE를 Kubernetes 네임스페이스로 바꿉니다.
  2. 노드에 연결합니다.

    gcloud compute ssh NODE_NAME \
        --zone=ZONE
    

    다음을 바꿉니다.

    • NODE_NAME: 노드 이름입니다.
    • ZONE: 노드가 실행되는 영역의 이름입니다.
  3. 특정 포드를 디버깅하려면 포드와 연결된 veth 인터페이스를 확인합니다.

    ip route | grep POD_IP
    

    POD_IP를 포드 IP 주소로 바꿉니다.

  4. 도구 상자 명령어를 실행합니다.

toolbox 명령어

toolbox는 디버깅 및 문제 해결을 위해 GKE 노드 내에 컨테이너화된 환경을 제공하는 유틸리티입니다. 이 섹션에서는 toolbox 유틸리티를 설치하고 이를 사용하여 노드 문제를 해결하는 방법을 설명합니다.

  1. 노드에 연결된 동안 toolbox 도구를 시작합니다.

    toolbox
    

    이렇게 하면 toolbox 유틸리티를 지원하는 파일이 다운로드됩니다.

  2. toolbox 루트 프롬프트에서 tcpdump를 설치합니다.

    • 외부 IP 주소 또는 Cloud NAT가 있는 클러스터의 경우:

      apt update -y && apt install -y tcpdump
      
    • Cloud NAT가 없는 비공개 클러스터의 경우:

      Cloud NAT가 없는 비공개 클러스터가 있는 경우 apt를 사용하여 tcpdump를 설치할 수 없습니다. 대신 공식 저장소에서 libpcaptcpdump 출시 파일을 다운로드하고 gcloud compute scp 또는 gsutil을 사용하여 파일을 VM에 복사합니다. 그런 다음 아래 단계에 따라 라이브러리를 수동으로 설치합니다.

      cp /media/root/home/USER_NAME/tcpdump-VERSION.tar.gz  /usr/sbin/
      cp /media/root/home/USER_NAME/libpcap-VERSION.tar.gz  /usr/sbin/
      cd /usr/sbin/
      tar -xvzf tcpdump-VERSION.tar.gz
      tar -xvzf libpcap-VERSION.tar.gz
      cd libpcap-VERSION
      ./configure ; make ; make install
      cd ../tcpdump-VERSION
      ./configure ; make ; make install
      tcpdump --version
      

      다음을 바꿉니다.

      • USER_NAME: 파일이 있는 시스템의 사용자 이름입니다.
      • VERSION: tcpdumplibpcap 패키지의 특정 버전 번호입니다.
  3. 패킷 캡처를 시작합니다.

    tcpdump -i eth0 -s 100 "port PORT" \
    -w /media/root/mnt/stateful_partition/CAPTURE_FILE_NAME
    

    다음을 바꿉니다.

    • PORT: 포트 번호의 이름입니다.
    • CAPTURE_FILE_NAME: 캡처 파일의 이름입니다.
  4. 패킷 캡처를 중지하고 tcpdump를 중단합니다.

  5. exit를 입력하여 도구 상자를 닫습니다.

  6. 패킷 캡처 파일을 나열하고 크기를 확인합니다.

    ls -ltr /mnt/stateful_partition/CAPTURE_FILE_NAME
    
  7. 노드에서 패킷 캡처를 컴퓨터의 현재 작업 디렉터리로 복사합니다.

    gcloud compute scp NODE_NAME:/mnt/stateful_partition/CAPTURE_FILE_NAME \
        --zone=ZONE
    

    다음을 바꿉니다.

    • NODE_NAME: 노드 이름입니다.
    • CAPTURE_FILE_NAME: 캡처 파일의 이름입니다.
    • ZONE: 영역 이름입니다.

대체 명령어

다음 방법을 사용하여 문제가 있는 포드의 연결 문제를 해결할 수도 있습니다.

  • 포드 컨테이너에 연결된 임시 디버그 워크로드

  • kubectl exec를 사용하여 대상 포드에서 직접 셸을 실행한 다음 tcpdump 명령어를 설치하고 실행합니다.

포드 네트워크 연결 문제

네트워크 개요 설명에서 언급한 것처럼, 효율적인 문제 해결을 위해서는 네트워크 네임스페이스에서 노드의 루트 네임스페이스로 포드가 연결된 방법을 이해하는 것이 중요합니다. 아래의 설명에서는 특별히 언급하지 않는 한 클러스터가 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를 더 줄여야 합니다.

GKE MTU

포드 인터페이스로 선택된 MTU는 클러스터 노드 및 기본 VPC MTU 설정에 사용되는 컨테이너 네트워크 인터페이스(CNI)에 따라 달라집니다. 자세한 내용은 포드를 참조하세요.

포드 인터페이스 MTU 값은 1460이거나 노드의 기본 인터페이스에서 상속됩니다.

CNI MTU GKE Standard
kubenet 1460 기본값
kubenet
(GKE 버전 1.26.1 이상)
상속됨 기본값
Calico 1460

--enable-network-policy를 사용하여 사용 설정됩니다.

자세한 내용은 네트워크 정책을 사용한 포드 및 서비스 간 통신 제어를 참조하세요.

netd 상속됨 다음 중 하나를 사용하여 사용 설정됩니다.
GKE Dataplane V2 상속됨

--enable-dataplane-v2를 사용하여 사용 설정됩니다.

자세한 내용은 GKE Dataplane V2 사용을 참조하세요.

간헐적인 연결 오류

포드에 대한 연결은 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

또한 NodeLocal DNSCache를 사용하여 연결 추적 항목을 줄일 수 있습니다.

컨테이너에 대해 '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   registry.k8s.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

다음 단계

  • Kubernetes DNS 문제 진단에 대한 일반적인 정보는 DNS 변환 디버깅을 참조하세요.