13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] kubectl

  • [k8s] Kubernetes 프로브 트러블슈팅: Liveness/Readiness 실패

    [k8s] Kubernetes 프로브 트러블슈팅: Liveness/Readiness 실패

    Kubernetes 프로브 트러블슈팅: Liveness/Readiness 실패

    Kubernetes 프로브 트러블슈팅이 중요한 이유

    Kubernetes 프로브 트러블슈팅은 단순히 /health 경로가 200을 반환하는지 확인하는 일이 아닙니다. 운영에서는 이 설정 하나가 재시작 루프, 트래픽 블랙홀, 배포 지연, 의존성 장애 전파를 만들기도 하고 막기도 하거든요. 13년 넘게 서비스를 운영하면서 느낀 건, 프로브는 애플리케이션의 상태를 보는 기능이라기보다 쿠버네티스가 장애에 어떻게 반응할지 정하는 정책에 가깝다는 점입니다.

    대표적인 장면은 비슷합니다. 앱 로그만 보면 큰 문제가 없는데 kubectl get pods의 RESTARTS가 계속 올라갑니다. 또는 배포는 끝났는데 Service 뒤에서 파드가 빠져 502, 503이 발생합니다. 이때 무작정 앱을 재배포하거나 노드를 의심하면 시간이 정말 빨리 녹습니다. 먼저 봐야 할 것은 kubelet 이벤트입니다. Liveness probe failed 뒤에 Killing container가 붙는지, Readiness probe failed만 반복되는지에 따라 원인이 완전히 갈립니다.

    위 흐름에서 핵심은 하나입니다. Liveness 실패는 재시작을 부르고, Readiness 실패는 트래픽 제외를 부릅니다. 둘 다 헬스 체크처럼 보이지만 운영 결과는 다릅니다. 그래서 프로브를 설계할 때는 “정상인가?”보다 “실패했을 때 쿠버네티스가 무엇을 해야 하는가?”를 먼저 정해야 합니다.

    Liveness Probe 실패와 Readiness Probe 오류의 차이

    Liveness Probe는 컨테이너가 복구 불가능한 상태에 빠졌는지 판단합니다. 실패가 임계치에 도달하면 kubelet이 해당 컨테이너를 재시작합니다. Readiness Probe는 지금 요청을 받아도 되는지 판단합니다. 실패해도 컨테이너는 계속 실행되지만, 해당 파드는 Service 엔드포인트에서 제외됩니다. Startup Probe는 초기 기동 중인 앱을 보호합니다. Startup Probe가 설정되면 성공하기 전까지 Liveness와 Readiness가 실행되지 않으므로, 기동이 느린 앱에서 조기 재시작을 막는 데 유용합니다.

    운영에서 많이 터지는 실수는 외부 의존성까지 Liveness에 넣는 겁니다. DB가 잠깐 느려졌을 뿐인데 모든 파드가 동시에 재시작되면, 커넥션 재생성·캐시 워밍업·JIT/클래스 로딩 같은 비용이 한꺼번에 몰립니다. 장애를 복구하는 게 아니라 장애에 연료를 붓는 모양이 되더라고요. DB, Redis, 메시지 브로커, 외부 API는 대부분 Readiness에서 다루는 편이 안전합니다.

    구분 Liveness Probe Readiness Probe Startup Probe 운영 판단 기준
    목적 컨테이너를 재시작해야 하는지 판단 트래픽을 받을 준비가 됐는지 판단 초기 기동이 끝났는지 판단 실패 시 원하는 조치가 재시작인지, 트래픽 차단인지부터 정합니다.
    실패 시 동작 컨테이너 재시작 Service 엔드포인트에서 제외 성공 전까지 다른 프로브 보류, 실패 시 컨테이너 재시작 RESTARTS, Events, EndpointSlice를 같이 봐야 오판이 줄어듭니다.
    적합한 검사 이벤트 루프 멈춤, 데드락, 기본 핸들러 무응답 DB 연결, 캐시 준비, 필수 설정 로딩, 마이그레이션 대기 JVM/Node.js/.NET 앱의 긴 부팅, 모델 로딩, 초기 캐시 구성 외부 의존성 실패가 곧 프로세스 사망을 의미하지는 않습니다.
    과도하게 엄격할 때 재시작 루프, 콜드 스타트 비용 증가 정상 파드가 트래픽을 못 받아 가용 용량 감소 기동 실패 감지가 늦어짐 엄격함은 안정성이 아니라 운영 정책입니다. 비용을 함께 계산해야 합니다.
    주로 확인할 증거 Killing container, BackOff, --previous 로그 Ready 0/1, Endpoint/EndpointSlice 누락 기동 로그, 초기화 완료 시점, Startup 실패 이벤트 로그보다 먼저 Events를 보면 방향을 빨리 잡습니다.

    쿠버네티스 헬스 체크 기본 설정 예시

    아래 예시는 세 프로브를 분리한 Deployment입니다. 실제 애플리케이션 이미지가 /livez, /readyz, /startupz 엔드포인트를 제공한다는 전제로 봐주세요. /livez는 프로세스와 HTTP 서버가 응답 가능한지만 확인하고, /readyz는 실제 요청 처리 준비 상태를 확인합니다. /startupz는 초기화가 끝나기 전 Liveness가 끼어들지 않도록 막습니다. 운영 매니페스트에서는 이 정도 분리가 나중에 장애 분석 시간을 꽤 줄여줍니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: probe-demo
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: probe-demo
      template:
        metadata:
          labels:
            app: probe-demo
        spec:
          terminationGracePeriodSeconds: 30
          containers:
            - name: app
              image: your-registry.example.com/probe-demo:1.0.0
              ports:
                - name: http
                  containerPort: 8080
              startupProbe:
                httpGet:
                  path: /startupz
                  port: http
                periodSeconds: 5
                timeoutSeconds: 2
                failureThreshold: 30
              livenessProbe:
                httpGet:
                  path: /livez
                  port: http
                periodSeconds: 10
                timeoutSeconds: 2
                failureThreshold: 3
              readinessProbe:
                httpGet:
                  path: /readyz
                  port: http
                periodSeconds: 5
                timeoutSeconds: 2
                successThreshold: 1
                failureThreshold: 3

    값을 고를 때는 감이 아니라 앱의 실제 시간을 기준으로 잡습니다. 기동 로그에서 “서버 포트 바인딩 완료”, “마이그레이션 완료”, “캐시 로딩 완료”, “요청 처리 가능” 시점을 분리해 보세요. initialDelaySeconds를 길게 늘리는 방식은 단순하지만, 앱이 빨리 떠도 무조건 기다립니다. 반면 startupProbe는 성공하는 순간 다음 단계로 넘어가므로, 느린 기동과 빠른 회복을 같이 다루기 좋습니다.

    프로브 파라미터는 서로 곱해져 운영 결과를 만듭니다. 예를 들어 periodSeconds: 10, failureThreshold: 3, timeoutSeconds: 2라면 실패를 확정하기까지 여러 번의 검사 주기가 필요합니다. 대략 30초 안팎으로 생각할 수 있지만, 정확한 감지 시간은 스케줄링과 응답 지연에 영향을 받습니다. 고정 수치처럼 외우기보다 “얼마나 빨리 빼고 얼마나 늦게 죽일 것인가”라는 정책으로 보는 편이 낫습니다.

    매니페스트에서 확인할 필드는 httpGet.path, port, periodSeconds, timeoutSeconds, failureThreshold, successThreshold입니다. 참고로 successThreshold는 Liveness와 Startup에서는 1이어야 하고, Readiness에서만 1보다 크게 둘 수 있습니다. Readiness 복귀를 일부러 보수적으로 만들고 싶을 때만 조정하세요.

    실전 디버깅: Kubernetes 프로브 트러블슈팅 명령어

    장애가 났을 때 저는 애플리케이션 로그보다 먼저 쿠버네티스 이벤트를 봅니다. 이벤트는 kubelet이 왜 컨테이너를 죽였는지, 왜 Ready에서 제외했는지 비교적 솔직하게 남깁니다. 아래 순서대로 보면 “앱 문제인지, 프로브 설정 문제인지, Service 라우팅 문제인지”가 빠르게 갈립니다.

    1. 파드의 Ready 상태와 재시작 횟수를 확인합니다.
    2. Events에서 Liveness probe failed, Readiness probe failed, Startup probe failed를 찾습니다.
    3. 현재 로그와 이전 컨테이너 로그를 비교합니다.
    4. Service, Endpoint, EndpointSlice에 파드 IP가 들어갔는지 확인합니다.
    5. 클러스터 내부에서 DNS, 포트, HTTP 경로를 직접 호출합니다.
    NS=default
    APP=probe-demo
    POD=$(kubectl get pod -n "$NS" -l app="$APP" -o jsonpath='{.items[0].metadata.name}')
    
    kubectl get pods -n "$NS" -l app="$APP" -o wide
    kubectl describe pod "$POD" -n "$NS"
    kubectl logs "$POD" -n "$NS" --all-containers=true --tail=200
    kubectl logs "$POD" -n "$NS" --previous --tail=200
    kubectl get events -n "$NS" --sort-by=.lastTimestamp
    kubectl get svc "$APP" -n "$NS"
    kubectl get endpoints "$APP" -n "$NS"
    kubectl get endpointslice -n "$NS" -l kubernetes.io/service-name="$APP"

    해석은 이렇게 합니다. Liveness probe failed 다음에 Killing container가 이어지면 재시작 원인은 Liveness입니다. Readiness probe failed만 반복되고 RESTARTS가 늘지 않으면 파드는 살아 있지만 Service 트래픽에서 제외된 겁니다. connection refused는 포트 미오픈, 잘못된 포트명, 앱 기동 전 검사 시작, 또는 앱이 127.0.0.1에만 바인딩된 경우를 의심합니다. HTTP probe failed with statuscode: 404는 대개 경로 오타입니다. context deadline exceeded나 timeout 계열 메시지는 앱 내부 지연, CPU throttling, I/O 대기, GC 지연까지 같이 봐야 합니다.

    kubectl run curl-debug \
      -n default \
      --image=curlimages/curl:8.5.0 \
      --restart=Never \
      --rm -it \
      -- sh
    
    # 디버그 파드 안에서 실행
    curl -sv --max-time 3 http://probe-demo.default.svc.cluster.local/readyz
    curl -sv --max-time 3 http://probe-demo.default.svc.cluster.local/livez
    curl -sv --max-time 3 http://probe-demo.default.svc.cluster.local:80/readyz

    여기서 중요한 건 “Service DNS로 되는가”와 “Pod IP로 직접 되는가”를 나눠 보는 겁니다. Service DNS는 실패하는데 Pod IP 직접 호출은 성공하면 Service selector나 EndpointSlice 쪽을 봅니다. Pod IP 직접 호출도 실패하면 컨테이너 포트, 앱 바인딩 주소, 경로, 응답 시간을 봅니다. 프로브는 kubelet이 Pod 네트워크의 Pod IP로 직접 보내는 검사라서, Ingress에서 보이는 증상과 다를 수 있습니다.

    재현 가능한 시나리오: Readiness Probe 오류 만들고 확인하기

    테스트 클러스터에서 일부러 잘못된 Readiness 경로를 넣어보면 동작이 선명해집니다. 아래 Pod는 nginx를 띄우지만 존재하지 않는 /not-ready를 Readiness로 검사합니다. 컨테이너는 실행되지만 Ready가 되지 않고, Service 엔드포인트에도 들어가지 않는 상태를 만들 수 있습니다. 이거 한 번 직접 보면 실장애 때 감이 훨씬 빨리 옵니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: bad-readiness
      labels:
        app: bad-readiness
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - name: http
              containerPort: 80
          readinessProbe:
            httpGet:
              path: /not-ready
              port: http
            initialDelaySeconds: 3
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 2
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: bad-readiness
    spec:
      selector:
        app: bad-readiness
      ports:
        - name: http
          port: 80
          targetPort: http
    kubectl apply -f bad-readiness.yaml
    kubectl get pod bad-readiness -w
    kubectl describe pod bad-readiness
    kubectl get endpoints bad-readiness
    kubectl get endpointslice -l kubernetes.io/service-name=bad-readiness
    kubectl delete -f bad-readiness.yaml

    정상적인 관찰 결과는 이렇습니다. kubectl get pod에서 READY가 0/1로 보이고, describe Events에는 Readiness 실패가 찍힙니다. 하지만 RESTARTS는 증가하지 않습니다. Service의 Endpoint 또는 EndpointSlice도 비어 있거나 해당 파드 IP가 빠져 있어야 합니다. 이 작은 실험을 해두면 “왜 컨테이너는 Running인데 트래픽이 안 가지?”라는 질문에 훨씬 빨리 답할 수 있습니다.

    반대로 Liveness 경로를 잘못 지정하면 결과가 달라집니다. READY 이전에 컨테이너가 재시작될 수 있고, Events에는 Liveness 실패와 컨테이너 종료 메시지가 같이 나타납니다. 같은 404라도 Readiness에서는 트래픽 제외, Liveness에서는 재시작입니다. 이 차이를 머리에 넣어두면 디버깅 방향이 흔들리지 않습니다.

    제가 겪은 Liveness Probe 실패 패턴

    가장 위험했던 패턴은 종합 건강검진식 Liveness였습니다. /health 요청 하나에 DB 쿼리, Redis ping, 외부 API 호출, 디스크 체크를 전부 넣는 방식입니다. 겉보기에는 꼼꼼합니다. 그런데 DB가 순간적으로 느려지면 모든 파드가 Liveness 실패로 재시작됩니다. 재시작된 파드는 다시 DB 커넥션을 만들고, 캐시를 채우고, 준비되지 않은 상태에서 또 검사를 받습니다. 문제의 뿌리는 DB 지연인데 증상은 애플리케이션 재시작 폭풍으로 바뀝니다.

    제가 쓰는 기준은 꽤 엄격합니다. Liveness는 “이 프로세스를 죽이면 나아지는가?”에 예라고 답할 수 있을 때만 실패시킵니다. 외부 의존성이 실패했지만 프로세스가 요청 큐를 비우고 회복할 수 있다면 Liveness가 아니라 Readiness입니다. 이 기준이 약간 보수적으로 보여도, 운영에서는 불필요한 재시작을 줄이는 쪽이 대체로 더 싸고 안전했습니다.

    • Liveness: 기본 HTTP 핸들러, 이벤트 루프, 내부 상태 플래그처럼 가볍고 로컬인 검사만 둡니다.
    • Readiness: DB 연결, 브로커 구독, 필수 설정 로딩, 캐시 워밍업처럼 트래픽 처리 조건을 둡니다.
    • Startup: 앱 부팅이 느리거나 초기화 단계가 여러 개인 서비스에서 Liveness 개입을 늦춥니다.
    • 외부 API: 핵심 경로가 아니라면 Readiness에서도 과하게 엄격하게 묶지 않습니다. 서킷 브레이커와 graceful degradation이 더 나은 경우가 많습니다.
    startupProbe:
      httpGet:
        path: /startupz
        port: 8080
      periodSeconds: 5
      timeoutSeconds: 2
      failureThreshold: 30
    livenessProbe:
      httpGet:
        path: /livez
        port: 8080
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 3
    readinessProbe:
      httpGet:
        path: /readyz
        port: 8080
      periodSeconds: 5
      timeoutSeconds: 2
      failureThreshold: 3

    이 구성을 그대로 복사하기보다, 먼저 앱의 실제 기동 경로를 관찰하세요. 포트가 열리는 시점과 요청을 안전하게 처리할 수 있는 시점은 다를 수 있습니다. 특히 Java, .NET, 대형 Node.js 앱처럼 초기 로딩이 긴 서비스는 포트가 먼저 열리고 내부 초기화가 나중에 끝나는 경우가 있습니다. 이때 Liveness를 빨리 켜면 멀쩡히 뜨는 중인 앱을 kubelet이 반복해서 죽입니다.

    검증/결과: 무엇을 보고 정상이라고 판단할까

    프로브 설정을 바꾼 뒤 “배포 성공”만 보고 끝내면 위험합니다. 제가 확인하는 기준은 네 가지입니다. 파드가 Ready인지, 재시작이 멈췄는지, Service 엔드포인트에 포함됐는지, 그리고 실패 이벤트가 더 이상 증가하지 않는지입니다. 이 확인 루틴은 짧지만, 운영에서는 진짜 편하더라고요.

    NS=default
    APP=probe-demo
    
    kubectl rollout status deployment/$APP -n $NS
    kubectl get pods -n $NS -l app=$APP -o custom-columns='NAME:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount,PHASE:.status.phase,NODE:.spec.nodeName'
    kubectl get endpoints $APP -n $NS -o wide
    kubectl get endpointslice -n $NS -l kubernetes.io/service-name=$APP -o wide
    kubectl describe deployment $APP -n $NS
    kubectl get events -n $NS --field-selector involvedObject.kind=Pod --sort-by=.lastTimestamp

    판단은 이렇게 가져갑니다. READY가 모든 컨테이너 수와 일치하면 요청을 받을 준비가 된 상태입니다. RESTARTS가 계속 늘면 Liveness 실패 또는 애플리케이션 크래시를 봅니다. EndpointSlice에 파드 IP가 없으면 Readiness 실패, Service selector 불일치, 포트명 불일치, 또는 라벨 문제를 봅니다. connection refused는 포트와 바인딩 주소, timeout은 앱 지연과 리소스 압박, 404는 경로 불일치부터 확인하세요.

    Kubernetes 프로브 트러블슈팅을 위한 kubectl 진단 결과 화면

    운영 대시보드가 있다면 프로브 실패율만 따로 보는 것도 좋습니다. 다만 프로브 자체가 너무 자주 실행되면 노드와 애플리케이션에 부하가 됩니다. 특히 exec 프로브는 컨테이너 안에서 프로세스를 실행하므로, 파드 수가 많고 주기가 짧으면 비용이 눈에 띄게 늘 수 있습니다. HTTP나 TCP로 충분한 경우라면 굳이 셸 명령을 매번 실행하지 않는 편이 낫습니다.

    Kubernetes 프로브 모범 사례와 선택 기준

    제가 팀에 리뷰할 때 가장 많이 보는 항목은 세 가지입니다. 첫째, Liveness가 외부 의존성을 검사하지 않는가. 둘째, Readiness가 실제 트래픽 처리 가능성을 반영하는가. 셋째, 느린 기동을 initialDelaySeconds 땜질이 아니라 Startup Probe로 풀었는가. 이 세 가지만 잡아도 Kubernetes 프로브 트러블슈팅의 절반은 줄어듭니다.

    상황별 추천

    • 애플리케이션 시작이 느리다면: Startup Probe를 추가하고 Liveness의 initialDelaySeconds를 과하게 늘리지 않습니다.
    • DB가 잠깐 끊길 수 있다면: DB 검사는 Readiness Probe에 둡니다. Liveness에서 DB를 때리지 않습니다.
    • 프로세스가 가끔 멈춘다면: 로컬 핸들러 기반의 가벼운 Liveness Probe로 재시작하게 합니다.
    • 경로가 자주 바뀐다면: 비즈니스 API와 헬스 체크 엔드포인트를 분리하고, 라우팅 리팩터링에 영향받지 않는 고정 경로를 씁니다.
    • 서비스 메시나 Ingress를 쓴다면: kubelet의 Pod 프로브, Service 라우팅, 외부 로드밸런서 헬스 체크를 서로 다른 계층으로 보고 분리해서 검증합니다.
    • 파드 수가 많다면: 짧은 periodSeconds와 무거운 exec 프로브 조합을 피합니다. 헬스 체크도 트래픽입니다.

    자주 묻는 질문

    Q. Liveness와 Readiness를 같은 경로로 써도 되나요?
    가능은 하지만 운영 기본값으로 추천하지 않습니다. 같은 경로가 DB나 외부 API를 검사하면 Readiness 실패로 끝날 문제가 Liveness 재시작으로 커질 수 있습니다. 정말 같은 경로를 써야 한다면 그 경로는 로컬 상태만 검사해야 합니다.

    Q. TCP Probe와 HTTP Probe 중 뭘 써야 하나요?
    HTTP 서비스라면 HTTP Probe가 보통 더 낫습니다. 상태 코드와 경로로 의도를 표현할 수 있기 때문입니다. TCP Probe는 포트가 열렸는지만 확인하므로, 앱이 내부 초기화 중이어도 성공할 수 있습니다.

    Q. Exec Probe는 언제 쓰나요?
    HTTP 엔드포인트를 만들기 어려운 배치성 프로세스나 레거시 앱에서 컨테이너 내부 상태 파일·명령 결과를 확인할 때 씁니다. 다만 명령 실행 비용, 이미지 안의 바이너리 존재 여부, 셸 의존성을 반드시 확인해야 합니다.

    Q. gRPC 서비스는 어떻게 하나요?
    gRPC Health Checking Protocol을 구현했다면 Kubernetes v1.27부터 안정화된 gRPC probe를 검토할 수 있습니다. gRPC probe는 HTTP/TCP probe와 달리 named port를 지원하지 않으므로 숫자 포트를 명시해야 합니다. 서비스 이름과 포트 설정을 명확히 하고, HTTP 게이트웨이와 혼동하지 않도록 매니페스트 리뷰를 거치는 편이 좋습니다.

    Liveness Probe Readiness Probe Startup Probe 선택 기준 요약

    공식 동작 기준은 Kubernetes 문서의 Configure Liveness, Readiness and Startup Probes와 Liveness, Readiness, and Startup Probes를 기준으로 확인하되, 실제 값은 서비스의 기동 로그와 장애 패턴에 맞춰 조정하세요. 문서의 예시는 출발점이지 운영 정책 그 자체는 아닙니다. 관련 배포 전략이나 장애 대응 문서가 있다면 내부 링크로 함께 묶어두면 독자가 다음 글로 자연스럽게 이동합니다.

    마무리: 운영에서 덜 아프게 쓰는 결론

    프로브 설계의 기준은 명확합니다. 재시작하면 좋아지는 문제만 Liveness에 넣고, 트래픽만 빼면 되는 문제는 Readiness에 넣고, 느린 기동은 Startup Probe로 보호하세요. 이 원칙을 어기면 프로브는 장애 감지 장치가 아니라 장애 증폭 장치가 됩니다.

    일반 웹 서비스라면 저는 이렇게 시작합니다. Liveness는 /livez에서 프로세스와 기본 핸들러만 확인합니다. Readiness는 /readyz에서 DB 연결, 필수 설정, 내부 큐 상태처럼 요청 처리 조건을 확인합니다. 기동이 느린 서비스는 /startupz를 두고, Startup Probe가 성공하기 전까지 Liveness가 개입하지 않게 합니다. 배포 후에는 kubectl describe pod, kubectl logs --previous, kubectl get endpoints, kubectl get endpointslice를 함께 확인합니다. 이 네 가지를 습관처럼 보면 Kubernetes 프로브 트러블슈팅에서 가장 흔한 삽질을 꽤 많이 줄일 수 있습니다.

  • [Kubernetes] 클라이언트 비교: Lens, K9s, kubectl 선택 가이드

    [Kubernetes] 클라이언트 비교: Lens, K9s, kubectl 선택 가이드

    [Kubernetes] 클라이언트 비교: Lens, K9s, kubectl 선택 가이드

    Kubernetes 클라이언트 비교가 왜 중요하냐고 물으시면, 운영 환경이 조금만 커져도 답이 바로 나옵니다. 파드(Pod, 컨테이너 실행 단위) 하나 상태 보는 건 쉬운데, 네임스페이스(Namespace, 리소스 격리 단위) 여러 개를 오가고 로그를 보고 이벤트를 확인하고 배포 상태까지 챙기려면 Kubernetes 관리 도구 선택이 꽤 중요하거든요. 저도 처음엔 kubectl만 붙잡고 버텼었는데, 실제로 써보니까 Lens, K9s, kubectl은 서로 경쟁 관계라기보다 역할이 다른 도구에 더 가깝더라고요. 오늘은 제가 홈랩과 실무에서 겪었던 흐름을 바탕으로, 어떤 상황에서 무엇을 고르면 덜 삽질하는지 정리해보겠습니다.

    특히 이 글은 단순한 스펙 나열이 아니라, Kubernetes 클라이언트 비교 관점에서 실제 운영 감각을 중심으로 풀어볼 생각입니다. 혹시 여러분도 'GUI가 편하긴 한데 결국 터미널로 돌아오게 되던데요?' 같은 경험 있으신가요? 저도 딱 그랬습니다 ㅎㅎ

    Lens, K9s, kubectl이 각각 어느 레이어에서 관리 경험을 제공하는지 한눈에 보여주는 개요 이미지입니다.

    Kubernetes 클라이언트 비교, 먼저 큰 그림부터 보겠습니다

    쉽게 말해 이렇습니다.

    • kubectl: Kubernetes 공식 CLI(Command Line Interface, 명령줄 도구)입니다.
    • K9s: 터미널 기반 TUI(Text User Interface, 텍스트 UI)로 클러스터 상태를 빠르게 탐색하게 도와줍니다.
    • Lens: GUI(Graphical User Interface, 그래픽 화면 기반) 중심으로 리소스를 시각적으로 관리하기 좋습니다.

    여기서 중요한 포인트가 하나 있습니다. 세 도구 모두 결국 kubeconfig(Kubernetes 접속 설정 파일)를 기반으로 클러스터에 접근하는 경우가 많다는 점입니다. 즉, 접근 권한과 컨텍스트(context, 현재 대상 클러스터/네임스페이스 설정)가 핵심이고, 겉모습만 다르다고 생각하시면 이해가 빠릅니다.

    Lens, K9s, kubectl 차이점은 어디서 체감될까요?

    1. kubectl 사용법이 기본기가 되는 이유

    kubectl은 결국 기준점입니다. Deployment(디플로이먼트, 배포 리소스), Service(서비스, 네트워크 노출 리소스), Ingress(인그레스, 외부 트래픽 진입점) 같은 객체를 가장 정확하게 다루기 좋거든요. 자동화 스크립트나 CI/CD에서도 거의 기본 전제로 깔립니다.

    제가 직접 해보니 장애 상황에서는 결국 kubectl로 돌아오게 되더라고요. 이유는 단순합니다. 로그, describe, events, yaml 출력까지 가장 세밀하게 볼 수 있기 때문입니다.

    2. K9s가 빛나는 순간

    K9s는 '터미널 안에서 빠르게 둘러보는 맛'이 있습니다. 방향키와 단축키로 파드 이동하고, 로그 보고, 리소스 상태를 훑는 흐름이 상당히 빠릅니다. SSH로 바로 들어가서 점검하는 환경에서는 특히 강합니다. GUI가 없는 서버 점검 환경에서는 진짜 편하더라고요.

    3. Lens가 편한 이유

    Lens는 시각화가 좋습니다. 여러 리소스를 화면에서 관계적으로 보기가 쉽고, 초보자도 진입 장벽이 낮은 편입니다. 특히 처음 Kubernetes를 접하는 분들은 YAML만 보는 것보다 상태를 한 화면에서 확인하는 방식이 훨씬 이해가 빠를 수 있습니다.

    다만 GUI가 편하다고 해서 운영의 본질이 쉬워지는 건 아닙니다. 여기서 많이 헷갈리시더라고요. 화면은 친절하지만, 권한 문제나 리소스 의미를 모르면 결국 판단은 사람이 해야 하거든요.

    쿠버네티스 관리 도구 비교 표로 정리해보겠습니다

    항목 kubectl K9s Lens
    형태 CLI TUI GUI
    학습 난이도 초반엔 높음 중간 낮음
    속도 익숙하면 매우 빠름 탐색이 빠름 화면 기반 확인이 편함
    자동화 적합성 매우 높음 낮음 낮음
    로그/이벤트 심층 분석 강함 강함 보통 이상
    원격 SSH 환경 매우 적합 매우 적합 제한적
    입문자 친화성 낮음 중간 높음
    추천 대상 운영자, 자동화 담당자 터미널 선호 운영자 초보자, 시각적 관리 선호자

    Lens K9s 비교만 놓고 보면, 초반 적응은 Lens가 쉽고 장기 운영 효율은 K9s가 더 손에 붙는 경우가 많습니다. 하지만 kubectl이 빠지면 결국 한계가 옵니다. 이건 꽤 중요합니다.

    실전 구현: kubectl, K9s, Lens를 같은 흐름에서 써보기

    이론만 보면 감이 잘 안 오죠. 그래서 간단한 점검 흐름으로 묶어보겠습니다. 예시는 특정 배포가 정상인지 확인하는 상황입니다.

    1. 현재 컨텍스트와 네임스페이스를 확인합니다.
    2. 배포와 파드 상태를 봅니다.
    3. 문제가 있는 파드 로그와 이벤트를 확인합니다.
    4. 필요하면 GUI나 TUI에서 리소스 관계를 함께 봅니다.

    1. kubectl로 기본 점검하기

    kubectl config get-contexts
    kubectl config current-context
    kubectl get ns
    kubectl get deploy -A
    kubectl get pods -A -o wide

    처음엔 이게 뭔가 싶었는데, 사실 운영자는 이 네 줄만 자주 써도 클러스터 현재 상태를 꽤 빨리 파악할 수 있습니다. 특히 current-context를 먼저 보는 습관은 꼭 들이시는 걸 추천합니다. 잘못된 클러스터에 명령 넣는 사고, 생각보다 자주 납니다.

    2. 특정 애플리케이션 문제 좁혀가기

    kubectl get pods -n production
    kubectl describe pod myapp-abc123 -n production
    kubectl logs myapp-abc123 -n production --tail=100
    kubectl get events -n production --sort-by=.metadata.creationTimestamp

    실제로 써보니까 장애 대응에서 제일 중요한 건 '무엇이 실패했는가'보다 '어디서부터 실패했는가'를 빠르게 찾는 거였습니다. describe 결과에서 이벤트를 보고, 로그에서 에러 패턴을 보고, 리소스 부족인지 이미지 풀(image pull) 실패인지 분리하는 게 핵심입니다.

    Kubernetes 클라이언트 비교에서 kubectl과 K9s 사용 장면 이미지

    운영자가 터미널 기반으로 파드 상태를 점검하고 로그를 추적하는 실전 흐름을 보여주는 이미지입니다.

    3. K9s로 빠르게 탐색하기

    k9s

    K9s는 실행 자체는 단순합니다. 다만 익숙해지려면 머릿속에 동선이 생겨야 합니다. 보통 저는 이런 순서로 봤습니다.

    1. 네임스페이스를 맞춥니다.
    2. Pods 화면에서 재시작 횟수와 상태를 봅니다.
    3. 문제 파드를 선택해 로그와 describe 성격의 정보를 확인합니다.
    4. 리소스 사용량이나 관련 객체로 이동합니다.

    SSH 세션 하나에서 다 처리되는 게 장점입니다. 홈랩에서 VM 여러 대 만질 때는 이 방식이 정말 편했어요. 마우스 손 안 대도 되니까 흐름이 끊기지 않거든요.

    4. Lens로 시각적으로 확인하기

    Lens는 클러스터를 등록한 뒤 Workloads(워크로드), Pods, Events, Config, Networking 같은 메뉴를 통해 탐색하는 방식입니다. 제가 초급자 분들 온보딩할 때는 Lens를 먼저 보여드린 적이 많았습니다. '아, Kubernetes 안에서 이런 객체들이 연결되는구나'가 빨리 보이기 때문입니다.

    예를 들어 서비스 장애가 났을 때, 파드만 보는 게 아니라 서비스와 엔드포인트(endpoint), 인그레스까지 같이 봐야 할 때가 있거든요. 이때 GUI의 장점이 분명히 있습니다.

    Kubernetes 클라이언트 비교를 위한 GUI 관리 도구 화면 이미지

    워크로드, 서비스, 인그레스 관계를 시각적으로 확인하는 GUI 관리 흐름을 표현한 이미지입니다.

    어떤 사람에게 어떤 도구가 맞을까요?

    kubectl이 맞는 경우

    • 운영 자동화와 스크립트 작업이 많습니다.
    • CI/CD 파이프라인과 연동해야 합니다.
    • 장애 원인을 깊게 파고드는 편입니다.
    • Kubernetes 클라이언트 도구의 기준 문법을 익히고 싶습니다.

    K9s가 맞는 경우

    • 터미널 작업이 익숙합니다.
    • 원격 서버나 SSH 환경에서 자주 일합니다.
    • 클러스터 탐색 속도를 중요하게 봅니다.
    • 마우스보다 키보드가 편합니다.

    Lens가 맞는 경우

    • Kubernetes 입문 단계입니다.
    • 팀원들과 화면 공유하며 상태를 설명해야 합니다.
    • 리소스 관계를 시각적으로 보는 게 편합니다.
    • 한눈에 파악되는 대시보드 느낌을 선호합니다.

    ⚠️ 실제로 자주 겪는 문제와 해결 방법

    여기서는 제가 삽질 좀 했던 부분을 솔직하게 적어보겠습니다. 이런 건 문서보다 경험에서 더 빨리 배우게 되더라고요.

    1. 컨텍스트(context) 착각

    가장 위험합니다. 개발 클러스터인 줄 알고 운영 클러스터를 보고 있던 적, 저도 있었습니다. 다행히 큰 사고는 안 났지만 식은땀이 나더라고요.

    kubectl config current-context
    kubectl config view --minify

    명령 전후로 현재 컨텍스트를 확인하는 습관이 중요합니다. 특히 여러 kubeconfig를 합쳐 쓰는 환경이라면 더 그렇습니다.

    2. 권한 문제(RBAC, Role-Based Access Control)

    Lens든 K9s든 kubectl이든, 권한이 없으면 안 보이거나 실패합니다. GUI가 예쁘게 보여도 백엔드는 결국 API 서버 권한 체크를 거치거든요.

    kubectl auth can-i get pods -n production
    kubectl auth can-i list secrets -n production

    화면 문제처럼 보이는데 사실은 권한 문제인 경우가 꽤 많습니다. 이거 진짜 자주 나옵니다.

    3. 로그만 보고 끝내는 실수

    처음엔 로그만 파봤었는데, 나중에 보니 이벤트(event) 쪽이 더 직접적인 경우가 많았습니다. 예를 들어 스케줄링 실패, 이미지 풀 실패, 프로브(probe) 실패 같은 건 이벤트에 힌트가 잘 남거든요.

    4. GUI 의존도가 너무 높아지는 문제

    Lens가 편해서 계속 쓰다 보면, 막상 터미널만 가능한 환경에서 당황할 수 있습니다. 그래서 제 추천은 이렇습니다. 입문은 Lens, 숙련은 K9s, 기준은 kubectl. 이 조합이 제일 덜 흔들렸습니다.

    검증: 실제로 무엇이 더 빨랐나?

    정량 벤치마크처럼 수치를 들이대고 싶진 않습니다. 확실하지 않은 숫자는 오히려 판단을 흐리거든요. 대신 체감 기준으로 말씀드리면 이렇습니다.

    • 처음 구조를 이해하는 속도는 Lens가 빨랐습니다.
    • 운영 중 다수 리소스를 훑는 속도는 K9s가 좋았습니다.
    • 정확한 원인 분석과 재현 가능한 절차 정리는 kubectl이 가장 강했습니다.

    제가 홈랩에서 여러 네임스페이스를 오가며 점검할 때도 비슷했습니다. 처음 상태 확인은 K9s나 Lens가 편했는데, 최종 확인 커맨드와 기록은 결국 kubectl로 남기게 되더라고요. 여기서 중요한 포인트! 빠른 확인 도구와 정확한 제어 도구는 다를 수 있습니다.

    Kubernetes 클라이언트 비교 결과를 정리한 운영 대시보드 이미지

    파드 상태, 이벤트, 로그 확인 결과가 정리된 운영 검증 화면을 묘사한 이미지입니다.

    정리: 제 선택은 하나가 아니라 조합입니다

    제 결론은 꽤 명확합니다. Kubernetes 클라이언트 비교를 할 때 하나만 고르려고 하면 오히려 답이 꼬입니다. kubectl은 반드시 익혀야 하고, 그 위에 K9s 또는 Lens를 얹는 방식이 가장 현실적입니다.

    • 초보자라면: Lens로 구조를 익히고 kubectl 사용법을 함께 배우세요.
    • 터미널 친화적 운영자라면: K9s와 kubectl 조합이 오래 갑니다.
    • 자동화까지 고려한다면: kubectl 중심 사고는 필수입니다.

    저도 처음엔 '어차피 GUI 있으면 되지 않나?' 싶었는데, 실제로 써보니까 클러스터를 깊게 이해할수록 kubectl 비중이 커졌습니다. 반대로 팀 협업이나 빠른 시각 확인은 Lens가 좋았고요. K9s는 그 사이에서 정말 실용적이었습니다. 이 셋은 배척할 대상이 아니라, 상황에 따라 꺼내 쓰는 공구함에 더 가깝습니다.

    세 도구의 장단점과 추천 대상, 사용 시나리오를 한 장으로 요약한 비교 인포그래픽 이미지입니다.

    FAQ: 많이 받는 질문

    Q1. Kubernetes 입문자는 무엇부터 시작하면 좋을까요?

    Lens로 객체 관계를 익히면서 kubectl 기본 명령을 같이 익히는 걸 추천합니다. 시각적 이해와 명령 기반 이해를 같이 가져가야 오래 갑니다.

    Q2. Lens K9s 비교에서 더 운영자다운 도구는 무엇인가요?

    터미널에 익숙하다면 K9s가 더 손에 붙을 가능성이 큽니다. 다만 팀 내 공유와 설명은 Lens가 더 편할 수 있습니다.

    Q3. kubectl만으로도 충분한가요?

    충분합니다. 다만 충분하다는 것과 편하다는 것은 다릅니다. 그래서 많은 분들이 K9s나 Lens를 보조 도구로 함께 씁니다.

    마무리

    오늘은 Kubernetes 클라이언트 비교라는 주제로 Lens, K9s, kubectl을 함께 봤습니다. 정답은 사람마다 조금 다르지만, 기준점은 분명합니다. 제어와 자동화는 kubectl, 빠른 탐색은 K9s, 시각적 이해는 Lens입니다. 이 프레임만 잡아도 도구 선택이 훨씬 쉬워집니다.

    다음 글에서는 kubectl 사용법을 조금 더 실전적으로 파서, 운영자가 자주 쓰는 조회 명령과 로그/이벤트 확인 루틴을 따로 정리해볼 예정입니다. 이전 글에서 다뤘던 홈랩 클러스터 구성 내용과 함께 보시면 더 이해가 잘 되실 겁니다. 혹시 지금 어떤 도구를 메인으로 쓰고 계신가요? 써보면 취향도 분명히 갈리더라고요.

  • [k8s] kubectl 고급 활용법: Pod, Service, Deployment 관리 팁

    [k8s] kubectl 고급 활용법: Pod, Service, Deployment 관리 팁

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘도 서버실의 삐걱거리는 소리와 함께 하루를 시작하네요. 😊

    인프라 엔지니어로 일하다 보면, 매일같이 손에서 놓지 못하는 도구들이 몇 가지 있습니다. 그중에서도 Kubernetes(쿠버네티스) 클러스터를 운영한다면 kubectl(큐브컨트롤)은 정말 없어서는 안 될 존재거든요. <code>kubectl은 쿠버네티스 클러스터와 상호작용하는 CLI(명령줄 인터페이스) 도구로, 파드(Pod), 서비스(Service), 디플로이먼트(Deployment) 등 모든 리소스(resource)를 관리할 수 있게 해줍니다.

    근데 이거, 진짜 제대로 쓰고 있나? 하는 의문, 혹시 여러분도 해보신 적 있으신가요? 저도 처음엔 kubectl get pod, kubectl apply -f 같은 기본적인 명령어만으로 충분하다고 생각했거든요. 하지만 클러스터가 커지고 문제 상황이 복잡해질수록, 더 깊고 강력한 kubectl 활용법이 필요하다는 걸 깨달았습니다. 덕분에 삽질 좀 했습니다만, 그만큼 얻은 꿀팁들도 많았죠. ㅎㅎ

    오늘은 13년차 인프라 엔지니어인 제가 직접 현업과 홈랩에서 써보면서 얻은 kubectl 고급 활용법들을 여러분께 알려드리려고 합니다. 특히 Pod, Service, Deployment를 효과적으로 관리하는 팁과 함께, 제가 겪었던 삽질 경험과 트러블슈팅 노하우도 솔직하게 공유해 드릴게요. 이 글을 통해 여러분의 쿠버네티스 운영 생산성이 한층 더 높아지기를 바랍니다!

    쿠버네티스 Pod, Service, Deployment는 서로 유기적으로 연결되어 동작합니다. (이미지: 쿠버네티스 핵심 컴포넌트 아키텍처)

    핵심 개념 다시 보기: Pod, Service, Deployment

    본격적인 팁을 알려드리기 전에, 오늘 다룰 세 가지 핵심 리소스에 대해 간단히 짚고 넘어갈게요. 이미 잘 알고 계시겠지만, 다시 한번 머릿속에 정리하는 시간이라고 생각하시면 좋습니다.

    • Pod (파드): 쿠버네티스에서 가장 작고 기본적인 배포 단위입니다. 하나 이상의 컨테이너(Container)를 포함할 수 있으며, 같은 파드 내의 컨테이너들은 네트워크와 스토리지를 공유해요. 쉽게 말해, ‘작업을 수행하는 최소한의 묶음’이라고 보시면 됩니다.
    • Service (서비스): 여러 파드에 대한 안정적인 네트워크 접근을 제공하는 추상화 계층이죠. 파드는 언제든 생성되고 삭제될 수 있는데, 서비스는 이런 파드의 동적인 변화와 관계없이 일정한 IP 주소와 DNS 이름을 통해 파드 그룹에 접근할 수 있도록 해줍니다. 마치 변동하는 직원들에게 고정된 부서 전화번호를 할당해주는 것과 같죠.
    • Deployment (디플로이먼트): 파드와 ReplicaSet(레플리카셋)을 선언적으로 관리하는 상위 리소스입니다. 애플리케이션의 버전을 관리하고, 원하는 수의 파드를 항상 유지하며, 롤링 업데이트(Rolling Update)나 롤백(Rollback) 같은 배포 전략을 손쉽게 구현할 수 있어요. ‘애플리케이션의 배포와 생명주기를 책임지는 관리자’라고 생각하시면 편합니다.

    kubectl 고급 활용법: Pod 관리 팁

    파드는 쿠버네티스에서 가장 많이 다루는 리소스 중 하나일 겁니다. 저도 매일 파드 상태를 확인하고, 로그를 보고, 문제가 생기면 파드 안으로 들어가 보곤 하거든요. 몇 가지 유용한 명령어들을 소개합니다.

    1. 로그 실시간 확인 (Log Streaming)

    애플리케이션 개발이나 트러블슈팅 시 가장 기본이 되는 작업이죠. kubectl logs 명령어로 특정 파드의 로그를 확인할 수 있습니다. 특히 -f (follow) 옵션은 로그를 실시간으로 스트리밍해주기 때문에, 마치 tail -f처럼 동작해서 에러를 잡을 때 정말 유용해요.

    # 특정 파드의 로그 실시간 스트리밍
    kubectl logs -f my-app-pod-abcdefg
    
    # 특정 컨테이너의 로그만 보기 (파드에 여러 컨테이너가 있을 경우)
    kubectl logs -f my-app-pod-abcdefg -c my-container-name
    
    # 마지막 100줄만 보고 싶을 때
    kubectl logs --tail=100 my-app-pod-abcdefg
    
    # 특정 시간 이후 로그만 보고 싶을 때
    kubectl logs --since=1h my-app-pod-abcdefg # 1시간 이내 로그
    kubectl logs --since=2023-01-01T00:00:00Z my-app-pod-abcdefg # 특정 시각 이후 로그

    제가 개발 중 에러를 잡을 때 정말 유용하게 썼던 기능입니다. 특히 컨테이너가 갑자기 재시작(restart)될 때, -f 옵션으로 로그를 계속 보고 있으면 어떤 이유로 죽었는지 금방 파악할 수 있더라고요. 💡

    2. 컨테이너 내부 접속 (Exec into Container)

    파드 안에서 직접 명령어를 실행하거나 파일 시스템을 확인해야 할 때가 있습니다. 마치 SSH로 서버에 접속하는 것처럼, kubectl exec 명령어로 컨테이너 내부에 접속할 수 있어요.

    # 특정 파드의 컨테이너 내부로 접속 (bash 셸 사용)
    kubectl exec -it my-app-pod-abcdefg -- bash
    
    # 특정 컨테이너 내부로 접속 (파드에 여러 컨테이너가 있을 경우)
    kubectl exec -it my-app-pod-abcdefg -c my-container-name -- sh # sh 셸 사용 예시

    터미널이 없으면 답답하잖아요? 문제가 생겼을 때 바로 파드 내부로 들어가서 설정 파일을 확인하거나, 특정 명령어를 실행해서 상황을 진단해볼 수 있어서 정말 편하더라고요. ⚠️ 중요한 건 -- 뒤에 실행할 명령어를 붙여야 한다는 점입니다.

    3. 자원 사용량 확인 (Resource Usage)

    파드가 갑자기 죽거나 성능이 저하되는 원인 중 하나는 CPU나 메모리 같은 리소스 부족인 경우가 많습니다. kubectl top pod 명령어를 사용하면 파드별 리소스 사용량을 한눈에 볼 수 있어요. 다만, 이 기능을 사용하려면 클러스터에 Metrics Server(메트릭스 서버)가 설치되어 있어야 합니다.

    # 모든 파드의 CPU 및 메모리 사용량 확인
    kubectl top pod
    
    # 특정 네임스페이스의 파드만 확인
    kubectl top pod -n my-namespace
    
    # 파드를 CPU 사용량 기준으로 정렬
    kubectl top pod --sort-by=cpu

    파드가 갑자기 죽는다? 대부분 CPU나 메모리를 너무 많이 쓰는 경우가 많더라고요. kubectl top pod로 확인해서 리소스 제한(resource limit)을 조정해주곤 합니다. 💡

    4. Pod 이벤트 확인 (Event Check)

    파드가 생성되지 않거나, Pending(대기) 상태에 머무르거나, 예상치 못하게 재시작될 때 그 원인을 알려주는 것이 바로 Event(이벤트)입니다. kubectl describe pod 명령의 하단에 이벤트 정보가 자세히 나옵니다.

    # 특정 파드의 상세 정보 및 이벤트 확인
    kubectl describe pod my-app-pod-abcdefg

    왜 내 파드가 Pending에 머물러 있을까? 답은 Event에 있습니다. 예를 들어, 필요한 볼륨(Volume)이 마운트(mount)되지 않았거나, 노드의 리소스가 부족하거나, 컨테이너 이미지를 풀(pull)하지 못하는 등 다양한 원인이 이벤트로 기록돼요. 저도 처음에 이거 때문에 밤샜습니다. 결국 이벤트 로그에 답이 있더라고요!

    kubectl 고급 활용법: Service 관리 팁

    서비스는 외부 트래픽을 파드로 연결해주는 중요한 역할을 합니다. 서비스 관련 문제가 생기면 애플리케이션 전체가 먹통이 될 수 있죠. 서비스 관리 팁들을 알아볼게요.

    1. 서비스 상세 정보 (Service Details)

    서비스의 IP 주소, 포트 매핑, 연결된 파드 정보 등 상세 정보를 확인할 때 kubectl describe service 명령이 유용해요.

    # 특정 서비스의 상세 정보 확인
    kubectl describe service my-app-service

    외부에서 접근이 안 될 때, 제일 먼저 살펴보는 곳입니다. Cluster IP(클러스터 IP), External IP(외부 IP), Port(포트) 매핑 정보가 정확한지 확인하곤 합니다. 특히 Type이 NodePort나 LoadBalancer일 경우, 외부 접근이 가능해야 하는데 안 된다면 여기서 단서를 찾을 수 있죠.

    2. 서비스 포트 포워딩 (Port Forwarding)

    클러스터 내부의 서비스에 로컬 머신에서 직접 접속해야 할 때가 있습니다. 예를 들어, 개발 중인 로컬 환경에서 클러스터 내부의 데이터베이스 서비스에 연결해야 할 때 유용해요. kubectl port-forward 명령으로 로컬 포트를 서비스 포트에 연결할 수 있습니다.

    # 특정 서비스의 80포트를 로컬 8080포트로 포워딩
    kubectl port-forward service/my-app-service 8080:80
    
    # 특정 파드의 80포트를 로컬 8080포트로 포워딩 (서비스 대신 파드에 직접 연결)
    kubectl port-forward my-app-pod-abcdefg 8080:80

    홈랩에서 외부 IP 없이 내부 서비스 테스트할 때 이거 진짜 꿀팁입니다! 로컬에서 개발한 프론트엔드(frontend)가 백엔드(backend) 서비스에 잘 연결되는지 확인할 때 아주 유용하게 썼어요. 🎉

    3. 엔드포인트 확인 (Endpoint Verification)

    서비스는 파드들의 IP 주소를 모아놓은 Endpoint(엔드포인트)를 기반으로 트래픽을 라우팅합니다. 서비스는 있는데 왜 연결이 안 되지? 싶을 때는 엔드포인트가 제대로 생성되었는지 확인해야 해요. 파드가 정상적으로 Running(실행) 상태여야 엔드포인트에 등록됩니다.

    # 특정 서비스에 연결된 엔드포인트 확인
    kubectl get endpoints my-app-service
    
    # 모든 엔드포인트 확인
    kubectl get ep

    서비스는 잘 있는데 왜 연결이 안 되지? 알고 보니 엔드포인트가 비어있을 때가 있더라고요. 파드가 죽었거나, readinessProbe(레디니스 프로브)가 실패해서 엔드포인트에서 제외된 경우였습니다. ⚠️

    kubectl 고급 활용법: Deployment 관리 팁

    디플로이먼트는 애플리케이션 배포의 핵심입니다. 새로운 버전을 배포하고, 문제가 생기면 이전 버전으로 되돌리고, 트래픽 변화에 따라 파드 개수를 조절하는 등 많은 작업을 디플로이먼트를 통해 하게 됩니다.

    다양한 kubectl 명령어를 활용해 Pod, Service, Deployment의 상태를 실시간으로 모니터링하는 모습입니다. (이미지: kubectl 모니터링)

    1. 롤링 업데이트 & 롤백 (Rolling Update & Rollback)

    새로운 버전의 애플리케이션을 배포할 때, kubectl apply 명령을 사용하면 디플로이먼트가 롤링 업데이트(Rolling Update)를 수행합니다. 이 과정에서 업데이트 상태를 확인하고, 문제가 생겼을 때 이전 버전으로 롤백(Rollback)하는 것이 중요해요.

    # 디플로이먼트 롤아웃(rollout) 상태 확인
    kubectl rollout status deployment/my-app-deployment
    
    # 디플로이먼트 롤아웃 히스토리(history) 확인
    kubectl rollout history deployment/my-app-deployment
    
    # 이전 버전으로 롤백 (이전 리비전으로 되돌리기)
    kubectl rollout undo deployment/my-app-deployment
    
    # 특정 리비전으로 롤백
    kubectl rollout undo deployment/my-app-deployment --to-revision=2

    실수로 잘못된 이미지를 배포했을 때, kubectl rollout undo 이거 한 방이면 되돌릴 수 있어서 얼마나 든든한지 모릅니다. 롤아웃 상태를 보면서 파드가 하나씩 교체되는 걸 지켜보는 것도 꽤 재미있고요. 😅

    2. 스케일링 (Scaling)

    애플리케이션에 트래픽이 몰리거나 반대로 줄어들 때, 파드의 개수를 동적으로 조절해야 합니다. kubectl scale 명령어를 사용하면 디플로이먼트의 파드 개수를 쉽게 늘리거나 줄일 수 있어요.

    # 디플로이먼트의 파드 개수를 5개로 스케일링
    kubectl scale deployment/my-app-deployment --replicas=5
    
    # 오토스케일러(Horizontal Pod Autoscaler, HPA) 설정
    kubectl autoscale deployment my-app-deployment --cpu-percent=50 --min=2 --max=10

    갑자기 트래픽이 몰릴 때, 빠르게 파드를 늘려줄 수 있죠. 물론 HPA를 설정하면 자동으로 스케일링되겠지만, 수동으로 빠르게 대응해야 할 때 이 명령어가 아주 유용해요.

    3. 환경 변수/시크릿 확인 (Env/Secret Check)

    애플리케이션이 제대로 동작하지 않을 때, 환경 변수(Environment Variable)나 시크릿(Secret) 설정이 잘못되었을 가능성도 있습니다. 디플로이먼트의 상세 정보를 통해 이를 확인할 수 있어요.

    # 특정 디플로이먼트의 상세 정보 확인
    kubectl describe deployment my-app-deployment

    describe 명령의 출력에서 Environment 섹션과 Volumes 섹션을 확인하면, 어떤 환경 변수가 설정되었고 어떤 시크릿이나 컨피그맵(ConfigMap)이 마운트(mount)되었는지 알 수 있습니다. 간혹 환경 변수 오타 때문에 앱이 안 뜨는 경우도 있더라고요. 디스크라이브로 확인하면 됩니다.

    ⚠️ 삽질 경험 & 트러블슈팅 팁

    제가 13년 동안 인프라 엔지니어로 일하면서 수없이 겪었던 삽질 경험들 중, kubectl과 관련하여 특히 기억에 남는 몇 가지를 공유합니다. 여러분은 저처럼 밤새지 마세요! 😅

    Problem 1: 파드가 계속 Pending 상태인 경우

    새로운 디플로이먼트를 배포했는데, 파드가 Running 상태가 되지 않고 계속 Pending(대기) 상태에 머무는 경우가 있습니다. 저도 처음엔 이게 뭔가 싶어서 계속 파드를 삭제하고 다시 만들고 했었죠.

    • 원인:
      • 노드의 리소스(CPU, 메모리) 부족
      • 이미지 풀(Image Pull) 실패 (예: 잘못된 이미지 이름, 프라이빗 레지스트리 인증 실패)
      • 볼륨(PersistentVolume, PersistentVolumeClaim) 바인딩 실패
      • 노드 셀렉터(Node Selector)나 테인트/톨러레이션(Taints/Tolerations) 설정 문제로 스케줄링할 노드를 찾지 못함
    • 해결:
      # 파드 상세 정보 확인 (가장 중요!)
      kubectl describe pod <pod-name>
      # 이벤트 섹션에서 FailedScheduling, Failed to pull image 등의 에러 메시지를 찾습니다.
      
      # 노드 리소스 확인
      kubectl top nodes
      
      # 이미지 레지스트리 인증 정보 확인 (Secret)
      kubectl get secret <image-pull-secret-name> -o yaml

      결국 답은 kubectl describe pod의 Events 섹션에 있습니다. 어떤 이유로 파드가 스케줄링되지 못했는지, 이미지를 가져오지 못했는지 자세히 알려주거든요. 저도 이거 때문에 밤샜습니다. 결국 이벤트 로그에 답이 있더라고요! 💡

    Problem 2: 서비스가 외부에서 접근 안 되는 경우

    분명히 서비스를 만들었고 파드도 잘 돌고 있는데, 외부에서 접속이 안 되는 경우가 있습니다. 특히 클라우드 환경에서 많이 겪었던 문제인데요.

    • 원인:
      • 서비스 타입(Service Type) 오설정 (ClusterIP는 외부 접근 불가)
      • 로드밸런서(LoadBalancer)나 인그레스(Ingress) 컨트롤러 문제
      • 클라우드 프로바이더(Cloud Provider)의 방화벽(Firewall) 설정
      • 서비스의 selector와 파드의 labels가 일치하지 않아 엔드포인트가 비어있음
    • 해결:
      # 서비스 타입 및 외부 IP 확인
      kubectl get svc <service-name> -o wide
      
      # 엔드포인트 확인 (파드가 서비스에 제대로 연결되었는지)
      kubectl get ep <service-name>
      
      # 서비스 상세 정보 확인
      kubectl describe svc <service-name>

      로드밸런서 타입으로 만들었는데 왜 안 돼? 알고 보니 클라우드 provider 설정 문제였던 적도 있었어요. 예를 들어, GCP나 AWS에서 로드밸런서가 프로비저닝(provisioning)되지 않았거나, 보안 그룹(Security Group) 설정이 잘못되어 있었죠. 😅 항상 엔드포인트와 서비스 타입을 먼저 확인하는 습관을 들이세요!

    Problem 3: Deployment 롤아웃이 멈춘 경우

    새로운 버전으로 디플로이먼트를 업데이트했는데, 롤아웃이 중간에 멈춰버리고 진행되지 않는 경험, 다들 있으시죠?

    • 원인:
      • 새로 생성된 파드가 CrashLoopBackOff 상태로 계속 죽음
      • 파드의 readinessProbe(레디니스 프로브)가 실패하여 새 파드가 Ready 상태가 되지 못함
      • 리소스 부족으로 새 파드가 생성되지 못함
    • 해결:
      # 롤아웃 상태 확인
      kubectl rollout status deployment/<deployment-name>
      
      # 새로운 파드의 이름 확인 (롤아웃 중 생성된 파드)
      kubectl get pod -l app=<app-label> --sort-by=.metadata.creationTimestamp
      
      # 문제의 파드 로그 및 이벤트 확인
      kubectl logs <new-pod-name>
      kubectl describe pod <new-pod-name>

      새로운 이미지가 문제가 있어서 기존 파드도 못 죽고 롤아웃이 멈춰버린 경험, 다들 있으시죠? 저도 몇 번 겪었습니다. 이럴 때는 새로 생성되는 파드의 로그와 이벤트를 확인해서 왜 Ready 상태가 되지 못하는지 파악하는 것이 중요해요. 그리고 문제가 파악되면 kubectl rollout undo로 빠르게 이전 버전으로 롤백하는 것이 현명한 방법입니다!

    쿠버네티스 환경에서 흔히 겪을 수 있는 트러블슈팅 상황을 한눈에 볼 수 있습니다. (이미지: 쿠버네티스 트러블슈팅)

    검증 및 결과 확인

    위에서 배운 명령어들을 활용하여 변경 사항이 잘 적용되었는지, 클러스터의 상태는 정상적인지 확인하는 것이 중요합니다. 주로 사용하는 명령어는 다음과 같아요.

    # 모든 네임스페이스의 모든 리소스 확인 (초보자에게 추천!)
    kubectl get all --all-namespaces
    
    # 특정 네임스페이스의 주요 리소스 확인 (파드, 서비스, 디플로이먼트, 레플리카셋)
    kubectl get deploy,svc,pod,rs -n my-namespace
    
    # 특정 리소스의 자세한 정보 확인 (IP 주소, 노드 정보 등)
    kubectl get deploy,svc,pod -o wide -n my-namespace

    이 명령어들로 제가 고친 설정이 잘 반영되었는지, 파드들이 정상적으로 돌고 있는지, 외부 IP는 제대로 할당되었는지 확인하곤 합니다. 특히 -o wide 옵션은 더 많은 정보를 보여줘서 매우 편리해요. ✅

    오늘 다룬 kubectl 고급 활용 팁들을 한 장으로 요약한 인포그래픽입니다. (이미지: kubectl 팁 요약)

    마무리하며: `kubectl` 마스터가 되는 그날까지!

    오늘은 13년차 인프라 엔지니어의 경험을 담아 kubectl 고급 활용법에 대해 알아봤습니다. Pod, Service, Deployment를 관리할 때 유용하게 사용할 수 있는 명령어들과 함께, 제가 직접 겪었던 삽질 경험과 트러블슈팅 노하우까지 솔직하게 공유해 드렸네요.

    사실 kubectl은 알면 알수록 강력한 도구거든요. 기본적인 명령어만 쓰기엔 아까운 기능들이 너무 많아요. 클러스터의 모든 것을 kubectl 하나로 제어할 수 있다고 해도 과언이 아닙니다. 저도 처음엔 헷갈렸지만, 꾸준히 사용하고 궁금한 점은 직접 찾아보면서 익혔습니다. 여러분도 저처럼 삽질하면서 얻은 팁들을 활용해서 kubectl 마스터가 되시길 바랍니다! 🎉

    다음에는 kubectl 플러그인이나 K9s 같은 터미널 UI 도구들에 대해서도 다뤄보면 좋겠네요. 쿠버네티스 운영에 또 다른 재미를 더해줄 도구들이거든요. 긴 글 읽어주셔서 감사합니다. 다음에도 더 유익한 정보로 찾아오겠습니다! 😉