소개

Kubernetes 클러스터를 운영하면서 인증서(SSL/TLS) 만료 관리는 매우 중요합니다. 인증서가 만료되면 API 서버, etcd, kubelet 등 주요 컴포넌트의 통신이 끊겨 서비스 장애로 이어질 수 있습니다. 이 글은 실제 운영환경에서 자주 사용하고 있는 kubeadm, RKE2, k3s, RKE, kubelet 등 주요 배포 방식별로 인증서 위치와 만료일(남은기간)을 확인하는 방법을 정리한 가이드입니다.

※ 모든 명령은 각 노드에서 실행해야 합니다. root 권한을 전제로 진행합니다.


핵심 요약

  • kubeadm: /etc/kubernetes/pki에 인증서가 위치. kubeadm certs check-expiration으로 한 번에 확인 가능.
  • RKE2: /var/lib/rancher/rke2/server/tls에 인증서가 위치.
  • k3s: /var/lib/rancher/k3s/server/tls에 인증서가 위치.
  • RKE: 배포 시 지정된 경로(예: /opt/rke/etc/kubernetes/ssl 또는 /etc/kubernetes/ssl)에 위치.
  • kubelet: 노드별로 /var/lib/kubelet/pki 등에서 인증서 확인.

※ 인증서의 위치는 환경에 따라 다를 수 있으므로 참고하시길 바랍니다.
 

공통 검사 방법으로 openssl을 이용해 PEM 파일의 만료일을 확인하거나, openssl -checkend로 특정 기간(예: 30일) 이내 만료 여부를 바로 확인할 수 있습니다.


공통: PEM 파일 직접 검사 방법 (openssl)

만료일만 출력

openssl x509 -noout -in /path/to/cert.crt -enddate

전체 날짜 정보

openssl x509 -noout -in /path/to/cert.crt -dates

남은 일수 계산 (예시)

enddate=$(openssl x509 -in /path/to/cert.crt -noout -enddate | cut -d= -f2)
endsecs=$(date -d "$enddate" +%s)
now=$(date +%s)
echo $(( (endsecs - now) / 86400 )) days left

특정 기간(예: 30일) 이내 만료 여부 확인

# 30일 = 60*60*24*30 = 2592000
openssl x509 -checkend 2592000 -noout -in /path/to/cert.crt && echo "OK (>=30d)" || echo "Will expire within 30 days"

배포별 상세 위치 및 확인 방법

1) kubeadm

  • 기본 인증서 경로: /etc/kubernetes/pki
  • 권장 명령:
kubeadm certs check-expiration
  • 개별 파일 확인:
openssl x509 -noout -in /etc/kubernetes/pki/apiserver.crt -enddate
openssl x509 -noout -in /etc/kubernetes/pki/etcd/peer.crt -enddate
# kubelet 관련 파일(노드에서): /var/lib/kubelet/pki/*.crt

kubeadm certs check-expiration은 kubeadm이 관리하는 로컬 PKI만 검사합니다. 외부 CA 또는 다른 경로에 추가되어 있는 인증서는 포함되지 않을 수 있습니다.


2) RKE2

  • 대표 경로: /var/lib/rancher/rke2/server/tls
  • 확인 예시: 

※ 아래는 최신버전부터 사용가능 (자세한 정보는 아래의 링크에서 확인)
-> https://docs.rke2.io/security/certificates

rke2 certificate check --output table

FILENAME                     SUBJECT                     USAGES       EXPIRES                  RESIDUAL TIME   STATUS
--------                     -------                     ------       -------                  -------------   ------
client-kube-proxy.crt        system:kube-proxy           ClientAuth   Jun 12, 2026 11:12 UTC   1 year          OK
client-kube-proxy.crt        rke2-client-ca@1749726675   CertSign     Jun 10, 2035 11:11 UTC   10 years        OK
client-kubelet.crt           system:node:ip-10-11-0-14   ClientAuth   Jun 12, 2026 11:12 UTC   1 year          OK
client-kubelet.crt           rke2-client-ca@1749726675   CertSign     Jun 10, 2035 11:11 UTC   10 years        OK
serving-kubelet.crt          ip-10-11-0-14               ServerAuth   Jun 12, 2026 11:12 UTC   1 year          OK
serving-kubelet.crt          rke2-server-ca@1749726675   CertSign     Jun 10, 2035 11:11 UTC   10 years        OK
client-rke2-controller.crt   system:rke2-controller      ClientAuth   Jun 12, 2026 11:12 UTC   1 year          OK
client-rke2-controller.crt   rke2-client-ca@1749726675   CertSign     Jun 10, 2035 11:11 UTC   10 years        OK
ls -l /var/lib/rancher/rke2/server/tls
for f in /var/lib/rancher/rke2/server/tls/*.crt; do
  echo "== $f =="
  openssl x509 -noout -in "$f" -subject -issuer -enddate
done

해당 경로와 다를 수 있으므로, 다르다면 환경에 맞게 확인 및 수정 후 진행


3) k3s

  • 대표 경로: /var/lib/rancher/k3s/server/tls
  • 확인 예시:

※ 아래는 최신버전부터 사용가능 (자세한 정보는 아래의 링크에서 확인)
-> https://docs.k3s.io/cli/certificate

k3s certificate check --output table

FILENAME                    SUBJECT                     USAGES       EXPIRES                  RESIDUAL TIME   STATUS
--------                    -------                     ------       -------                  -------------   ------
client-kube-proxy.crt       system:kube-proxy           ClientAuth   Jun 09, 2026 10:17 UTC   1 year          OK
client-kube-proxy.crt       k3s-client-ca@1749464211    CertSign     Jun 07, 2035 10:16 UTC   10 years        OK
client-kubelet.crt          system:node:ip-10-11-0-14   ClientAuth   Jun 09, 2026 10:17 UTC   1 year          OK
client-kubelet.crt          k3s-client-ca@1749464211    CertSign     Jun 07, 2035 10:16 UTC   10 years        OK
serving-kubelet.crt         ip-10-11-0-14               ServerAuth   Jun 09, 2026 10:17 UTC   1 year          OK
serving-kubelet.crt         k3s-server-ca@1749464211    CertSign     Jun 07, 2035 10:16 UTC   10 years        OK
client-k3s-controller.crt   system:k3s-controller       ClientAuth   Jun 09, 2026 10:17 UTC   1 year          OK
client-k3s-controller.crt   k3s-client-ca@1749464211    CertSign     Jun 07, 2035 10:16 UTC   10 years        OK
ls -l /var/lib/rancher/k3s/server/tls
openssl x509 -noout -in /var/lib/rancher/k3s/server/tls/server-ca.crt -enddate

해당 경로와 다를 수 있으므로, 다르다면  환경에 맞게 확인 및 수정 후 진행


4) RKE (Rancher RKE v1)

  • 대표 경로: /opt/rke/etc/kubernetes/ssl 또는 /etc/kubernetes/ssl (설치 시 지정됨)
  • 확인 예시:
for f in /opt/rke/etc/kubernetes/ssl/*.pem /opt/rke/etc/kubernetes/ssl/*.crt; do
  [ -f "$f" ] || continue
  openssl x509 -noout -in "$f" -enddate || true
done

5) kubelet 

  • 대표 경로: /var/lib/kubelet/pki (구성에 따라 다름)
  • 확인 예시:
ls -l /var/lib/kubelet/pki
openssl x509 -noout -in /var/lib/kubelet/pki/kubelet.crt -enddate

kubelet 인증서는 kubeadm의 자동 갱신 대상과 일부 다를 수 있으므로 노드별 확인이 필요합니다. ps aux | grep kubelet로 --cert-dir 옵션 확인 권장.


운영 Tip: 폴더 전체를 한 번에 검사하는 스크립트

아래 스크립트는 지정한 디렉터리(기본 /etc/kubernetes/pki) 내의 .crt/.pem 파일을 찾아서 만료일·발행자·남은일수를 출력합니다.

#!/bin/bash
# check-certs.sh

dir=${1:-/etc/kubernetes/pki}

echo "file,subject,issuer,enddate,days_left"

find "$dir" -type f \( -name '*.crt' -o -name '*.pem' \) | while read -r f; do
  if openssl x509 -in "$f" -noout >/dev/null 2>&1; then
    enddate=$(openssl x509 -in "$f" -noout -enddate | cut -d= -f2)
    endsecs=$(date -d "$enddate" +%s)
    now=$(date +%s)
    days=$(( (endsecs - now) / 86400 ))
    subject=$(openssl x509 -in "$f" -noout -subject | sed 's/subject= //')
    issuer=$(openssl x509 -in "$f" -noout -issuer | sed 's/issuer= //')
    echo "\"$f\","\"$subject\","\"$issuer\","\"$enddate\",$days"
  fi
done

 

./check-certs.sh

file,subject,issuer,enddate,days_left
"/etc/kubernetes/pki/apiserver.crt","CN=kube-apiserver","CN=kube-ca","Wed Oct 25 15:00:00 2025",10
"/etc/kubernetes/pki/etcd/server.crt","CN=etcd-server","CN=etcd-ca","Fri Nov 14 15:00:00 2025",30
"/etc/kubernetes/pki/front-proxy-client.crt","CN=front-proxy-client","CN=front-proxy-ca","Sun Dec 14 15:00:00 2025",60

예시 출력화면입니다.


간단 체크(30일 임박 여부)

for f in /etc/kubernetes/pki/*.crt /var/lib/rancher/*/server/tls/*.crt; do
  [ -f "$f" ] || continue
  openssl x509 -checkend $((60*60*24*30)) -noout -in "$f" && echo "$f: OK" || echo "$f: WILL EXPIRE WITHIN 30 DAYS"
done

자동화 & 모니터링 Tip

  • Crontab + Slack/메일 알림: 위 스크립트를 주기적으로 실행해 특정 디렉토리 저장 혹은 결과를 메일/슬랙으로 전송
  • Prometheus Exporter: 인증서를 파싱해 cert_expiry_seconds{cert="apiserver.crt"} 같은 메트릭으로 노출하면 Alertmanager로 알림 설정 가능
  • kubeadm 사용자: kubeadm certs check-expiration을 정기 스케줄로 등록
  • RKE2/k3s 사용자: /var/lib/rancher/.../tls 파일 생성/갱신 이벤트를 모니터링

주의사항

  • kubeadm certs check-expiration은 kubeadm이 관리하는 로컬 PKI만 검사합니다. 외부 CA로 발급되었거나, 클라우드/외부에 존재하는 인증서는 포함되지 않을 수 있습니다.
  • managed 서비스 같은 경우는 인증서 관리를 플랫폼에서 자동화하므로 문서 확인 필요

참고자료


정리

Kubernetes 클러스터를 운영하다보면, 자체 인증서를 주기적으로 갱신하게 됩니다. 갱신해야되는 시기를 놓치고 지나갈 경우 시스템 장애로 이어질 수 있기때문에, 인증서의 남은 기간은 주기적으로 체크하는 것이 좋습니다.

Kubernetes 환경에서 Pod가 갑작스럽게 종료되며 OOMKilled 상태가 되는 경우, 운영자 입장에서는 당황할 수밖에 없습니다. 이는 단순한 에러 로그가 아니라, 리소스 설정 미스 혹은 애플리케이션의 과도한 메모리 사용이 불러온 중요한 징후입니다.

이 글에서는 OOMKilled의 발생 원리부터, 원인 분석, 그리고 재발 방지를 위한 설정 팁까지 다뤄보겠습니다.

 

OOMKilled란 무엇인가?


OOMKilled는 Out Of Memory Killed의 약자로, 컨테이너가 할당받은 메모리 한도(memory limit)를 초과하여 종료된 상태를 의미합니다.

Kubernetes는 컨테이너 실행 시 설정한 memory limit 값보다 메모리를 더 사용하면, cgroup 제한에 의해 커널이 해당 프로세스를 강제로 종료시킵니다. 이 때 Pod는 OOMKilled 상태로 기록됩니다.

kubectl get pod my-app-123 -n <namespace> -o json | jq '.status.containerStatuses[].lastState.terminated'
{
  "reason": "OOMKilled",
  "exitCode": 137,
  ...
}

 

왜 OOMKilled가 발생할까?


  1. 리소스 제한 설정이 너무 낮은 경우
    • 개발 환경에서 무심코 설정한 낮은 limit 값이 실제 서비스 상황에서 부족해짐
  2. 메모리 누수(Memory Leak)
    • 애플리케이션의 메모리 해제가 정상적으로 이뤄지지 않아 사용량이 지속 증가
  3. 버스트 트래픽/비정상 입력
    • 특정 입력이나 이벤트로 인해 일시적으로 과도한 메모리 소비 발생
  4. Limit 설정은 했지만 Request는 안 한 경우
    • CPU는 공유되지만, 메모리는 강제 제한됨 → 예상보다 적은 메모리로 실행됨
  5. JVM 기반 앱의 heap 설정 누락
    • Java 앱에서 -Xmx 없이 실행하면, 전체 메모리를 heap으로 쓰려고 시도

 

OOMKilled 문제 진단 방법


1. Pod 상태 확인

kubectl describe pod <pod-name> -n <namespace>
  • 이벤트 로그에서 OOMKilled 여부 확인

2. 리소스 사용량 확인

  • 모니터링 도구 활용 -> Prometheus/Grafana 등을 활용
kubectl top pod <pod-name> -n <namespace>
  • 리소스 peak 시점 기록 여부 확인

3. 로그 및 내부 상태 분석

  • 애플리케이션 로그 확인 (메모리 누수 의심)
  • 필요시 컨테이너 쉘 접속하여 ps, top 등으로 메모리 사용량 추적

 

해결 방법과 재발 방지 팁


1. 적절한 리소스 설정 (Request / Limit)

resources:
  requests:
    memory: "256Mi"
  limits:
    memory: "512Mi"
  • 너무 작게 잡으면 잦은 OOM, 너무 크면 노드 낭비 → 실제 사용량 기반으로 조정

2. JVM 기반 앱이라면 heap 제한 필수

JAVA_OPTS="-Xms128m -Xmx256m"
  • Container limit보다 JVM heap이 커지면 바로 OOM 발생

3. VPA (Vertical Pod Autoscaler) 도입 고려

  • 애플리케이션의 메모리 사용량을 학습해 자동으로 Request/Limit을 조정

4. 메모리 누수 점검

  • Go, Node.js, Java 등에서 누수 탐지 도구 활용 (e.g., pprof, heap dump)

5. 모니터링 설정

  • 메모리 사용량에 대한 알람 설정
  • OOMKilled 발생 시 자동 Slack/이메일 알림

 

정리


Pod의 OOMKilled는 단순 종료가 아니라 운영 환경이 감당하지 못하는 워크로드가 발생했음을 나타내는 신호입니다. 이는 곧 시스템의 신뢰성과 사용자 경험에 영향을 미칠 수 있기 때문에, 정확한 분석과 대응이 필수입니다.

리소스 설정, 모니터링, 애플리케이션의 메모리 효율 개선을 통해 운영환경에서 최적의 방법을 찾으시면 도움이 될 것이라고 생각합니다. 

개요


Kubernetes에서 kubectl get nodes 명령을 실행했을 때 특정 노드가 NotReady 상태로 표시되는 경우가 있습니다. 이는 노드가 정상적으로 클러스터에 참여하지 못하고 있으며, 해당 노드에서 실행 중인 워크로드에도 영향을 미칠 수 있음을 의미합니다.

이 글에서는 NotReady 상태가 발생하는 주요 원인과 해결 방법을 정리하여, Kubernetes 운영 시 문제를 신속하게 진단하고 복구하는데 도움이 되셨으면 합니다.

 

NotReady 상태가 발생하는 경우


Kubelet 프로세스 이상

원인: Kubelet 프로세스가 충돌했거나 중지됨

확인 방법:

systemctl status kubelet
journalctl -u kubelet --no-pager | tail -n 20

설명: Kubelet이 실행되지 않으면 노드가 상태를 클러스터에 보고하지 못하여 NotReady가 됩니다.

 

네트워크 연결 문제

원인: 마스터 노드와의 네트워크 단절, CNI 플러그인 오류

확인 방법:

ping <node-ip>
kubectl get pods -n kube-system -o wide | grep calico #calico, cilium 등, CNI 플러그인 확인.

설명: API 서버와의 통신이 불가능하면 노드가 NotReady 상태가 됩니다.

 

디스크 공간 부족

원인: 디스크 공간이 가득 차서 Kubelet이 실행되지 못함

확인 방법:

df -h
kubectl describe node <node-name> | grep "DiskPressure"

설명: 디스크 압박(DiskPressure)이 발생하면 노드는 NotReady 상태가 됩니다.

 

CPU/메모리 부족

원인: 과부하로 인해 노드 리소스 부족

확인 방법:

kubectl describe node <node-name> | grep -E "MemoryPressure|PIDPressure"
top

설명: MemoryPressure가 감지되면 노드가 NotReady 상태가 될 수 있습니다.

 

컨트롤 플레인 문제

원인: kube-apiserver 또는 etcd 장애, kube-proxy 오류

확인 방법:

kubectl get pods -n kube-system | grep -E "etcd|kube-apiserver|kube-proxy"

설명: 컨트롤 플레인과 연결되지 않으면 노드가 NotReady 상태가 됩니다.

 

인증서 만료 문제

원인: Kubelet 인증서(/var/lib/kubelet/pki/kubelet-client.crt) 또는 API 서버 인증서 만료

확인 방법:

openssl x509 -in /var/lib/kubelet/pki/kubelet-client.crt -noout -enddate

설명: Kubernetes의 kubelet 및 API 서버는 인증서를 사용하여 상호 인증을 수행합니다. 인증서가 만료되면 kubelet이 API 서버와 통신할 수 없어 노드가 NotReady 상태가 됩니다.

 

해결 방법


Kubelet 재시작

systemctl restart kubelet

 

네트워크 문제 해결

마스터 노드와의 연결 확인:

ping <master-node-ip>

 

CNI 플러그인 재설정:

kubectl delete pod <cni-pod-name> -n kube-system

 

방화벽 규칙 확인:

iptables -L -v -n

 

디스크 정리

해결 예시: 디스크를 차지하고 있는 로그 삭제

rm -rf /var/log/*.log

 

리소스 최적화

해결 예시: 리소스를 많이 차지하고 있는 Pod 삭제 

kubectl top pod -A
kubectl delete pod <pod-name> -n <namespace>

 

컨트롤 플레인과 연결 문제 해결

해결 예시: kube-proxy 연결 재시작

systemctl restart kube-proxy

 

인증서 갱신 및 재시작

인증서의 갱신과정은 설치한 k8s 방법(kubeadm, kubespary 등)에 따라 다를 수 있으며 아래의 내용은 참고사항입니다.

 

인증서 수동 갱신:

kubeadm certs renew all
systemctl restart kubelet

 

kubelet 인증서 갱신:

systemctl stop kubelet
rm -f /var/lib/kubelet/pki/kubelet-client.*
systemctl start kubelet

kubelet이 API 서버와 다시 연결되면서 새로운 인증서를 자동으로 요청합니다.

 

정리


Kubernetes에서 노드가 NotReady 상태가 되는 주요 원인은 Kubelet 오류, 네트워크 문제, 디스크 및 리소스 부족, 컨트롤 플레인 장애, 그리고 인증서 만료 등입니다. 원인을 신속하게 파악하고 적절한 조치를 취하면 클러스터의 안정성을 유지할 수 있습니다.

빠른 점검 체크리스트

    1. systemctl status kubelet 확인 (Kubelet 상태 점검)
    2. ping <master-node-ip> (네트워크 연결 점검)
    3. df -h (디스크 용량 확인)
    4. kubectl describe node <node-name> (리소스 상태 및 인증서 만료 여부 확인)
    5. kubectl get pods -n kube-system (kube-system namespace 내부 컴포넌트 정상 동작 여부 확인)

 

위 체크리스트를 활용하여 문제를 빠르게 해결하고, 클러스터 운영 중 NotReady 상태가 발생하더라도 신속하게 대응할 수 있도록 준비하는 것이 중요합니다.

개요


Kubernetes에서 Pod를 생성하면 정상적으로 실행(Running)되거나 오류로 인해 실패(Failed)하는 경우가 있습니다. 그러나 때때로 Pod가 Pending 상태에서 멈춰있는 경우가 발생합니다. Pending 상태는 일반적으로 노드에서 Pod가 실행될 준비가 완료되지 않았음을 의미합니다. 본 글에서는 Pod가 Pending 상태에 머무르는 주요 원인과 해결 방법에 대해서 알아보고자 합니다.

 

Pending 상태란?


Kubernetes에서 Pending 상태는 Pod가 스케줄링되지 않았거나, 컨테이너가 아직 시작되지 않은 상태를 의미합니다. kubectl get pods 명령어를 실행했을 때 STATUS 필드가 Pending으로 나타날 수 있습니다.

kubectl get pods -n <namespace>

 

출력 예시:

NAME           READY   STATUS    RESTARTS   AGE
my-pod         0/1     Pending   0          5m

Pod가 Pending 상태로 남아있는 경우, 다음과 같은 주요 원인들을 확인해야 합니다.

 

Pending 상태의 주요 원인과 해결 방법


적절한 노드가 없음 (Insufficient Resources)

원인:

  • 클러스터 내에서 사용할 수 있는 CPU, 메모리 등의 리소스가 부족하여 Pod가 배치되지 못하는 경우.
  • 특정 노드에만 할당하도록 설정했는데 해당 노드의 리소스가 부족한 경우.

확인 방법:

kubectl describe pod my-pod -n <namespace>

 

출력 예시:

Events:
  Type     Reason             Age   From               Message
  ----     ------             ----  ----               -------
  Warning  FailedScheduling   2m    default-scheduler  0/3 nodes are available: 3 Insufficient cpu.

 

해결 방법:

  • 클러스터의 리소스 사용량 확인 (kubectl top nodes)
  • 노드 추가 또는 기존 노드의 리소스 증가
  • Pod의 리소스 요청 줄이기 (spec.containers.resources.requests 설정 수정)

예시 (deployment.yaml):

resources:
  requests:
    cpu: "500m"
    memory: "256Mi"
  limits:
    cpu: "1000m"
    memory: "512Mi"

 

노드 셀렉터(Node Selector) 또는 테인트(Taint) 문제

원인:

  • Pod에 특정 노드에만 배치되도록 nodeSelector가 설정되었으나, 해당 노드가 존재하지 않거나 스케줄링이 불가능한 경우.
  • 노드에 Taint가 설정되어 있어 Pod가 해당 노드에 배치되지 못하는 경우.

확인 방법:

kubectl describe pod my-pod -n <namespace>

 

출력 예시:

Events:
  Type     Reason             Age   From               Message
  ----     ------             ----  ----               -------
  Warning  FailedScheduling   1m    default-scheduler  0/3 nodes available: node(s) had taint {key=value: NoSchedule}.

 

해결 방법:

  1. nodeSelector 설정 확인 (kubectl get nodes --show-labels)
  2. 필요하다면 kubectl taint nodes 명령어로 taint 제거:
kubectl taint nodes node-name key=value:NoSchedule-

 

PersistentVolume (PV) 및 Storage 문제

원인:

  • Pod가 PersistentVolumeClaim(PVC)을 요청했으나, 적절한 PersistentVolume이 할당되지 않은 경우.
  • 동적 볼륨 프로비저닝(dynamic provisioning)이 실패한 경우.

확인 방법:

kubectl get pvc -n <namespace>

 

출력 예시:

NAME        STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-volume   Pending                                       standard       5m

 

해결 방법:

  • 적절한 StorageClass가 존재하는지 확인 (kubectl get storageclass)
  • 수동으로 PersistentVolume을 생성하여 PVC와 매칭

 

이미지 다운로드 지연 (Image Pull Error)

원인:

  • 컨테이너 이미지가 존재하지 않거나, Docker Hub 또는 프라이빗 레지스트리에서 다운로드가 실패한 경우.
  • 네트워크 문제로 인해 이미지 다운로드가 오래 걸리는 경우.

확인 방법:

kubectl describe pod my-pod -n <namespace>

 

출력 예시:

Events:
  Type     Reason             Age   From               Message
  ----     ------             ----  ----               -------
  Warning  Failed             30s   kubelet            Failed to pull image "my-private-registry/my-app": image not found

 

해결 방법:

  • 컨테이너 이미지 태그 확인 (kubectl get pod my-pod -o yaml)
  • 프라이빗 레지스트리 인증 필요 시, imagePullSecrets 설정
  • 노드에서 수동으로 이미지 다운로드 가능 여부 확인 (docker pull <image> 또는 crictl pull <image> 실행)

 

네트워크 정책 및 CNI 문제

원인:

  • CNI(Container Network Interface) 플러그인이 정상적으로 동작하지 않는 경우.
  • 네트워크 정책(NetworkPolicy)으로 인해 Pod의 통신이 차단된 경우.

확인 방법:

kubectl get pods -n kube-system

출력 예시:

NAME                                       READY   STATUS             RESTARTS   AGE
coredns-66bff467f8-8c9jk                   0/1     CrashLoopBackOff   5          10m
calico-node-h5s7t                          0/1     CrashLoopBackOff   5          10m

 

해결 방법:

  • CNI 플러그인 재설치 (kubectl get ds -n kube-system 확인 후 재배포)
  • kubectl logs -n kube-system <cni-pod>로 로그 확인
  • 네트워크 정책 수정하여 Pod 간 통신 가능하도록 설정

 

정리


Kubernetes에서 Pod가 Pending 상태로 멈춰 있는 경우, 다음과 같은 주요 원인을 확인해야 합니다.

원인 확인 방법 해결 방법
리소스 부족 kubectl describe pod 리소스 추가, 요청량 조정
노드 제약 조건 kubectl get nodes --show-labels nodeSelector 수정, taint 제거
PVC 문제 kubectl get pvc 올바른 PV 제공
이미지 다운로드 실패 kubectl describe pod 이미지 태그 확인, imagePullSecrets 설정
CNI 네트워크 문제 kubectl get pods -n kube-system CNI 플러그인 확인 및 재설치

 

 

운영 환경에서는 Pod가 Pending 상태인 경우를 자주 맞이하게 됩니다. 당장 Pending 상태라면 위의 체크리스트를 하나씩 확인하고 적절한 조치를 취하면 해결하는데 도움이 될 수 있을 것 입니다.

개요



Kubernetes를 운영하면서 가장 자주 마주하는 이슈 중 하나가 CrashLoopBackOff입니다. 이 글에서는 CrashLoopBackOff가 발생하는 이유를 살펴보고, 그중 컨테이너 이미지 태그 오류로 인해 발생하는 사례를 재현하여 분석해 보겠습니다.


CrashLoopBackOff란?



CrashLoopBackOff는 Kubernetes에서 특정 Pod가 지속적으로 충돌(Crash)하여 재시작을 반복하는 상태를 의미합니다. 일반적으로 Pod가 실행되었다가 비정상적으로 종료되면, kubelet은 이를 다시 시작하려고 합니다. 하지만 계속해서 실패하면 일정 시간 동안 재시도 간격을 늘려가며 CrashLoopBackOff 상태를 반복하게 됩니다.

 

발생 가능한 원인


CrashLoopBackOff가 발생하는 주요 원인은 다음과 같습니다.

  • 잘못된 컨테이너 이미지 태그: 이미지 레지스트리에서 해당 태그를 찾을 수 없음
  • 애플리케이션 내부 오류: 코드 버그, 의존성 문제, 환경 변수 설정 오류
  • 권한 문제: 애플리케이션이 실행되기 위한 권한 부족
  • 네트워크 문제: 외부 서비스와의 통신 실패로 인해 앱이 종료됨
  • OOMKilled(Out of Memory): 컨테이너가 메모리 초과로 종료됨
  • Liveness Probe 실패: Kubernetes가 컨테이너가 비정상적으로 동작한다고 판단하여 재시작함

이번 글에서는 "잘못된 컨테이너 이미지 태그"로 인해 CrashLoopBackOff가 발생하는 시나리오를 다뤄보겠습니다.

(예시의 namespace 는 default로 가정합니다)

잘못된 컨테이너 이미지 태그로 인해 CrashLoopBackOff 발생


잘못된 이미지 태그를 가진 Deployment 생성

아래의 Deployment 파일을 보면 image: nginx:invalid-tag라는 존재하지 않는 태그를 사용하고 있습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:invalid-tag  # 존재하지 않는 태그
        ports:
        - containerPort: 80

 

 

이제 이 Deployment를 생성합니다.

kubectl apply -f nginx-deployment.yaml

 

Pod 상태 확인

Deployment를 생성한 후 Pod 상태를 확인하면 다음과 같이 ImagePullBackOff 또는 ErrImagePull 상태가 나타납니다.

kubectl get pods

 

출력 예시:

NAME                                 READY   STATUS             RESTARTS   AGE
nginx-deployment-5d4b5b4b7f-abcde    0/1     ImagePullBackOff   0          1m

 

이후 Kubernetes가 지속적으로 재시도를 하다가 일정 시간이 지나면 CrashLoopBackOff 상태로 전환됩니다.

kubectl describe pod nginx-deployment-5d4b5b4b7f-abcde

 

출력 예시:

Events:
  Type     Reason             Age                From               Message
  ----     ------             ----               ----               -------
  Normal   Scheduled          2m                 default-scheduler  Successfully assigned default/nginx-deployment-5d4b5b4b7f-abcde to node01
  Normal   Pulling            2m                 kubelet            Pulling image "nginx:invalid-tag"
  Warning  Failed             2m                 kubelet            Failed to pull image "nginx:invalid-tag": rpc error: code = NotFound desc = failed to pull and unpack image "nginx:invalid-tag"
  Warning  Failed             2m                 kubelet            Error: ErrImagePull
  Normal   BackOff            1m (x3 over 2m)    kubelet            Back-off pulling image "nginx:invalid-tag"
  Warning  Failed             1m                 kubelet            Error: ImagePullBackOff

 

해결 방법


1. 컨테이너 이미지 태그 확인

  • 올바른 태그를 사용하고 있는지 체크합니다.
  • 사용 가능한 태그 목록은 공개된 컨테이너 이미지 레지스트리 또는 프라이빗 컨테이너 이미지 레지스트리에서 확인할 수 있습니다.
docker pull nginx:latest

사용가능한 이미지와 태그를 Docker Hub를 통해 받아옵니다.

 

2. Deployment 수정 nginx:latest 또는 기존에 존재하는 태그로 변경하고, 다시 배포합니다.

containers:
- name: nginx
  image: nginx:latest  # 존재하는 태그로 변경
kubectl apply -f nginx-deployment.yaml

 

3. Pod 삭제 및 재시작 기존 Pod를 삭제하고 새로운 Pod를 배포합니다.
kubectl delete pod nginx-deployment-5d4b5b4b7f-abcde
 

Kubernetes가 새롭게 생성하는 Pod에서 정상적으로 이미지가 받아지면 문제가 해결됩니다.

 

4. 프라이빗 레지스트리 사용 시 인증 확인 만약 내부 레지스트리를 사용한다면, 인증 문제가 원인일 수도 있습니다. 해당 경우, imagePullSecrets을 추가하여 해결할 수 있습니다.

spec:
  imagePullSecrets:
  - name: my-registry-secret

 

정리


CrashLoopBackOff는 Kubernetes에서 흔히 발생하는 문제 중 하나이며, 여러 가지 원인으로 인해 발생할 수 있습니다. 이번 글에서는 컨테이너 이미지 태그 오류로 인해 발생하는 CrashLoopBackOff 문제를 분석하고 해결하는 방법을 살펴보았습니다.

Kubernetes 환경에서 CrashLoopBackOff가 발생하면 다음을 확인해보셔야 합니다.

  • kubectl describe pod <POD_NAME> 명령어로 이벤트 로그 분석
  • kubectl logs <POD_NAME> 명령어로 컨테이너 로그 확인
  • kubectl get events 명령어로 클러스터 이벤트 체크

운영 환경에서 발생하는 CrashLoopBackOff는 다양한 원인으로 발생할 수 있으므로, 본 글에서 다룬 내용들이 빠르게 대응하는데 도움이 되셨으면 좋겠습니다.

개요


Kubernetes를 운영하면서 가장 자주 사용하는 CLI 도구가 바로 kubectl입니다. 하지만 kubectl get pods와 같은 명령어를 실행했을 때, 실제로 어떤 과정이 일어나고, etcd에서 데이터가 어떻게 조회되는지에 대해 한번쯤은 궁금하게 되는데요. 본 글에서는 kubectl의 내부 동작을 탐구하며, 명령어가 실행되는 과정부터 etcd에서 데이터를 읽어오는 방식까지 자세히 알아보겠습니다.
 

Kubectl의 기본 동작


kubectl은 Kubernetes API 서버와 통신하여 클러스터의 상태를 조회하거나 리소스를 조작하는 역할을 합니다. 기본적인 동작 방식은 다음과 같습니다.
  1. 사용자가 kubectl 명령어를 입력합니다.
  2. kubectl은 ~/.kube/config 파일을 참고하여 API 서버의 주소 및 인증 정보를 확인합니다.
  3. REST API 요청을 API 서버(kube-apiserver)에 전송합니다.
  4. API 서버는 요청을 처리하고, 필요한 경우 etcd에서 데이터를 조회하거나 변경합니다.
  5. API 서버는 결과를 kubectl에 반환하고, kubectl은 이를 사용자에게 출력합니다.
이제 위 과정이 실제로 어떻게 동작하는지 더 자세히 살펴보겠습니다.
 

Kubectl의 명령어 실행 과정


사용자가 kubectl get pods 명령어를 실행하면 내부적으로 다음과 같은 과정이 진행됩니다.
 

kubectl이 API 요청을 생성

    1. kubectl은 ~/.kube/config 파일을 읽고, 클러스터의 API 서버 주소와 인증 정보를 확인합니다.

    2. kubectl은 Kubernetes API에 대한 HTTP 요청을 생성합니다. 예를 들어, kubectl get pods를 실행하면 다음과 같은 요청이 생성됩니다.

GET /api/v1/namespaces/default/pods HTTP/1.1
Host: <API_SERVER>
Authorization: Bearer <TOKEN>

 

    3. 요청은 RESTful API를 통해 Kubernetes API 서버로 전달됩니다.

 

API 서버가 요청을 처리

API 서버는 다음 단계를 거쳐 요청을 처리합니다.

  1. 인증(Authentication): API 서버는 ~/.kube/config에 저장된 인증 정보(예: Bearer Token)를 확인하여 요청을 보낸 사용자가 유효한지 검증합니다.
  2. 인가(Authorization): RBAC(Role-Based Access Control) 정책을 확인하여 사용자가 해당 리소스를 조회할 권한이 있는지 확인합니다.
  3. 어드미션 컨트롤(Admission Control): 요청을 검증하고, 필요한 경우 정책(예: 리소스 제한, 네임스페이스 정책 등)을 적용합니다.
  4. etcd에서 데이터 조회: 요청이 유효하면, API 서버는 etcd에서 해당 데이터를 조회하여 응답을 생성합니다.

 

etcd에서 Pod 정보 조회

etcd는 Kubernetes의 핵심 데이터 저장소로, 모든 클러스터 상태 정보를 Key-Value 형식으로 저장하고 있습니다. kubectl get pods를 실행하면 API 서버는 etcd에서 pod 정보를 조회하는데, 이 과정은 다음과 같습니다.

 

    1. API 서버는 etcd 클라이언트를 통해 특정 key를 검색합니다. 예를 들어, default 네임스페이스의 pod 목록을 가져오는 경우, etcd에서 다음 key를 조회합니다.

/registry/pods/default/

    2. etcd는 요청을 처리하고, 해당 key에 저장된 데이터를 JSON 형식으로 반환합니다.

    3. API 서버는 etcd에서 받은 데이터를 Kubernetes API 응답 형식으로 변환합니다.

    4. 변환된 데이터는 kubectl로 반환됩니다.

 

응답을 사용자에게 출력

  1. kubectl은 API 서버에서 받은 JSON 데이터를 사용자가 보기 쉽게 가공합니다.
  2. 최종적으로 터미널에 보기 좋은 형태로 pod 목록이 출력됩니다.
NAME       READY   STATUS    RESTARTS   AGE
my-pod     1/1     Running   0          5m

 

etcd의 데이터 저장 방식


etcd는 Kubernetes의 상태를 Key-Value 형태로 저장하는 분산 저장소입니다. pod 정보는 다음과 같은 형식으로 저장됩니다.

  • Key: /registry/pods/{namespace}/{pod_name}
  • Value: Pod 객체의 JSON 데이터

예를 들어, default 네임스페이스의 my-pod의 데이터를 조회하면 etcd 내부에서는 다음과 같은 JSON 값이 저장되어 있습니다.

{
  "metadata": {
    "name": "my-pod",
    "namespace": "default",
    "uid": "12345",
    "creationTimestamp": "2025-02-28T12:00:00Z"
  },
  "status": {
    "phase": "Running"
  },
  "spec": {
    "containers": [
      {
        "name": "app-container",
        "image": "nginx:latest"
      }
    ]
  }
}

이 데이터를 API 서버가 조회하여 kubectl에 반환하는 것입니다.

 

정리


kubectl이 어떻게 동작하는지 한눈에 정리하면 다음과 같습니다.

  1. 사용자가 kubectl 명령어를 실행하면 HTTP 요청이 Kubernetes API 서버로 전달됩니다.
  2. API 서버는 인증 및 인가를 확인한 후, 필요한 경우 etcd에서 데이터를 조회합니다.
  3. etcd는 Key-Value 저장소에서 데이터를 찾아 API 서버에 반환합니다.
  4. API 서버는 받은 데이터를 JSON으로 변환하여 kubectl에 반환합니다.
  5. kubectl은 데이터를 사용자 친화적으로 가공하여 출력합니다.

이제 kubectl이 단순한 CLI 도구가 아니라, Kubernetes API 서버 및 etcd와 긴밀하게 연동되어 동작하는 구조임을 이해할 수 있습니다. 이러한 내부 동작을 이해하면 Kubernetes를 더 효과적으로 운영하고 문제 발생 시 빠르게 원인을 파악할 수 있습니다.

이번 포스트에서는 Kubernetes 환경에서의 패키지 매니저로서 주로 오픈소스 어플리케이션 배포시에 자주 사용하는 Helm 설치 방법을 소개합니다.
설치환경에 따라 설치방법이 다를 수 있으니 참고 부탁드립니다.
 
설치환경
- Apple M1 MacBook Pro 13 Inch 2020
- MacOS Sonoma
 
https://helm.sh/docs/intro/install/

Installing Helm

Learn how to install and get running with Helm.

helm.sh

 

1. Helm 공식 홈페이지 접속 후 설치 스크립트 확인

 

2. 설치 스크립트를 로컬에 다운로드 후 설치 진행

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

 
get_helm.sh 파일을 실행가능한 파일로 변경해 줍니다.

chmod 700 get_helm.sh

 
ls 명령어로만 확인하게 되면 터미널 설정에 의해 색깔이 변하게 되는데 더 정확하게 파악하기 위해서는 ls -al 명령어를 통해 현재 유저에 rw권한이 있는지 확인해 줍니다.

./get_helm.sh

 
설치 스크립트를 실행하여 설치를 진행해 줍니다. (현재 유저의 패스워드를 입력 후 진행)

위와 같은 상태로 보여지면 설치가 완료 됩니다.
 

3. Helm 설치 완료

helm version 명령어를 통해 설치된 버전을 확인합니다.

helm version

 
이렇게 진행하면 아주 간단한 방법으로 Helm 설치가 가능합니다.
 
감사합니다.

'Mac' 카테고리의 다른 글

Docker Desktop에서 Kubernetes 설치하기  (0) 2024.07.07
Mac에서 Docker Desktop 설치하기  (0) 2024.07.07
Mac에서 oh-my-zsh 설치하기  (0) 2024.07.07
Mac에서 Homebrew 설치하기  (0) 2024.06.30
Mac에서 iTerm2 설치하기  (0) 2024.06.28

이번 포스트에서는 Docker Desktop을 이용하여 Kubernetes(v1.29.2) 설치 방법을 소개합니다.
설치환경에 따라 설치방법이 다를 수 있으니 참고 부탁드립니다.
 
Docker Desktop 설치는 아래의 포스팅을 참고하시길 바랍니다.
https://dailyhodoo.tistory.com/8

[Mac] M1 Mac에서 Docker Desktop 설치하기

이번 포스트에서는 Docker Desktop 설치 방법을 소개합니다.설치환경에 따라 설치 방법이 다를 수 있으니 참고하시길 바랍니다.  설치 환경- Apple M1 MacBook Pro 13 Inch 2020- MacOS Sonoma https://www.docker.com/p

dailyhodoo.tistory.com

 
설치환경
- Apple M1 MacBook Pro 13 Inch 2020
- MacOS Sonoma
- docker desktop 4.31.0

 

1. docker desktop 실행 후 Settings 화면 접근

Enable Kubernetes 선택후 Apply & restart 버튼을 클릭합니다.

 

2. Kubernetes Cluster Installation 진행

Install 버튼을 클릭하여 설치를 진행합니다.

 
Starting... 상태가 지속되며 설치가 진행 됩니다.

 

3. Kubernetes 설치 완료 후 확인

 
컨트롤 플레인 노드가 정상적으로 설치되었는지 확인합니다. (버전 확인)

kubectl get node

 
이렇게 진행하면 아주 간단한 방법으로 Docker Desktop을 이용하여 Kubernetes 설치가 가능합니다.
 
감사합니다.

'Mac' 카테고리의 다른 글

Mac에서 Helm 설치하기  (0) 2024.07.07
Mac에서 Docker Desktop 설치하기  (0) 2024.07.07
Mac에서 oh-my-zsh 설치하기  (0) 2024.07.07
Mac에서 Homebrew 설치하기  (0) 2024.06.30
Mac에서 iTerm2 설치하기  (0) 2024.06.28

+ Recent posts