1 - 동작 중인 파드 디버그

이 페이지는 노드에서 동작 중인(혹은 크래시된) 파드를 디버그하는 방법에 대해 설명한다.

시작하기 전에

  • 여러분의 파드는 이미 스케줄링 되어 동작하고 있을 것이다. 만약 파드가 아직 동작중이지 않다면, 애플리케이션 트러블슈팅을 참고한다.
  • 일부 고급 디버깅 과정에서는 해당 파드가 어떤 노드에서 동작하고 있는지 알아야 하고, 해당 노드에서 쉘 명령어를 실행시킬 수 있어야 한다. kubectl을 사용하는 일반적인 디버깅 과정에서는 이러한 접근 권한이 필요하지 않다.

파드의 로그 확인하기

먼저, 확인하고자 하는 컨테이너의 로그를 확인한다.

kubectl logs ${POD_NAME} ${CONTAINER_NAME}

만약 컨테이너가 이전에 크래시 되었다면, 다음의 명령을 통해 컨테이너의 크래시 로그를 살펴볼 수 있다.

kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}

exec를 통해 컨테이너 디버깅하기

만약 컨테이너 이미지에 디버깅 도구가 포함되어 있다면, kubectl exec을 통해 특정 컨테이너에서 해당 명령들을 실행할 수 있다. (리눅스나 윈도우 OS를 기반으로 만들어진 이미지에는 대부분 디버깅 도구를 포함하고 있다.)

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

예를 들어, 동작 중인 카산드라 파드의 로그를 살펴보기 위해서는 다음과 같은 명령을 실행할 수 있다.

kubectl exec cassandra -- cat /var/log/cassandra/system.log

kubectl exec-i-t 옵션을 사용해서 터미널에서 접근할 수 있는 쉘을 실행시킬 수도 있다. 예를 들면 다음과 같다.

kubectl exec -it cassandra -- sh

더욱 상세한 내용은 다음 동작중인 컨테이너의 쉘에 접근하기를 참고하라.

임시(ephemeral) 디버그 컨테이너를 사용해서 디버깅하기

FEATURE STATE: Kubernetes v1.23 [beta]

컨테이너가 크래시 됐거나 distroless 이미지처럼 컨테이너 이미지에 디버깅 도구를 포함하고 있지 않아 kubectl exec로는 충분하지 않은 경우에는 임시(Ephemeral) 컨테이너를 사용하는 것이 인터랙티브한 트러블슈팅에 유용하다.

임시 컨테이너를 사용한 디버깅 예시

kubectl debug 명령어를 사용해서 동작 중인 파드에 임시 컨테이너를 추가할 수 있다. 먼저, 다음과 같이 파드를 추가한다.

kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never

이 섹션의 예시에서는 디버깅 도구가 포함되지 않은 이미지의 사례를 보여드리기 위해 pause 컨테이너 이미지를 사용했는데, 이 대신 어떠한 이미지를 사용해도 될 것이다.

만약 kubectl exec을 통해 쉘을 생성하려 한다면 다음과 같은 에러를 확인할 수 있을 텐데, 그 이유는 이 이미지에 쉘이 존재하지 않기 때문이다.

kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown

이 명령어 대신 kubectl debug을 사용해서 디버깅 컨테이너를 생성할 수 있다. 만약 -i/--interactive 인자를 사용한다면, kubectl은 임시 컨테이너의 콘솔에 자동으로 연결할 것이다.

kubectl debug -it ephemeral-demo --image=busybox --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #

이 명령어는 새로운 busybox 컨테이너를 추가하고 해당 컨테이너로 연결한다. --target 파라미터를 사용하면 다른 컨테이너의 프로세스 네임스페이스를 대상으로 하게 된다. 여기서는 이 옵션이 꼭 필요한데, kubectl run이 생성하는 파드에 대해 프로세스 네임스페이스 공유를 활성화하지 않기 때문이다.

kubectl describe 명령을 사용하면 새롭게 생성된 임시 컨테이너의 상태를 확인할 수 있다.

kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
  debugger-8xzrl:
    Container ID:   docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
    Image:          busybox
    Image ID:       docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 12 Feb 2020 14:25:42 +0100
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

디버깅이 다 끝나면 kubectl delete을 통해 파드를 제거할 수 있다.

kubectl delete pod ephemeral-demo

파드의 복제본을 이용해서 디버깅하기

때때로 파드의 설정 옵션에 따라 특정 상황에서 트러블슈팅을 하기가 어려울 수 있다. 예를 들어, 만일 여러분의 컨테이너 이미지가 쉘을 포함하고 있지 않거나, 여러분의 애플리케이션이 컨테이너 시작에서 크래시가 발생한다면 kubectl exec을 이용해서 컨테이너를 트러블슈팅할 수 없을 수 있다. 이러한 상황에서는 kubectl debug을 사용해서 파드의 복제본을 디버깅을 위한 추가적인 설정 옵션과 함께 생성할 수 있다.

새 컨테이너와 함께 파드의 복제본 생성하기

만일 여러분의 애플리케이션이 동작은 하고 있지만 예상과는 다르게 동작하는 경우, 파드의 복제본에 새로운 컨테이너를 추가함으로써 추가적인 트러블슈팅 도구들을 파드에 함께 추가할 수 있다.

가령, 여러분의 애플리케이션 컨테이너 이미지는 busybox를 기반으로 하고 있는데 여러분은 busybox에는 없는 디버깅 도구를 필요로 한다고 가정해 보자. 이러한 시나리오는 kubectl run 명령을 통해 시뮬레이션 해볼 수 있다.

kubectl run myapp --image=busybox --restart=Never -- sleep 1d

다음의 명령을 실행시켜 디버깅을 위한 새로운 우분투 컨테이너와 함께 myapp-debug이란 이름의 myapp 컨테이너 복제본을 생성할 수 있다.

kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

명령어를 변경하며 파드의 복제본 생성하기

때때로 컨테이너의 명령어를 변경하는 것이 유용한 경우가 있는데, 예를 들면 디버그 플래그를 추가하기 위해서나 애플리케이션이 크래시 되는 경우이다.

다음의 kubectl run 명령을 통해 즉각적으로 크래시가 발생하는 애플리케이션의 사례를 시뮬레이션해 볼 수 있다.

kubectl run --image=busybox myapp -- false

kubectl describe pod myapp 명령을 통해 이 컨테이너에 크래시가 발생하고 있음을 확인할 수 있다.

Containers:
  myapp:
    Image:         busybox
    ...
    Args:
      false
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1

이러한 경우에 kubectl debug을 통해 명령어를 지정함으로써 해당 파드의 복제본을 인터랙티브 쉘로 생성할 수 있다.

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

이제 인터랙티브 쉘에 접근할 수 있으니 파일 시스템 경로를 확인하거나 동작 중인 컨테이너의 명령어를 직접 확인하는 등의 작업이 가능하다.

사용이 모두 끝나면, 디버깅에 사용된 파드들을 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

컨테이너 이미지를 변경하며 파드의 복제본 생성하기

특정한 경우에 여러분은 제대로 동작하지 않는 파드의 이미지를 기존 프로덕션 컨테이너 이미지에서 디버깅 빌드나 추가적인 도구를 포함한 이미지로 변경하고 싶을 수 있다.

이 사례를 보여주기 위해 kubectl run 명령을 통해 파드를 생성하였다.

kubectl run myapp --image=busybox --restart=Never -- sleep 1d

여기서는 kubectl debug 명령을 통해 해당 컨테이너 이미지를 ubuntu로 변경하며 복제본을 생성하였다.

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

--set-image의 문법은 kubectl set image와 동일하게 container_name=image 형식의 문법을 사용한다. *=ubuntu라는 의미는 모든 컨테이너의 이미지를 ubuntu로 변경하겠다는 의미이다.

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod myapp myapp-debug

노드의 쉘을 사용해서 디버깅하기

만약 위의 어떠한 방법도 사용할 수 없다면, 파드가 현재 동작 중인 노드를 찾아 호스트의 네임스페이스로 동작하는 특권 파드를 생성할 수 있다. 다음 kubectl debug 명령을 통해 해당 노드에서 인터랙티브한 쉘을 생성할 수 있다.

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#

노드에서 디버깅 세션을 생성할 때 유의해야 할 점은 다음과 같다.

  • kubectl debug는 노드의 이름에 기반해 새로운 파드의 이름을 자동으로 생성한다.
  • 컨테이너는 호스트 네임스페이스(IPC, 네트워크, PID 네임스페이스)에서 동작한다.
  • 노드의 루트 파일시스템은 /host에 마운트된다.

사용이 모두 끝나면, 디버깅에 사용된 파드를 잊지 말고 정리한다.

kubectl delete pod node-debugger-mynode-pdx84

2 - 동작중인 컨테이너의 셸에 접근하기

이 페이지는 동작중인 컨테이너에 접근하기 위해 kubectl exec을 사용하는 방법에 대해 설명한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

컨테이너의 셸에 접근하기

이 예시에서는 하나의 컨테이너를 가진 파드를 생성할 것이다. 이 컨테이너는 nginx 이미지를 실행한다. 해당 파드에 대한 설정 파일은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  hostNetwork: true
  dnsPolicy: Default

파드를 생성한다.

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

다음을 통해 컨테이너가 동작하고 있는지 확인할 수 있다.

kubectl get pod shell-demo

동작중인 컨테이너의 셸에 접근한다.

kubectl exec --stdin --tty shell-demo -- /bin/bash

셸에 접근해서 다음처럼 루트 디렉토리를 확인해 볼 수 있다.

# Run this inside the container
ls /

접근한 셸에서 다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.

# You can run these example commands inside the container
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

nginx의 최상단 페이지 작성하기

앞에서 생성한 파드에 대한 설정을 살펴보아라. 파드에는 emptyDir 볼륨이 사용되었고, 이 컨테이너는 해당 볼륨을 /usr/share/nginx/html 경로에 마운트하였다.

접근한 셸 환경에서 /usr/share/nginx/html 디렉터리에 index.html 파일을 생성해 보아라.

# Run this inside the container
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

셸 환경에서 nginx 서버에 GET 요청을 시도해보면 다음과 같다.

# Run this in the shell inside your container
apt-get update
apt-get install curl
curl http://localhost/

출력 결과는 여러분이 index.html 파일에 작성한 텍스트를 출력할 것이다.

Hello shell demo

셸 사용이 모두 끝났다면 exit을 입력해 종료하라.

exit # To quit the shell in the container

컨테이너에서 개별 명령어 실행하기

셸이 아닌 일반적인 커맨드 환경에서 다음처럼 동작중인 컨테이너의 환경 변수를 출력할 수 있다.

kubectl exec shell-demo env

다른 명령어도 한번 실행해 보아라. 다음은 실행해 볼 명령의 예시이다.

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

파드에 한 개 이상의 컨테이너가 있을 경우 셸에 접근하기

만일 파드에 한 개 이상의 컨테이너가 있을 경우, kubectl exec 명령어에 --container 혹은 -c 옵션을 사용해서 컨테이너를 지정하라. 예를 들어, 여러분이 my-pod라는 이름의 파드가 있다고 가정해 보자. 이 파드에는 main-apphelper-app 이라는 이름의 두 컨테이너가 있다. 다음 명령어는 main-app 컨테이너에 대한 셸에 접근할 것이다.

kubectl exec -i -t my-pod --container main-app -- /bin/bash

다음 내용

3 - 리소스 메트릭 파이프라인

컨테이너 CPU 및 메모리 사용량과 같은 리소스 사용량 메트릭은 쿠버네티스의 메트릭 API를 통해 사용할 수 있다. 이 메트릭은 kubectl top 커맨드 사용하여 사용자가 직접적으로 액세스하거나, Horizontal Pod Autoscaler 같은 클러스터의 컨트롤러에서 결정을 내릴 때 사용될 수 있다.

메트릭 API

메트릭 API를 통해, 주어진 노드나 파드에서 현재 사용중인 리소스의 양을 알 수 있다. 이 API는 메트릭 값을 저장하지 않으므로, 예를 들어, 지정된 노드에서 10분 전에 사용된 리소스의 양을 가져오는 것과 같은 일을 할 수는 없다.

이 API와 다른 API는 차이가 없다.

  • 다른 쿠버네티스 API의 엔드포인트와 같이 /apis/metrics.k8s.io/ 하위 경로에서 발견될 수 있다
  • 동일한 보안, 확장성 및 신뢰성 보장을 제공한다

k8s.io/metrics 리포지터리에서 이 API를 정의하고 있다. 여기에서 이 API에 대한 더 상세한 정보를 찾을 수 있다.

리소스 사용량 측정

CPU

CPU는 일정 기간 동안 CPU 코어에서 평균 사용량으로 리포트된다. 이 값은 커널(리눅스와 윈도우 커널 모두)에서 제공하는 누적 CPU 카운터보다 높은 비율을 적용해서 얻는다. kubelet은 비율 계산에 사용할 윈도우를 선택한다.

메모리

메모리는 메트릭이 수집된 순간 작업 집합으로 리포트 된다. 이상적인 환경에서 "작업 집합(working set)"은 압박(memory pressure)에서 풀려날 수 없는 사용 중인(in-use) 메모리의 양이다. 그러나 작업 집합의 계산은 호스트 OS에 따라 다르며, 일반적으로 휴리스틱스를 사용해서 평가한다. 쿠버네티스는 스왑(swap)을 지원하지 않기 때문에 모든 익명(파일로 백업되지 않은) 메모리를 포함한다. 호스트 OS가 항상 이러한 페이지를 회수할 수 없기 때문에 메트릭에는 일반적으로 일부 캐시된(파일 백업) 메모리도 포함된다.

메트릭 서버

메트릭 서버는 클러스터 전역에서 리소스 사용량 데이터를 집계한다. kube-up.sh 스크립트에 의해 생성된 클러스터에는 기본적으로 메트릭 서버가 디플로이먼트 오브젝트로 배포된다. 만약 다른 쿠버네티스 설치 메커니즘을 사용한다면, 제공된 디플로이먼트 components.yaml 파일을 사용하여 메트릭 서버를 배포할 수 있다.

메트릭 서버는 각 노드에서 Kubelet에 의해 노출된 Summary API에서 메트릭을 수집하고, 쿠버네티스 aggregator를 통해 메인 API 서버에 등록된다.

설계 문서에서 메트릭 서버에 대해 자세하게 배울 수 있다.

요약(Summary) API 소스

Kubelet은 노드, 볼륨, 파드, 컨테이너 수준의 통계를 수집하며, 소비자(consumer)가 읽을 수 있도록 이 통계를 요약 API에 기록한다.

1.23 이전에는 이러한 자원들은 기본적으로 cAdvisor에 의해 수집되었다. 그러나, 1.23에서 PodAndContainerStatsFromCRI 기능 게이트가 추가되면서, 컨테이너 및 파드 수준 통계를 CRI 구현에서 수집할 수 있게 되었다. 참고: 이를 위해서는 CRI 구현에서도 이 기능을 지원해야 한다(containerd >= 1.6.0, CRI-O >= 1.23.0).

4 - 리소스 모니터링 도구

애플리케이션을 스케일하여 신뢰할 수 있는 서비스를 제공하려면, 애플리케이션이 배포되었을 때 애플리케이션이 어떻게 동작하는지를 이해해야 한다. 컨테이너, 파드, 서비스, 그리고 전체 클러스터의 특성을 검사하여 쿠버네티스 클러스터 내의 애플리케이션 성능을 검사할 수 있다. 쿠버네티스는 각 레벨에서 애플리케이션의 리소스 사용량에 대한 상세 정보를 제공한다. 이 정보는 애플리케이션의 성능을 평가하고 병목 현상을 제거하여 전체 성능을 향상할 수 있게 해준다.

쿠버네티스에서 애플리케이션 모니터링은 단일 모니터링 솔루션에 의존하지 않는다. 신규 클러스터에서는, 리소스 메트릭 또는 완전한 메트릭 파이프라인으로 모니터링 통계를 수집할 수 있다.

리소스 메트릭 파이프라인

리소스 메트릭 파이프라인은 Horizontal Pod Autoscaler 컨트롤러와 같은 클러스터 구성요소나 kubectl top 유틸리티에 관련되어 있는 메트릭들로 제한된 집합을 제공한다. 이 메트릭은 경량의 단기 인메모리 저장소인 metrics-server에 의해서 수집되며 metrics.k8s.io API를 통해 노출된다.

metrics-server는 클러스터 상의 모든 노드를 발견하고 각 노드의 Kubelet에 CPU와 메모리 사용량을 질의한다. Kubelet은 쿠버네티스 마스터와 노드 간의 다리 역할을 해서 머신에서 구동되는 파드와 컨테이너를 관리한다. Kubelet은 각각의 파드를 해당하는 컨테이너로 변환하고 컨테이너 런타임 인터페이스를 통해서 컨테이너 런타임에서 개별 컨테이너의 사용량 통계를 가져온다. Kubelet은 이 정보를 레거시 도커와의 통합을 위해 kubelet에 통합된 cAdvisor를 통해 가져온다. 그 다음으로 취합된 파드 리소스 사용량 통계를 metric-server 리소스 메트릭 API를 통해 노출한다. 이 API는 kubelet의 인증이 필요한 읽기 전용 포트 상의 /metrics/resource/v1beta1에서 제공된다.

완전한 메트릭 파이프라인

완전한 메트릭 파이프라인은 보다 풍부한 메트릭에 접근할 수 있도록 해준다. 쿠버네티스는 Horizontal Pod Autoscaler와 같은 메커니즘을 활용해서 이런 메트릭에 대한 반응으로 클러스터의 현재 상태를 기반으로 자동으로 스케일링하거나 클러스터를 조정할 수 있다. 모니터링 파이프라인은 kubelet에서 메트릭을 가져와서 쿠버네티스에 custom.metrics.k8s.ioexternal.metrics.k8s.io API를 구현한 어댑터를 통해 노출한다.

CNCF 프로젝트인, 프로메테우스는 기본적으로 쿠버네티스, 노드, 프로메테우스 자체를 모니터링할 수 있다. CNCF 프로젝트가 아닌 완전한 메트릭 파이프라인 프로젝트는 쿠버네티스 문서의 범위가 아니다.

5 - 스테이트풀셋 디버깅하기

이 문서에서는 스테이트풀셋을 디버깅 방법에 대해 설명한다.

시작하기 전에

  • 쿠버네티스 클러스터가 준비되어 있어야 하고, kubectl 커맨드 라인 도구가 클러스터와 통신할 수 있게 사전에 설정되어 있어야 한다.
  • 조사하고자 하는 스테이트풀셋이 사전에 준비되어 있어야 한다.

스테이트풀셋 디버깅하기

레이블이 app=myapp으로 지정된 스테이트풀셋 파드를 전부 나열하기 위해서는 다음의 명령을 사용할 수 있다.

kubectl get pods -l app=myapp

만약 오랜 시간동안 Unknown이나 Terminating 상태에 있는 파드들을 발견하였다면, 이러한 파드들을 어떻게 다루는지 알아보기 위해 스테이트풀셋 파드 삭제하기를 참고하길 바란다. 스테이트풀셋에 포함된 개별 파드들을 디버깅하기 위해서는 파드 디버그하기 가이드를 참고하길 바란다.

다음 내용

초기화 컨테이너(Init container) 디버그하기를 참고길 바란다.

6 - 초기화 컨테이너(Init Containers) 디버그하기

이 페이지는 초기화 컨테이너의 실행과 관련된 문제를 조사하는 방법에 대해 보여준다. 아래 예제의 커맨드 라인은 파드(Pod)를 <pod-name> 으로, 초기화 컨테이너를 <init-container-1><init-container-2> 로 표시한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

초기화 컨테이너의 상태 체크하기

사용자 파드의 상태를 표시한다.

kubectl get pod <pod-name>

예를 들어, Init:1/2 상태는 두 개의 초기화 컨테이너 중 하나가 성공적으로 완료되었음을 나타낸다.

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

상태값과 그 의미에 대한 추가 예제는 파드 상태 이해하기를 참조한다.

초기화 컨테이너에 대한 상세 정보 조회하기

초기화 컨테이너의 실행에 대한 상세 정보를 확인한다.

kubectl describe pod <pod-name>

예를 들어, 2개의 초기화 컨테이너가 있는 파드는 다음과 같이 표시될 수 있다.

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

파드 스펙의 status.initContainerStatuses 필드를 읽어서 프로그래밍 방식으로 초기화 컨테이너의 상태를 조회할 수도 있다.

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

이 명령은 원시 JSON 방식으로 위와 동일한 정보를 반환한다.

초기화 컨테이너의 로그 조회하기

초기화 컨테이너의 로그를 확인하기 위해 파드의 이름과 초기화 컨테이너의 이름을 같이 전달한다.

kubectl logs <pod-name> -c <init-container-2>

셸 스크립트를 실행하는 초기화 컨테이너는, 초기화 컨테이너가 실행될 때 명령어를 출력한다. 예를 들어, 스크립트의 시작 부분에 set -x 를 추가하고 실행하여 Bash에서 명령어를 출력할 수 있도록 수행할 수 있다.

파드의 상태 이해하기

Init: 으로 시작하는 파드 상태는 초기화 컨테이너의 실행 상태를 요약한다. 아래 표는 초기화 컨테이너를 디버깅하는 동안 사용자가 확인할 수 있는 몇 가지 상태값의 예이다.

상태 의미
Init:N/M 파드가 M 개의 초기화 컨테이너를 갖고 있으며, 현재까지 N 개가 완료.
Init:Error 초기화 컨테이너 실행 실패.
Init:CrashLoopBackOff 초기화 컨테이너가 반복적으로 실행 실패.
Pending 파드가 아직 초기화 컨테이너를 실행하지 않음.
PodInitializing or Running 파드가 이미 초기화 컨테이너 실행을 완료.

7 - 클러스터 트러블슈팅

이 문서는 클러스터 트러블슈팅에 대해 설명한다. 사용자가 겪고 있는 문제의 근본 원인으로서 사용자의 애플리케이션을 이미 배제했다고 가정한다. 애플리케이션 디버깅에 대한 팁은 애플리케이션 트러블슈팅 가이드를 참조한다. 자세한 내용은 트러블슈팅 문서를 참조한다.

클러스터 나열하기

클러스터에서 가장 먼저 디버그해야 할 것은 노드가 모두 올바르게 등록되었는지 여부이다.

다음을 실행한다.

kubectl get nodes

그리고 보일 것으로 예상되는 모든 노드가 존재하고 모두 Ready 상태인지 확인한다.

클러스터의 전반적인 상태에 대한 자세한 정보를 얻으려면 다음을 실행할 수 있다.

kubectl cluster-info dump

로그 보기

현재로서는 클러스터를 더 깊이 파고들려면 관련 머신에서 로그 확인이 필요하다. 관련 로그 파일 위치는 다음과 같다. (systemd 기반 시스템에서는 journalctl을 대신 사용해야 할 수도 있다.)

마스터

  • /var/log/kube-apiserver.log - API 서버, API 제공을 담당
  • /var/log/kube-scheduler.log - 스케줄러, 스케줄 결정을 담당
  • /var/log/kube-controller-manager.log - 레플리케이션 컨트롤러를 담당하는 컨트롤러

워커 노드

  • /var/log/kubelet.log - Kubelet, 노드에서 컨테이너 실행을 담당
  • /var/log/kube-proxy.log - Kube Proxy, 서비스 로드밸런싱을 담당

클러스터 장애 모드의 일반적인 개요

아래에 일부 오류 상황 예시 및 문제를 완화하기 위해 클러스터 설정을 조정하는 방법을 나열한다.

근본 원인

  • VM(들) 종료
  • 클러스터 내 또는 클러스터와 사용자 간의 네트워크 분할
  • 쿠버네티스 소프트웨어의 충돌
  • 데이터 손실 또는 퍼시스턴트 스토리지 사용 불가 (e.g. GCE PD 또는 AWS EBS 볼륨)
  • 운영자 오류, 예를 들면 잘못 구성된 쿠버네티스 소프트웨어 또는 애플리케이션 소프트웨어

특정 시나리오

  • API 서버 VM 종료 또는 API 서버 충돌
    • 다음의 현상을 유발함
      • 새로운 파드, 서비스, 레플리케이션 컨트롤러를 중지, 업데이트 또는 시작할 수 없다.
      • 쿠버네티스 API에 의존하지 않는 기존 파드 및 서비스는 계속 정상적으로 작동할 것이다.
  • API 서버 백업 스토리지 손실
    • 다음의 현상을 유발함
      • API 서버가 구동되지 않을 것이다.
      • kubelet에 도달할 수 없게 되지만, kubelet이 여전히 동일한 파드를 계속 실행하고 동일한 서비스 프록시를 제공할 것이다.
      • API 서버를 재시작하기 전에, 수동으로 복구하거나 API서버 상태를 재생성해야 한다.
  • 지원 서비스 (노드 컨트롤러, 레플리케이션 컨트롤러 매니저, 스케쥴러 등) VM 종료 또는 충돌
    • 현재 그것들은 API 서버와 같은 위치에 있기 때문에 API 서버와 비슷한 상황을 겪을 것이다.
    • 미래에는 이들도 복제본을 가질 것이며 API서버와 별도로 배치될 수도 있다.
    • 지원 서비스들은 상태(persistent state)를 자체적으로 유지하지는 않는다.
  • 개별 노드 (VM 또는 물리적 머신) 종료
    • 다음의 현상을 유발함
      • 해당 노드의 파드가 실행을 중지
  • 네트워크 분할
    • 다음의 현상을 유발함
      • 파티션 A는 파티션 B의 노드가 다운되었다고 생각한다. 파티션 B는 API 서버가 다운되었다고 생각한다. (마스터 VM이 파티션 A에 있다고 가정)
  • Kubelet 소프트웨어 오류
    • 다음의 현상을 유발함
      • 충돌한 kubelet은 노드에서 새 파드를 시작할 수 없다.
      • kubelet이 파드를 삭제할 수도 있고 삭제하지 않을 수도 있다.
      • 노드는 비정상으로 표시된다.
      • 레플리케이션 컨트롤러는 다른 곳에서 새 파드를 시작한다.
  • 클러스터 운영자 오류
    • 다음의 현상을 유발함
      • 파드, 서비스 등의 손실
      • API 서버 백업 저장소 분실
      • API를 읽을 수 없는 사용자
      • 기타

완화

  • 조치: IaaS VM을 위한 IaaS 공급자의 자동 VM 다시 시작 기능을 사용한다.

    • 다음을 완화할 수 있음: API 서버 VM 종료 또는 API 서버 충돌
    • 다음을 완화할 수 있음: 지원 서비스 VM 종료 또는 충돌
  • 조치: API 서버+etcd가 있는 VM에 IaaS 제공자의 안정적인 스토리지(예: GCE PD 또는 AWS EBS 볼륨)를 사용한다.

    • 다음을 완화할 수 있음: API 서버 백업 스토리지 손실
  • 조치: 고가용성 구성을 사용한다.

    • 다음을 완화할 수 있음: 컨트롤 플레인 노드 종료 또는 컨트롤 플레인 구성 요소(스케줄러, API 서버, 컨트롤러 매니저) 충돌
      • 동시에 발생하는 하나 이상의 노드 또는 구성 요소 오류를 허용한다.
    • 다음을 완화할 수 있음: API 서버 백업 스토리지(i.e., etcd의 데이터 디렉터리) 손실
      • 고가용성 etcd 구성을 사용하고 있다고 가정
  • 조치: API 서버 PD/EBS 볼륨의 주기적인 스냅샷

    • 다음을 완화할 수 있음: API 서버 백업 스토리지 손실
    • 다음을 완화할 수 있음: 일부 운영자 오류 사례
    • 다음을 완화할 수 있음: 일부 쿠버네티스 소프트웨어 오류 사례
  • 조치: 파드 앞에 레플리케이션 컨트롤러와 서비스 사용

    • 다음을 완화할 수 있음: 노드 종료
    • 다음을 완화할 수 있음: Kubelet 소프트웨어 오류
  • 조치: 예기치 않은 재시작을 허용하도록 설계된 애플리케이션(컨테이너)

    • 다음을 완화할 수 있음: 노드 종료
    • 다음을 완화할 수 있음: Kubelet 소프트웨어 오류

8 - 트러블슈팅하기

때때로 문제가 발생할 수 있다. 이 가이드는 이러한 상황을 해결하기 위해 작성되었다. 문제 해결에는 다음 두 가지를 참고해 볼 수 있다.

여러분이 현재 사용중인 릴리스에 대한 알려진 이슈들을 다음의 릴리스 페이지에서 확인해 볼 수도 있다.

도움 받기

여러분의 문제가 위에 소개된 어떠한 가이드로도 해결할 수 없다면, 쿠버네티스 커뮤니티로부터 도움을 받을 수 있는 다양한 방법들을 시도해 볼 수 있다.

질문

이 사이트의 문서들은 다양한 질문들에 대한 답변을 제공할 수 있도록 구성되어 있다. 개념은 쿠버네티스의 아키텍처와 각 컴포넌트들이 어떻게 동작하는지에 대해 설명하고, 시작하기는 쿠버네티스를 시작하는 데 유용한 지침들을 제공한다. 태스크는 흔히 사용되는 작업들을 수행하는 방법에 대해 소개하고, 튜토리얼은 실무, 산업 특화 혹은 종단간 개발에 특화된 시나리오를 통해 차근차근 설명한다. 레퍼런스 섹션에서는 쿠버네티스 APIkubectl과 같은 커맨드 라인 인터페이스(CLI)에 대한 상세한 설명을 다룬다.

도와주세요! 내 질문이 다뤄지지 않았어요! 도움이 필요해요!

스택 오버플로우

여러분들이 겪고 있는 문제와 동일한 문제에 대한 도움을 위해 커뮤니티의 다른 사람들이 이미 질문을 올렸을 수 있다. 쿠버네티스 팀은 쿠버네티스 태그가 등록된 글들을 모니터링하고 있다. 발생한 문제와 도움이 되는 질문이 없다면, 새로운 질문을 올려라!

슬랙

쿠버네티스 슬랙의 #kubernetes-users 채널을 통해 쿠버네티스 커뮤니티의 여러 사람들을 접할 수도 있다. 쿠버네티스 슬랙을 사용하기 위해서는 등록이 필요한데, 다음을 통해 채널 초대 요청을 할 수 있다. (누구나 가입할 수 있다). 슬랙 채널은 여러분이 어떠한 질문을 할 수 있도록 언제나 열려있다. 가입하고 나면 여러분의 웹 브라우저나 슬랙 앱을 통해 쿠버네티스 슬랙 에 참여할 수 있다.

쿠버네티스 슬랙에 참여하게 된다면, 다양한 주제의 흥미와 관련된 여러 채널들에 대해 살펴본다. 가령, 쿠버네티스를 처음 접하는 사람이라면 #kubernetes-novice 채널에 가입할 수 있다. 혹은, 만약 당신이 개발자라면 #kubernetes-dev 채널에 가입할 수 있다.

또한 각 국가 및 사용 언어별 채널들이 여럿 존재한다. 사용하는 언어로 도움을 받거나 정보를 얻기 위해서는 다음의 채널에 참가한다.

국가 / 언어별 슬랙 채널
국가 채널
China(중국) #cn-users, #cn-events
Finland(핀란드) #fi-users
France(프랑스) #fr-users, #fr-events
Germany(독일) #de-users, #de-events
India(인도) #in-users, #in-events
Italy(이탈리아) #it-users, #it-events
Japan(일본) #jp-users, #jp-events
Korea(한국) #kr-users
Netherlands(네덜란드) #nl-users
Norway(노르웨이) #norw-users
Poland(폴란드) #pl-users
Russia(러시아) #ru-users
Spain(스페인) #es-users
Sweden(스웨덴) #se-users
Turkey(터키) #tr-users, #tr-events

포럼

공식 쿠버네티스 포럼에 참여하는 것도 추천되는 방법이다. discuss.kubernetes.io.

버그와 기능 추가 요청

만약 여러분이 버그처럼 보이는 것을 발견했거나, 기능 추가 요청을 하기 위해서는 GitHub 이슈 트래킹 시스템을 사용한다.

이슈를 작성하기 전에는, 여러분의 이슈가 기존 이슈에서 이미 다뤄졌는지 검색해 본다.

버그를 보고하는 경우에는, 해당 문제를 어떻게 재현할 수 있는지에 관련된 상세한 정보를 포함한다. 포함되어야 하는 정보들은 다음과 같다.

  • 쿠버네티스 버전: kubectl version
  • 클라우드 프로바이더, OS 배포판, 네트워크 구성, 및 도커 버전
  • 문제를 재현하기 위한 절차

9 - 파드 실패의 원인 검증하기

이 페이지는 컨테이너 종료 메시지를 읽고 쓰는 방법을 보여준다.

종료 메시지는 컨테이너가 치명적인 이벤트에 대한 정보를, 대시보드나 모니터링 소프트웨어 도구와 같이 쉽게 조회 및 표시할 수 있는 위치에 기록하는 방법을 제공한다. 대부분의 경우에 종료 메시지에 넣는 정보는 일반 쿠버네티스 로그에도 쓰여져야 한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

종료 메시지 읽기 및 쓰기

이 예제에서는, 하나의 컨테이너를 실행하는 파드를 생성한다. 하단의 설정 파일은 컨테이너가 시작될 때 수행하는 명령어를 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. 다음의 YAML 설정 파일에 기반한 파드를 생성한다.

     kubectl apply -f https://k8s.io/examples/debug/termination.yaml
    
    YAML 파일에 있는 `command` 와 `args` 필드에서 컨테이너가 10초 간 잠든 뒤에
    "Sleep expired" 문자열을 `/dev/termination-log` 파일에 기록하는
    것을 확인할 수 있다. 컨테이너는 "Sleep expired" 메시지를
    기록한 후에 종료된다.
    
  2. 파드와 관련된 정보를 출력한다.

     kubectl get pod termination-demo
    
    파드가 더 이상 실행되지 않을 때까지 앞선 명령어를 반복한다.
    
  3. 파드에 관한 상세 정보를 출력한다.

     kubectl get pod termination-demo --output=yaml
    
    결과는 "Sleep expired" 메시지를 포함한다.
    
     apiVersion: v1
     kind: Pod
     ...
         lastState:
           terminated:
             containerID: ...
             exitCode: 0
             finishedAt: ...
             message: |
               Sleep expired
             ...
    
  4. 종료 메시지만을 포함하는 출력 결과를 보기 위해서는 Go 템플릿을 사용한다.

     kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

종료 메시지 사용자 정의하기

쿠버네티스는 컨테이너의 terminationMessagePath 필드에 지정된 종료 메시지 파일에서 종료 메시지를 검색하며, 이 필드의 기본값은 /dev/termination-log 이다. 이 필드를 사용자 정의 함으로써 쿠버네티스가 종료 메시지를 검색할 때 다른 파일을 사용하도록 조정할 수 있다. 쿠버네티스는 지정된 파일의 내용을 사용하여 컨테이너의 성공 및 실패에 대한 상태 메시지를 채운다.

종료 메시지는 assertion failure 메세지처럼 간결한 최종 상태로 생성된다. kubelet은 4096 바이트보다 긴 메시지를 자른다. 모든 컨테이너의 총 메시지 길이는 12KiB로 제한된다. 기본 종료 메시지 경로는 /dev/termination-log이다. 파드가 시작된 후에는 종료 메시지 경로를 설정할 수 없다.

다음의 예제에서 컨테이너는, 쿠버네티스가 조회할 수 있도록 /tmp/my-log 파일에 종료 메시지를 기록한다.

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

또한 사용자는 추가적인 사용자 정의를 위해 컨테이너의 terminationMessagePolicy 필드를 설정할 수 있다. 이 필드의 기본 값은 File 이며, 이는 오직 종료 메시지 파일에서만 종료 메시지가 조회되는 것을 의미한다. terminationMessagePolicy 필드의 값을 "FallbackToLogsOnError 으로 설정함으로써, 종료 메시지 파일이 비어 있고 컨테이너가 오류와 함께 종료 되었을 경우 쿠버네티스가 컨테이너 로그 출력의 마지막 청크를 사용하도록 지시할 수 있다. 로그 출력은 2048 바이트나 80 행 중 더 작은 값으로 제한된다.

다음 내용

10 - 파드와 레플리케이션컨트롤러(ReplicationController) 디버그하기

이 페이지에서는 파드와 레플리케이션컨트롤러를 디버깅하는 방법을 소개한다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

파드 디버깅

파드 디버깅의 첫 번째 단계는 파드를 살펴 보는 것이다. 다음의 명령어를 사용하여 파드의 현재 상태와 최근 이벤트를 점검한다.

kubectl describe pods ${POD_NAME}

파드 내부 컨테이너의 상태를 확인한다. 모두 Running 상태인가? 최근에 재시작 되었는가?

파드의 상태에 따라 디버깅을 계속한다.

파드가 pending 상태로 유지

파드가 Pending 상태로 멈춰 있는 경우는, 노드에 스케줄 될 수 없음을 의미한다. 일반적으로 이것은 어떤 유형의 리소스가 부족하거나 스케줄링을 방해하는 다른 요인 때문이다. 상단의 kubectl describe ... 명령의 결과를 확인하자. 파드를 스케줄 할 수 없는 이유에 대한 스케줄러의 메세지가 있어야 한다. 이유는 다음과 같다.

부족한 리소스

사용자 클러스터의 CPU 나 Memory의 공급이 소진되었을 수 있다. 이 경우 몇 가지 방법을 시도할 수 있다.

  • 클러스터에 노드를 더 추가하기.

  • pending 상태인 파드를 위한 공간을 확보하기 위해 불필요한 파드 종료하기

  • 파드가 노드보다 크지 않은지 확인한다. 예를 들어 모든 노드가 cpu:1 의 용량을 가지고 있을 경우, cpu: 1.1 을 요청하는 파드는 절대 스케줄 될 수 없다.

    사용자는 kubectl get nodes -o <format> 명령으로 노드의 용량을 점검할 수 있다. 다음은 필요한 정보를 추출하는 몇 가지 명령의 예이다.

    kubectl get nodes -o yaml | egrep '\sname:|cpu:|memory:'
    kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cap: .status.capacity}'
    

    리소스 쿼터 기능은 사용할 수 있는 전체 리소스의 양을 제한하도록 설정할 수 있다. 네임스페이스와 함께 사용하면, 한 팀이 모든 리소스를 점유하는 것을 방지할 수 있다.

hostPort 사용하기

파드를 hostPort 에 바인딩 할 때 파드를 스케줄링 할 수 있는 위치는 제한되어 있다. 대부분의 경우 hostPort 는 불필요하다. 서비스 오브젝트를 사용하여 파드를 노출하도록 한다. hostPort 가 필요한 경우 컨테이너 클러스터에 있는 노드의 수만큼 파드를 스케줄 할 수 있다.

파드가 waiting 상태로 유지

파드가 Waiting 상태에서 멈춘 경우, 워커 노드에 스케줄 되었지만, 해당 장비에서 사용할 수 없다. 거듭 강조하지만, kubectl describe ... 의 정보는 유익하게 사용되어야 한다. Waiting 파드의 가장 일반적인 원인은 이미지를 가져오지 못하는 경우이다. 확인해야 할 3가지 사항이 있다.

  • 이미지 이름이 올바른지 확인한다.
  • 이미지를 저장소에 푸시하였는가?
  • 이미지가 풀 될 수 있는지 보기 위해, 사용자의 장비에서 docker pull <image> 를 수동으로 실행한다.

파드가 손상(crashing)되었거나 양호하지 않을(unhealthy) 경우

일단 사용자의 파드가 스케줄 되면, 구동중인 파드 디버그하기에 기술된 메서드를 디버깅에 사용할 수 있다.

레플리케이션컨트롤러 디버깅

레플리케이션컨트롤러는 매우 간단하다. 이 오브젝트는 파드를 만들거나 만들 수 없는 경우뿐이다. 만약 파드를 만들 수 없는 경우, 위의 지침을 참조하여 파드를 디버그한다.

사용자는 kubectl describe rc ${CONTROLLER_NAME} 을 사용하여 레플리케이션 컨트롤러와 관련된 이벤트를 검사할 수도 있다.