처음 배우는 쿠버네티스 개발 전략

kubectl 사용 및 트러블 슈팅

kubectl 사용 및 트러블 슈팅


kubectl 사용과 트러블슈팅

Kubernetes에서 애플리케이션이 정상적으로 동작하지 않을 때는 문제가 발생한 단계를 먼저 찾아야 한다. 이미지를 내려받지 못한 상태와 애플리케이션이 실행된 뒤 오류를 발생시키는 상태는 확인해야 할 정보가 다르다.

Pod가 실행 중인데 요청이 실패한다면 Service, DNS, Port 설정을 살펴봐야 한다. Pod가 재시작되고 있다면 종료 사유와 이전 Container의 로그가 중요하다. Pod가 Node에 배치되지 않았다면 애플리케이션 로그보다 스케줄링 Event를 먼저 확인해야 한다.

kubectl은 이러한 상태를 조회하고 Kubernetes 객체를 관리하는 기본 도구다. 명령어를 많이 외우는 것보다 현재 상태를 확인하고, 원인을 좁히고, 수정 결과를 검증하는 순서를 익히는 편이 실제 장애 대응에 도움이 된다.

flowchart TD
    A["문제 발생"] --> B["Context와 Namespace 확인"]
    B --> C["Pod 상태 조회"]
    C --> D["Describe와 Event 확인"]
    D --> E["현재 로그와 이전 로그 확인"]
    E --> F["필요하면 Container 내부 점검"]
    F --> G["이미지, 설정, 자원, 네트워크 원인 확인"]
    G --> H["원본 Manifest 또는 코드 수정"]
    H --> I["재배포 후 상태와 요청 검증"]

kubectl과 클러스터 연결

kubectl은 API Server에 요청한다

kubectl은 로컬 컴퓨터에서 실행하는 명령줄 도구다. 사용자가 입력한 명령을 Kubernetes API Server에 전달하고 결과를 출력한다.

flowchart LR
    A["kubectl"] --> B["kubeconfig"]
    B --> C["선택한 Context"]
    C --> D["Kubernetes API Server"]
    D --> E["인증과 권한 확인"]
    E --> F["객체 조회 또는 변경"]

예를 들어 kubectl get pods는 API Server에 Pod 목록을 요청한다. kubectl apply는 객체의 원하는 설정을 전달한다. 애플리케이션이 실제로 실행되도록 만드는 과정은 이후 Controller, Scheduler, kubelet 등이 수행한다.

따라서 apply가 성공했다는 사실과 애플리케이션이 정상적으로 실행되었다는 사실은 다르다.

kubeconfig와 Context

kubectl은 kubeconfig에 저장된 정보를 사용해 클러스터에 연결한다. 기본 설정 파일은 사용자 홈 디렉터리의 .kube/config이며, --kubeconfig 옵션으로 다른 파일을 지정할 수도 있다.

Context는 다음 세 정보를 묶은 연결 설정이다.

구성의미
Cluster요청을 보낼 API Server와 연결 정보
User인증에 사용할 자격 증명 또는 인증 방식
Namespace명령에서 생략했을 때 사용할 기본 Namespace

같은 Cluster라도 User나 Namespace가 다르면 별도의 Context로 관리할 수 있다. Context를 선택한다고 새로운 권한이 부여되는 것은 아니다. 실제 접근 권한은 클러스터의 RBAC 등 서버 측 정책이 결정한다. kubeconfig의 Context 구성

Context 조회와 변경

전체 Context를 조회한다.

kubectl config get-contexts

다음은 출력 예다.

CURRENT   NAME            CLUSTER          AUTHINFO        NAMESPACE
          local-context   docker-desktop   docker-desktop   default
*         prod-dev        prod-cluster     developer        shop

CURRENT 열의 *가 현재 선택한 Context다. 위 상태에서 Namespace를 따로 지정하지 않으면 prod-cluster의 shop Namespace를 대상으로 요청한다.

현재 Context만 확인할 수도 있다.

kubectl config current-context
prod-dev

로컬 Context로 변경한다.

kubectl config use-context local-context
Switched to context "local-context".

기본 Context를 바꾸지 않고 특정 명령만 다른 Context에서 실행하려면 --context를 사용한다.

kubectl --context=local-context get pods -n default

운영 환경에서 객체를 수정하거나 삭제할 때는 Context와 Namespace를 명시하면 대상을 확인하기 쉽다.

Docker Desktop과 EKS 연결

Docker Desktop에서 Kubernetes 기능을 활성화하면 일반적으로 로컬 kubeconfig에 연결 정보가 추가된다. Context 이름은 다음 명령으로 실제 값을 확인한다.

kubectl config get-contexts

Amazon EKS에서는 AWS CLI로 연결 정보를 생성하거나 갱신할 수 있다.

aws eks update-kubeconfig --region ap-northeast-2 --name my-cluster

Region과 Cluster 이름은 실제 환경에 맞춰 바꾼다. 이 명령은 kubeconfig를 갱신하면서 현재 Context도 변경할 수 있으므로 실행 후 대상을 다시 확인한다.

kubectl config current-context
kubectl get services -n default

kubeconfig 생성에 성공하더라도 Kubernetes 객체를 조회할 권한이 자동으로 생기지는 않는다. AWS 인증 정보, EKS 접근 설정, Kubernetes 권한을 함께 확인해야 한다. EKS kubeconfig 구성

연결 오류와 권한 오류 구분하기

오류먼저 확인할 내용
connection refusedAPI Server 주소, 클러스터 실행 상태
i/o timeoutVPN, 방화벽, Private Endpoint 접근 경로
Unauthorized인증 정보 만료, 인증 Plugin 동작
Forbidden해당 사용자에게 필요한 권한이 있는지
인증서 관련 오류CA 설정, Endpoint, 인증서 유효성

Forbidden은 보통 서버와 통신하고 인증까지 진행했지만 요청한 작업이 허용되지 않았다는 의미다. 이 경우 Pod를 재시작해도 해결되지 않는다.

현재 사용자의 권한은 다음과 같이 확인할 수 있다.

kubectl auth can-i get pods -n shop
kubectl auth can-i get pods --subresource=log -n shop
kubectl auth can-i create pods --subresource=exec -n shop

Pod 상태에서 문제 찾기

Pod 목록 조회

가장 먼저 대상 Namespace의 Pod 목록을 확인한다.

kubectl get pods -n default
NAME         READY   STATUS             RESTARTS   AGE
nginx        0/1     ImagePullBackOff   0          4m20s
sample-app   1/1     Running            2          4m17s

Node와 Pod IP까지 확인하려면 -o wide를 사용한다.

kubectl get pods -n default -o wide

상태 변화를 계속 관찰하려면 --watch를 사용한다.

kubectl get pods -n default --watch

READY, STATUS, RESTARTS의 의미

READY는 일반적인 애플리케이션 Container 중 Ready로 판단된 수를 보여준다. 1/1이라고 해서 모든 업무 기능이 정상이라는 의미는 아니다. Readiness Probe가 없다면 실제 요청 처리 준비 상태를 충분히 검사하지 못할 수 있다.

STATUS는 kubectl이 Pod와 Container 정보를 종합해 보여주는 표시다. Pod의 공식 Phase와 항상 같지는 않다.

표시의미
Pending스케줄링 또는 Container 실행 준비 중
RunningPod가 실행 단계에 있음
Completed작업이 정상 종료됨
ErrImagePullImage Pull 시도 실패
ImagePullBackOffImage Pull 실패 후 재시도 대기
CrashLoopBackOffContainer 종료와 재시작이 반복되며 대기
CreateContainerConfigErrorConfigMap, Secret 등 실행 설정 오류
TerminatingPod 삭제 또는 종료 진행 중

ImagePullBackOff와 CrashLoopBackOff는 Pod의 공식 Phase가 아니다. 예를 들어 kubectl get pods에서는 ImagePullBackOff, kubectl describe pod에서는 Pod 상태가 Pending으로 표시될 수 있다.

RESTARTS는 Container 재시작 횟수다. 현재 Running이고 READY가 정상이어도 재시작 횟수가 증가했다면 이전 종료 원인을 확인해야 한다. Pod가 삭제되고 새로 만들어진 경우에는 새 Pod의 정보만 표시되므로 이 값만으로 전체 장애 이력을 알 수는 없다.

describe로 상태와 Event 확인하기

kubectl describe pod nginx -n default

우선 다음 항목을 살펴본다.

항목확인 목적
Node어디에 배치되었는지
Image실제 요청한 이미지 주소와 Tag
State현재 Container 상태
Last State이전 Container의 종료 상태
Reason대기 또는 종료 사유
Exit Code프로세스 종료 코드
Ready요청을 받을 준비 여부
Restart CountContainer 재시작 횟수
Requests, Limits자원 설정
Events스케줄링, Image Pull, Mount, Probe 관련 문제

Event는 애플리케이션 로그와 다르다. Image Pull과 Volume Mount처럼 프로세스가 시작되기 전에 발생한 문제도 확인할 수 있다.

다만 Event는 영구적인 감사 기록이 아니다. 반복 Event가 합쳐지거나 보존 기간이 지나 사라질 수 있으므로 장애 시점에 필요한 정보를 확보해야 한다.

ImagePullBackOff 해결하기

ErrImagePull과 ImagePullBackOff

Container를 시작하려면 Node가 Registry에서 이미지를 내려받아야 한다. 이 과정이 실패하면 ErrImagePull이 나타나고, 재시도 사이의 대기 구간에는 ImagePullBackOff가 표시된다.

flowchart TD
    A["Image Pull 시도"] --> B{"다운로드 성공"}
    B -->|"성공"| C["Container 생성"]
    B -->|"실패"| D["ErrImagePull"]
    D --> E["ImagePullBackOff"]
    E --> F["대기 후 재시도"]
    F --> A

이때 Pod를 삭제하는 것보다 실패 메시지를 읽는 일이 먼저다. 잘못된 이미지 주소는 다시 생성해도 그대로 실패한다.

이미지 이름 오타 확인

다음 메시지가 있다고 가정한다.

Failed to pull image "nginxs:1.14.2"
pull access denied for nginxs
repository does not exist or may require authorization

nginxs는 Nginx 이미지 이름을 잘못 입력한 값이다. 하지만 pull access denied만 보고 언제나 이름 오타라고 판단해서는 안 된다. Private Registry의 인증 문제에서도 비슷한 메시지가 나올 수 있다.

원인확인할 사항
이미지 이름 오타Registry와 Repository 경로
존재하지 않는 TagRegistry에 해당 Tag가 있는지
인증 실패imagePullSecrets, 접근 권한
Registry 접근 실패DNS, 방화벽, Proxy, 인증서
다운로드 제한Registry Rate Limit
Architecture 불일치Node Architecture와 이미지 지원 범위

잘못된 이미지로 확인하는 로컬 실습

다음 YAML은 Image Pull 오류를 확인하기 위해 이미지 이름을 의도적으로 잘못 작성한 예다. 로컬 실습 클러스터에서 사용한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx-debug
spec:
  containers:
    - name: nginx
      image: nginxs:stable-alpine
      imagePullPolicy: IfNotPresent
      resources: {}

image-pull-test.yaml로 저장하고 적용한다.

kubectl apply -f image-pull-test.yaml
kubectl get pod nginx -n default
kubectl describe pod nginx -n default

Registry 응답과 재시도 시점에 따라 ErrImagePull 또는 ImagePullBackOff가 표시될 수 있다. Events에서 실제 요청한 이미지 이름을 확인한다.

kubectl edit으로 이미지 수정

실행 중인 객체를 Editor에서 수정할 수 있다.

kubectl edit pod nginx -n default

Container의 image를 다음과 같이 변경한다.

image: nginx:stable-alpine

저장한 뒤 상태를 확인한다.

kubectl get pod nginx -n default --watch

정상이라면 이미지가 내려받아지고 Pod가 Running으로 전환된다.

Pod Spec은 대부분의 필드를 자유롭게 변경할 수 없지만, Container 이미지처럼 변경이 허용되는 필드가 있다. 환경 변수나 Volume 설정도 같은 방식으로 수정할 수 있다고 일반화해서는 안 된다.

Editor에 보이는 uid, resourceVersion, creationTimestamp는 Kubernetes가 관리하는 정보다. kubectl.kubernetes.io/last-applied-configuration에는 과거 적용 내용이 남아 있을 수 있다. 실제 Container 이미지 설정은 spec.containers[].image에서 확인한다.

원본 Manifest에도 수정 반영하기

kubectl edit은 클러스터 객체를 빠르게 수정하지만 로컬 YAML이나 Git 저장소까지 바꾸지는 않는다. 원본 파일에 잘못된 이미지가 남아 있으면 다음 배포에서 문제가 다시 생길 수 있다.

원본 YAML도 수정한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx-debug
spec:
  containers:
    - name: nginx
      image: nginx:stable-alpine
      imagePullPolicy: IfNotPresent
      resources: {}

다시 적용하고 Ready 상태를 확인한다.

kubectl apply -f image-pull-test.yaml
kubectl wait --for=condition=Ready pod/nginx -n default --timeout=120s

stable-alpine은 실습용 예제 Tag다. 운영에서는 변경되지 않는 버전 Tag나 Digest를 사용해 어떤 이미지를 배포했는지 명확하게 관리하는 편이 좋다.

Deployment가 관리하는 Pod라면 Pod 하나의 이미지를 직접 고치는 대신 Deployment의 Pod Template과 원본 Manifest를 수정해야 한다.

로그로 애플리케이션 문제 확인하기

현재 Container 로그 조회

kubectl logs sample-app -n default

Container가 여러 개라면 이름을 지정한다.

kubectl logs sample-app -n default -c app

kubectl logs는 Container의 표준 출력과 표준 오류에 기록된 로그를 조회한다. 애플리케이션이 임의의 파일에만 로그를 작성하면 해당 내용이 자동으로 표시되지는 않는다.

Container 환경에서 로그를 stdout과 stderr로 출력하는 구성이 편리한 이유다.

로그 범위 조절과 실시간 조회

새 로그를 계속 확인하려면 -f를 사용한다.

kubectl logs -f sample-app -n default -c app

최근 200줄과 Timestamp를 함께 확인한다.

kubectl logs sample-app -n default -c app --tail=200 --timestamps=true

최근 10분 로그를 확인한다.

kubectl logs sample-app -n default -c app --since=10m

kubectl logs가 애플리케이션 실행 이후의 모든 로그를 항상 제공하는 것은 아니다. Node의 로그 보존과 Rotation 설정에 영향을 받는다. 장기 이력 조회에는 중앙 로그 시스템이 필요하다.

재시작 이전 로그 조회

Container가 종료된 뒤 재시작되었다면 현재 로그에는 새로운 시작 과정만 보일 수 있다. 이때 --previous를 사용한다.

kubectl logs sample-app -n default -c app --previous

다음 정보를 함께 보면 원인을 좁히기 쉽다.

kubectl describe pod sample-app -n default
kubectl logs sample-app -n default -c app --previous --timestamps=true
상태 또는 종료 사유확인할 내용
OOMKilledMemory Limit, Heap과 Native Memory 사용량
Error예외, 종료 코드, 설정 누락
Liveness 실패 Event검사 경로, 응답 지연, 초기화 시간
Completed 반복서버 프로세스가 왜 정상 종료되는지
CrashLoopBackOff위 정보를 통해 반복 종료 원인 확인

--previous는 같은 Pod 안에서 이전에 종료된 Container 인스턴스의 로그를 조회한다. 이미 삭제된 다른 Pod의 로그를 가져오는 명령은 아니다. Kubernetes 로그 조회와 보존

로그 수준만으로 장애를 판단하지 않기

Spring 애플리케이션에서 다음과 같은 메시지를 볼 수 있다.

H2Dialect does not need to be specified explicitly
No JTA platform available
Started ServerConnector on port 8080

명시적인 Dialect 설정이 불필요하다는 경고는 설정 정리 대상일 수 있지만 서버 기동 실패를 뜻하지는 않는다. JTA를 사용하지 않는 애플리케이션에서 JTA Platform이 없다는 메시지도 곧바로 장애로 볼 수 없다.

로그 수준과 함께 실제 예외, 종료 여부, 요청 실패 시각, Probe 결과를 연결해서 확인해야 한다.

Container 내부 점검

kubectl exec

kubectl exec는 실행 중인 Container 안에서 명령을 실행한다.

kubectl exec -it sample-app -n default -c app -- bash

Bash가 없다면 sh를 사용할 수 있다.

kubectl exec -it sample-app -n default -c app -- sh
옵션의미
-i표준 입력 유지
-t터미널 할당
-c대상 Container 지정
--이후 값을 Container에서 실행할 명령으로 구분

SSH 접속과 비슷하게 Shell을 사용할 수 있지만 SSH Server나 Port를 사용하는 방식은 아니다. Kubernetes의 exec 기능으로 Container 내부에 새 프로세스를 실행한다.

프로세스와 파일 확인

Container에 필요한 도구가 있다면 다음 명령을 사용할 수 있다.

ps aux
ls -al
df -h

ps aux에서는 프로세스, CPU 사용량, 메모리와 실행 명령을 확인한다. 다만 Container 내부에서 보이는 프로세스 범위는 PID Namespace 설정에 따라 달라진다.

Java 프로세스의 RSS는 JVM Heap만을 의미하지 않는다. Thread Stack, Metaspace, Direct Buffer 등 다른 메모리도 포함하므로 Heap 크기와 Container 전체 메모리를 구분해야 한다.

Shell은 대화형으로 실행하지 않고 필요한 명령만 호출할 수도 있다.

kubectl exec sample-app -n default -c app -- ls -al /app
kubectl exec sample-app -n default -c app -- printenv SPRING_PROFILES_ACTIVE

printenv로 모든 환경 변수를 출력하면 Secret에서 주입한 값도 노출될 수 있다. 설정 확인은 필요한 변수만 선택해서 수행하는 편이 좋다.

Container 안에서 네트워크 확인

Curl이 있다면 외부 API에 요청할 수 있다.

curl --connect-timeout 3 --max-time 10 https://httpbin.org/get

이 요청이 성공했다면 해당 시점에 그 Container에서 외부 Host를 조회하고 HTTPS 요청을 완료했다는 의미다. 모든 외부 목적지나 클러스터 내부 Service가 정상이라는 증거로 확대해서는 안 된다.

존재하지 않는 Service를 호출하면 다음과 같은 오류가 발생할 수 있다.

curl http://nginx-service/test
curl: (6) Could not resolve host: nginx-service

이 오류는 HTTP 요청 처리 이전의 이름 해석 단계에서 실패했다는 뜻이다. 먼저 Service 존재 여부와 Namespace를 확인해야 한다.

kubectl get services -n default

Shell이나 진단 도구가 없는 경우

운영 이미지는 크기와 공격 표면을 줄이기 위해 Bash, Curl, 패키지 관리자를 제외할 수 있다. Distroless Image에는 Shell 자체가 없기도 하다.

실행 중인 Container에 도구를 설치하면 원래 Image와 실제 환경이 달라진다. 권한이나 인터넷 접근 제한으로 설치가 불가능할 수도 있다. 이러한 경우 별도의 Debug Container나 임시 Pod를 사용한다.

Debug Container와 임시 Pod

기존 Pod에 Debug Container 추가하기

kubectl debug는 기존 Pod에 Ephemeral Container를 추가해 진단할 수 있다.

kubectl debug -it sample-app -n default \
  --image=busybox:1.36.1 \
  --target=app \
  -- sh

이 명령에서 sample-app은 Pod 이름이고 --target=app은 대상 Container 이름이다.

Debug Container는 같은 Pod의 네트워크 Namespace를 사용하므로 네트워크 상태를 점검하는 데 유용하다. --target은 대상 Container의 프로세스 Namespace에 접근하도록 요청하며, 지원 여부는 Container Runtime에 따라 달라진다.

Debug Container가 추가되었다고 대상 Container의 Root 파일 시스템이나 환경 변수가 자동으로 복제되는 것은 아니다. 파일 접근에는 공유 Volume, 프로세스 정보와 권한 등 별도 조건이 필요하다. 실행 중인 Pod 디버깅

Ephemeral Container는 실행이 끝나도 기록이 Pod에 남으며 일반 Container처럼 재시작하거나 개별적으로 제거할 수 없다. 기존 Pod에 변경을 가하는 작업이라는 점도 고려해야 한다.

별도의 임시 Pod 실행하기

클러스터 내부 DNS나 Service 접근을 확인할 때는 별도의 임시 Pod로 충분한 경우가 많다.

kubectl run network-check -n default \
  --rm -it \
  --restart=Never \
  --image=busybox:1.36.1 \
  -- sh

Pod 안에서 DNS와 HTTP 접근을 확인한다.

nslookup kubernetes.default.svc.cluster.local
nslookup nginx-service
wget -S -O- http://nginx-service/
exit

--rm은 명령 종료 후 임시 Pod를 정리하기 위한 옵션이다. 연결이 중단되었다면 잔여 Pod가 있는지 확인할 수 있다.

kubectl get pod network-check -n default

임시 Pod에서 통신이 성공해도 기존 애플리케이션 Pod의 통신까지 확인된 것은 아니다. Namespace, Label, ServiceAccount, NetworkPolicy와 Service Mesh 설정이 다를 수 있기 때문이다.

Redis Client로 접속 확인

Redis Client가 필요하면 해당 도구가 포함된 이미지로 일회성 명령을 실행할 수 있다.

kubectl run redis-cli -n default \
  --rm -it \
  --restart=Never \
  --image=redis:7-alpine \
  -- redis-cli -h redis-service.platform.svc.cluster.local -p 6379 PING

인증이 필요 없는 실습 Redis라면 정상 응답은 다음과 같다.

PONG

인증이 필요한 서버는 별도 인증 절차가 필요하다. PING 성공은 연결과 명령 실행을 확인하는 결과이며 실제 세션이나 Cache 업무 로직까지 검증한 것은 아니다.

자원 사용량과 Pending 진단

kubectl top

Pod와 Node의 CPU, Memory 사용량을 조회한다.

kubectl top pods -n default
kubectl top pods -n default --containers
kubectl top nodes

메모리 사용량 기준으로 정렬할 수도 있다.

kubectl top pods -n default --sort-by=memory

이 명령은 Metrics Server 등 Resource Metrics API를 제공하는 구성이 필요하다. 설치되지 않았거나 수집이 지연되면 결과가 나오지 않을 수 있다. kubectl top 사용 조건

top은 최근 사용량을 빠르게 확인하는 도구다. 장시간 추세나 순간적인 Peak 분석에는 Prometheus 등의 모니터링이 필요하다.

사용량이 낮아도 Pending일 수 있다

Scheduler는 Pod의 순간 CPU 사용량보다 requests와 Node의 배치 가능한 자원량을 기준으로 판단한다.

따라서 kubectl top nodes에서 CPU 사용률이 낮게 보여도 기존 Pod의 Request 합계가 크면 새로운 Pod가 배치되지 않을 수 있다.

kubectl describe pod <POD_NAME> -n default
kubectl describe node <NODE_NAME>

Node의 Allocatable과 Allocated resources, Pod의 Event를 함께 확인한다.

Event 또는 조건확인할 원인
Insufficient cpuCPU Request를 만족하는 Node가 없음
Insufficient memoryMemory Request를 만족하는 Node가 없음
Untolerated TaintPod의 Toleration이 부족함
Node Affinity 불일치배치 조건을 만족하는 Node가 없음
Unbound PVCVolume 연결 또는 Provisioning 대기
Volume Topology 불일치Node와 Storage 위치 조건이 맞지 않음

Pending을 해결하려고 무조건 Request를 낮추기보다 실제 필요 자원과 Node 용량, Storage 조건을 먼저 확인해야 한다.

Service와 DNS 트러블슈팅

오류가 발생한 계층부터 확인하기

현상우선 확인할 범위
Could not resolve host이름, Namespace, DNS
Connection refused대상 Port, Listener, 준비된 Endpoint
Connection TimeoutRouting, NetworkPolicy, 방화벽, 응답 지연
HTTP 404Host와 Path, 애플리케이션 Routing
HTTP 500애플리케이션 예외와 내부 의존성

HTTP 응답을 받았다면 적어도 어떤 서버까지는 요청이 도달한 것이다. 404를 곧바로 DNS 장애라고 판단하거나, 이름 해석 실패를 애플리케이션 Controller 오류로 판단하지 않아야 한다.

Service 존재와 Namespace 확인

kubectl get service nginx-service -n default
kubectl get services -A

다른 Namespace의 Service라면 Namespace를 포함한 이름으로 호출한다.

nginx-service.web.svc.cluster.local

cluster.local은 일반적인 Cluster Domain이며 실제 클러스터에서는 다른 값으로 구성할 수도 있다.

Selector와 EndpointSlice 확인

Service가 있어도 대상 Pod가 없거나 Selector가 잘못되어 있을 수 있다.

kubectl describe service nginx-service -n default
kubectl get pods -n default --show-labels
kubectl get endpointslices -n default \
  -l kubernetes.io/service-name=nginx-service \
  -o yaml

Endpoint의 주소뿐 아니라 conditions.ready도 확인한다. Readiness가 실패한 Endpoint가 있으면 목록에 보이더라도 일반적인 요청 분배 대상에서 제외될 수 있다.

DNS, ClusterIP, Pod IP 순서로 비교

진단 Pod에서 다음을 비교하면 문제 범위를 좁힐 수 있다.

nslookup nginx-service
wget -S -O- http://nginx-service/
wget -S -O- http://<SERVICE_CLUSTER_IP>:<SERVICE_PORT>/
wget -S -O- http://<POD_IP>:<TARGET_PORT>/

Pod IP 호출은 진단용이다. 애플리케이션 설정에 고정해서 사용하지 않는다.

flowchart TD
    A["Service 이름 호출 실패"] --> B["DNS 조회 확인"]
    B --> C["ClusterIP와 Service Port 확인"]
    C --> D["Selector와 Ready Endpoint 확인"]
    D --> E["Pod IP와 Target Port 직접 확인"]
    E --> F["Listener와 애플리케이션 응답 확인"]

DNS 이름과 ClusterIP 호출 결과가 다르면 이름 해석을, Service 호출과 Pod 직접 호출 결과가 다르면 Endpoint와 Service 경로를 더 살펴볼 수 있다. NetworkPolicy는 함께 확인해야 한다. Kubernetes Service 디버깅

Deployment 재시작과 검증

rollout restart

설정을 반영하거나 새 Pod로 교체해야 할 때 다음 명령을 사용할 수 있다.

kubectl rollout restart deployment/sample-deploy -n default
kubectl rollout status deployment/sample-deploy -n default --timeout=180s

rollout restart는 기존 Deployment의 Pod Template을 변경해 새 Rollout을 유도한다. Deployment 객체를 삭제하거나 새로 만드는 명령은 아니다. Pod 교체는 해당 Deployment의 업데이트 전략을 따른다. kubectl rollout

이 명령 자체가 이미지 주소를 변경하지는 않는다. 하지만 변경 가능한 Tag를 사용하거나 ConfigMap과 Secret의 값이 바뀌었다면 새 Pod가 기존 Pod와 다른 코드나 설정으로 실행될 수 있다.

재시작 전에 원인 확보

재시작으로 일시적으로 복구되더라도 원인을 알지 못하면 같은 문제가 반복될 수 있다. 필요한 경우 다음 정보를 먼저 확보한다.

kubectl describe pod <POD_NAME> -n default
kubectl logs <POD_NAME> -n default -c <CONTAINER_NAME> --tail=200 --timestamps=true
kubectl logs <POD_NAME> -n default -c <CONTAINER_NAME> --previous

재시작 후에는 배포 상태와 실제 요청을 각각 확인한다.

kubectl get deployment sample-deploy -n default
kubectl get pods -n default
kubectl rollout status deployment/sample-deploy -n default

Rollout 완료는 새 Pod가 Deployment의 준비 조건을 만족했다는 뜻이다. 로그인, 데이터 조회, 외부 연동 등 실제 업무 기능의 정상 여부는 별도 요청으로 검증해야 한다.

자주 사용하는 명령어

목적명령
현재 연결 대상 확인kubectl config current-context
Context 목록kubectl config get-contexts
Pod 목록kubectl get pods -n <NAMESPACE>
Pod와 Node 위치kubectl get pods -n <NAMESPACE> -o wide
상세 상태와 Eventkubectl describe pod <POD> -n <NAMESPACE>
현재 로그kubectl logs <POD> -n <NAMESPACE> -c <CONTAINER>
실시간 로그kubectl logs -f <POD> -n <NAMESPACE> -c <CONTAINER>
이전 Container 로그kubectl logs <POD> -n <NAMESPACE> -c <CONTAINER> --previous
Container 명령 실행kubectl exec -it <POD> -n <NAMESPACE> -c <CONTAINER> -- sh
Pod 자원 사용량kubectl top pods -n <NAMESPACE>
Node 자원 사용량kubectl top nodes
Service 확인kubectl describe service <SERVICE> -n <NAMESPACE>
Deployment 재시작kubectl rollout restart deployment/<NAME> -n <NAMESPACE>
배포 진행 확인kubectl rollout status deployment/<NAME> -n <NAMESPACE>

<POD>, <NAMESPACE>, <CONTAINER> 같은 값은 실제 대상 이름으로 바꿔 사용한다.

정리

트러블슈팅은 Context와 Namespace 확인에서 시작한다. 이후 Pod 상태, 상세 Event, 현재 로그와 이전 로그를 확인하면 문제가 스케줄링, 이미지 다운로드, 애플리케이션 실행 중 어느 단계에서 발생했는지 구분할 수 있다.

Running과 READY만으로 정상 여부를 판단하지 않고 재시작 횟수와 실제 요청 결과를 함께 확인해야 한다. Container에 Shell이나 도구가 없다면 Debug Container와 임시 Pod를 사용할 수 있지만, 각각의 네트워크와 파일 접근 범위는 다르다.

빠른 복구에 edit이나 rollout restart가 도움이 될 수 있다. 수정 사항은 원본 Manifest에도 반영하고, 재배포 후에는 객체 상태와 업무 요청을 모두 검증해야 같은 문제가 반복되는 일을 줄일 수 있다.


© 2020. All rights reserved.

SIKSIK