
디플로이먼트 장애, 왜 발생하고 어떻게 해결할까요?
새로운 버전을 배포했는데 갑자기 모든 서비스가 멈췄다는 알람이 울리는 상황을 상상해 보세요. 새벽 시간에 울리는 호출은 엔지니어에게 가장 큰 압박으로 다가오지요. 디플로이먼트 트러블슈팅이 익숙하지 않다면, 어떤 명령어를 먼저 쳐야 할지, 로그의 어디를 봐야 할지 몰라 당황하며 시간을 허비하게 됩니다.
실제로 운영 환경에서 발생하는 장애는 단순히 애플리케이션 코드가 틀려서 발생하는 경우보다 설정 오류나 리소스 부족, 네트워크 문제인 경우가 훨씬 많아요. 쿠버네티스라는 복잡한 시스템 안에서 디플로이먼트가 정상적으로 파드(Pod)를 생성하지 못하거나, 생성된 파드가 계속해서 죽고 살아나기를 반복하는 현상은 매우 흔한 일입니다. 이런 문제를 빠르게 해결하지 못하면 서비스 가용성이 떨어지고 결국 비즈니스 손실로 이어지게 돼요.
이 글은 클라우드 환경으로의 전환을 준비하거나, 실무에서 쿠버네티스를 운영하며 갑작스러운 장애 상황에 직면한 시스템 엔지니어를 위해 작성했어요. 복잡한 이론보다는 실제 장애 상황에서 무엇을 보고 어떤 조치를 취해야 하는지를 중심으로 논리적인 흐름을 구성했습니다. 이 가이드를 끝까지 읽고 나면, 장애 발생 시 당황하지 않고 체계적인 진단 절차를 밟을 수 있는 능력을 갖추게 될 거예요.
이번 글에서 핵심적으로 다룰 내용은 다음과 같아요.
- 디플로이먼트 장애의 대표적인 증상과 그 의미
- 장애 진단을 시작하기 전 반드시 갖춰야 할 환경과 지식
- 단계별 로그 및 이벤트 분석을 통한 원인 압축 과정
- 상황별 구체적인 복구 조치 시나리오
- 동일한 장애를 방지하기 위한 안정적인 설정 방법
장애 진단 전 반드시 확인해야 할 준비 사항
문제를 해결하기 위해 무턱대고 명령어를 입력하기 전에, 현재 내가 처한 상황을 객관적으로 파악할 수 있는 도구와 지식이 준비되어 있어야 해요. 준비 없이 뛰어들면 오히려 잘못된 조치로 인해 클러스터의 상태를 더 악화시킬 수도 있습니다.
필수 진단 도구와 권한 확인
가장 먼저 kubectl 명령어를 사용할 수 있는 환경인지 확인해야 해요. 단순히 명령어를 아는 것을 넘어, 현재 접속한 계정이 디플로이먼트와 파드의 상태를 상세히 조회할 수 있는 read/list/watch 권한을 가지고 있는지 체크하는 것이 중요합니다. 권한이 부족하다면 describe 명령어를 실행해도 핵심적인 메시지를 볼 수 없기 때문이에요.
또한, 클러스터의 메트릭을 실시간으로 확인할 수 있는 모니터링 도구(예: Prometheus, Grafana)가 연결되어 있다면 매우 유리해요. 로그 파일만으로는 파악하기 어려운 CPU 사용량 급증이나 메모리 누수 같은 리소스 문제를 시각적으로 즉시 잡아낼 수 있거든요.
배포 전략에 따른 상태 이해
디플로이먼트가 어떤 방식으로 업데이트를 수행하도록 설정되었는지 아는 것도 매우 중요해요. 설정된 전략에 따라 장애가 발생했을 때의 파드 상태가 완전히 달라지기 때문입니다.
| 배포 전략 유형 | 작동 방식 | 장애 발생 시 특징 |
|---|---|---|
| RollingUpdate | 기존 파드를 하나씩 제거하고 새 파드를 점진적으로 추가함 | 일부 파드는 정상이나 일부만 장애인 상태가 지속됨 |
| Recreate | 모든 기존 파드를 먼저 종료한 후 새 파드를 생성함 | 서비스가 완전히 중단된 후 새 파드가 뜨기를 기다려야 함 |
| OnDelete | 사용자가 수동으로 파드를 삭제할 때만 새 파드가 생성됨 | 배포 명령을 내려도 파드 상태에 즉각적인 변화가 없음 |
위 표에서 볼 수 있듯이, RollingUpdate 전략을 사용 중이라면 장애가 발생했을 때 일부 트래픽은 성공하고 일부는 실패하는 혼합된 상태가 나타날 수 있다는 점을 염두에 두어야 해요.
디플로이먼트는 직접 파드를 관리하지 않아요. 디플로이먼트는 레플리카셋(ReplicaSet)을 만들고, 레플리카셋이 실제 파드를 생성하고 유지하는 구조예요. 따라서 문제가 발생했을 때 레플리카셋의 상태도 함께 확인하는 습관이 필요합니다.
체계적인 장애 진단과 원인 분석 단계
문제가 발생했다면 이제부터는 논리적인 추적을 시작해야 합니다. 무작정 모든 것을 뒤지는 것이 아니라, 계층 구조를 따라 내려가는 방식으로 접근하면 원인을 훨씬 빠르게 좁힐 수 있어요. 디플로이먼트에서 시작하여 레플리카셋, 파드, 그리고 컨테이너 내부로 파고드는 5단계 프로세스를 소개할게요.
STEP 1. 디플로이먼트와 파드의 상태 확인
가장 먼저 해야 할 일은 현재 클러스터의 전체적인 상태를 훑어보는 것이에요. kubectl get deployments와 kubectl get pods 명령어를 사용하여 어떤 객체가 문제가 되고 있는지 확인하세요. 이때 단순히 이름만 보는 것이 아니라 STATUS 열을 유심히 관찰해야 합니다.
파드의 상태가 Pending이라면 리소스를 할당받지 못하고 있는 상태이고, CrashLoopBackOff라면 컨테이너가 실행된 직후 계속 종료되고 있다는 뜻이에요. ImagePullBackOff는 컨테이너 이미지를 가져오는 과정에서 문제가 생겼음을 의미하죠. 이 상태값들은 문제의 방향을 알려주는 아주 중요한 이정표가 됩니다.
STEP 2. 이벤트 로그를 통한 환경 문제 진단
파드의 상태를 확인했다면, 이제 왜 그런 상태가 되었는지에 대한 ‘이유’를 찾아야 합니다. kubectl describe pod [파드이름] 명령어를 실행하여 하단의 Events 섹션을 확인하세요. 이곳에는 쿠버네티스 컨트롤 플레인이 기록한 시스템적인 기록들이 남아 있습니다.
예를 들어, 노드에 자원이 부족하여 파드를 배치할 수 없다면 FailedScheduling 이벤트가 나타날 거예요. 혹은 스토리지 볼륨을 연결하지 못했다면 FailedMount 메시지를 볼 수 있습니다. 이 단계에서는 애플리케이션의 코드가 아니라, 인프라와 쿠버네티스 설정 간의 충돌을 찾아내는 것이 목표입니다.
STEP 3. 컨테이너 내부 로그 분석
만약 파드 상태가 Running인데 애플리케이션이 정상적으로 동작하지 않거나, 계속해서 CrashLoopBackOff가 발생한다면 이제 시선을 컨테이너 내부로 돌려야 합니다. kubectl logs [파드이름] 명령어를 통해 애플리케이션이 남긴 표준 출력(stdout)과 표준 에러(stderr)를 확인하세요.
여기서 주의할 점은, 파드가 이미 종료되어 로그를 볼 수 없을 때가 있다는 것입니다. 이럴 때는 kubectl logs [파드이름] --previous 옵션을 사용해 보세요. 직전에 종료된 컨테이너가 남긴 마지막 메시지를 확인할 수 있어, 애플리케이션이 왜 죽었는지(예: 설정 파일 누락, DB 연결 실패 등)를 파악하는 데 결정적인 단서를 제공합니다.
로그에 아무런 내용이 찍히지 않는다면, 애플리케이션이 로그를 파일 시스템에만 쓰고 있는 것은 아닌지 확인해야 해요. 쿠버네티스 환경에서는 로그를 반드시 표준 출력(stdout)으로 내보내도록 설정해야 합니다.
STEP 4. 리소스 제한 및 OOM 확인
애플리케이션 로그에 특별한 에러가 없는데도 파드가 자꾸 죽는다면, OOMKilled(Out Of Memory Killed)를 의심해야 합니다. 이는 컨테이너가 설정된 메모리 제한(limit)을 초과하여 사용했을 때 커널이 프로세스를 강제로 종료시킨 상황이에요.
이 경우 kubectl describe pod에서 상태가 Terminated이며 원인이 OOMKilled로 표시되는지 확인하세요. 해결을 위해서는 메모리 제한 값을 높여주거나, 애플리케이션의 메모리 누수(Memory Leak) 문제를 해결해야 합니다. CPU 문제의 경우 파드가 죽지는 않지만 성능이 급격히 저하되는 현상으로 나타나니, CPU Throttling 여부도 함께 검토하는 것이 좋습니다.
STEP 5. 네트워크 및 서비스 연결성 검증
파드는 정상적으로 떠 있는데 외부에서 접속이 안 된다면, 이제 네트워크 계층을 점검해야 합니다. 디플로이먼트의 문제가 아니라 서비스(Service)나 인그레스(Ingress)의 문제일 가능성이 높기 때문이죠. kubectl get endpoints 명령어를 통해 서비스가 파드의 IP를 올바르게 가리키고 있는지 확인하세요. 만약 Endpoint 리스트가 비어 있다면, 서비스의 Selector 설정이 디플로이먼트의 Label과 일치하지 않는 것이 가장 흔한 원인입니다.
네트워크 점검 시에는 파드 내부로 직접 들어가 보는 방법도 유용해요.
kubectl exec -it [파드이름] -- /bin/sh를 통해 접속한 뒤, curl이나 telnet 명령어로 대상 서버에 통신이 되는지 직접 테스트해 보는 것이 가장 확실합니다.이러한 5단계 과정을 통해 우리는 장애의 범위를 인프라 → 쿠버네티스 설정 → 컨테이너 런타임 → 애플리케이션 코드 순으로 논리적으로 좁혀 나갈 수 있습니다.
자주 하는 실수와 해결법 및 자주 묻는 질문
현장에서 엔지니어들이 반복적으로 겪는 실수들을 정리했습니다. 비슷한 상황에 직면했다면 아래 내용을 먼저 체크해 보세요.
자주 하는 실수와 해결법
❌ 이미지 이름이나 태그를 잘못 입력함
왜 발생하는가: YAML 파일 작성 시 오타가 있거나, 존재하지 않는 태그(예: latest 대신 잘못된 버전 번호)를 지정했을 때 발생합니다.
✅ 해결법: kubectl describe pod의 이벤트 로그를 확인하여 ImagePullBackOff 메시지가 있는지 보고, 레지스트리에 해당 이미지가 존재하는지 다시 확인하세요.
❌ ConfigMap이나 Secret을 참조하지 못함
왜 발생하는가: 애플리케이션이 실행될 때 필요한 환경 변수나 설정 파일이 정의된 ConfigMap/Secret이 존재하지 않거나, 이름이 틀린 경우입니다.
✅ 해결법: 디플로이먼트의 envFrom 또는 volumeMounts 설정을 확인하고, 실제 해당 객체가 같은 네임스페이스에 생성되어 있는지 체크하세요.
❌ 리소스 요청(Requests)과 제한(Limits) 설정 오류
왜 발생하는가: 파드가 요구하는 리소스가 노드의 가용 자원보다 크면 파드는 계속 Pending 상태에 머물게 됩니다.
✅ 해결법: 노드의 자원 상태를 확인하고, 파드의 리소스 요청 값을 현실적인 수준으로 조정하거나 노드를 증설해야 합니다.
❌ Selector와 Label의 불일치
왜 발생하는가: 서비스(Service)가 파드를 찾지 못해 트래픽이 전달되지 않는 경우입니다.
✅ 해결법: 서비스의 selector 섹션에 적힌 라벨과 파드의 metadata.labels가 글자 하나 틀리지 않고 일치하는지 확인하세요.
❌ 포트(Port) 설정 미스매치
왜 발생하는가: 컨테이너 내부에서 앱이 듣고 있는 포트와 서비스가 전달하려는 targetPort가 다를 때 발생합니다.
✅ 해결법: 애플리케이션 설정 파일과 디플로이먼트의 containerPort, 서비스의 targetPort가 모두 동일한지 확인하세요.
자주 묻는 질문
Q. 파드가 계속해서 CrashLoopBackOff 상태인데 무엇부터 봐야 하나요?
가장 먼저 kubectl logs --previous 명령어를 사용하세요. 앱이 실행되자마자 죽는 것이라면 설정 파일 누락이나 DB 연결 실패 같은 애플리케이션 초기화 에러일 확률이 90% 이상입니다.
Q. 디플로이먼트를 업데이트했는데 이전 버전으로 되돌리고 싶어요. 어떻게 하나요?
kubectl rollout undo deployment/[디플로이먼트명] 명령어를 사용하면 즉시 이전의 안정적인 리비전(Revision)으로 롤백할 수 있습니다. 급한 상황에서는 이 명령어가 가장 빠르고 안전합니다.
Q. Pending 상태인 파드가 왜 노드에 할당되지 않을까요?
주로 두 가지 이유입니다. 첫째, 노드의 CPU나 메모리가 부족한 경우이고, 둘째는 NodeSelector나 Taints/Tolerations 설정 때문에 특정 노드에만 들어가야 하는데 조건이 맞지 않는 경우입니다. describe pod 명령어로 이벤트를 확인해 보세요.
Q. OOMKilled가 발생하면 무조건 메모리 제한을 늘려야 하나요?
꼭 그렇지는 않습니다. 만약 메모리 사용량이 일정하게 증가하다가 죽는다면 메모리 누수(Leak)일 가능성이 높으니 코드 수정을 고려해야 하고, 특정 작업 시에만 일시적으로 튀는 것이라면 리소스 제한 값을 높여주는 것이 맞습니다.
Q. 서비스(Service)를 만들었는데 외부에서 접속이 안 됩니다.
먼저 kubectl get endpoints를 쳐보세요. 엔드포인트에 파드 IP가 없다면 라벨 셀렉터 문제입니다. 만약 엔드포인트는 있는데 안 된다면 인그레스(Ingress) 설정이나 클라우드 제공업체의 로드밸런서 설정, 혹은 네트워크 보안 그룹(Security Group)을 확인해야 합니다.
안정적인 운영을 위한 최종 요약과 다음 단계
디플로이먼트 장애는 당황스럽지만, 체계적인 프로세스만 있다면 충분히 통제 가능한 문제입니다. 오늘 다룬 내용을 바탕으로 장애 대응 역량을 키워보세요.
- 장애 발생 시 상태값(Status)을 먼저 파악하여 문제의 유형을 결정하세요.
- 인프라 문제는
describe의 Events를, 앱 문제는logs를 확인하세요. - 컨테이너 종료 시에는
--previous옵션으로 이전 로그를 추적하세요. - 리소스 부족은 OOMKilled와 Pending 키워드로 진단하세요.
- 네트워크 문제는 Selector와 Endpoint 일치 여부를 가장 먼저 보세요.
- 장애 복구 후에는 반드시 롤백(Rollout Undo) 명령어를 숙지해 두세요.
장애를 겪고 나서 단순히 해결하고 끝내는 것이 아니라, 왜 그런 일이 일어났는지를 기록하는 습관이 중요합니다. 이를 통해 동일한 장애가 재발하는 것을 막을 수 있습니다.
성장을 위한 다음 단계
오늘 할 일: 현재 운영 중인 서비스의 디플로이먼트 YAML 파일에서 리소스 제한(Limits)이 적절히 설정되어 있는지 검토해 보세요.
이번 주 할 일: 테스트 환경에서 의도적으로 잘못된 이미지를 배포하거나 메모리 제한을 낮게 설정하여, 실제 장애 상황과 동일한 로그를 직접 눈으로 확인해 보세요.
실행 직전 할 일: 장애 발생 시 바로 사용할 수 있는 kubectl 명령어 모음(Cheat Sheet)을 개인 메모장에 정리해 두세요.
실습 환경에서 직접 적용해 보고, 분석 과정에서 막히는 부분이 있다면 언제든 댓글로 질문을 남겨 주세요. 함께 해결 방법을 고민해 보겠습니다.
함께 읽으면 좋은 글:
– 쿠버네티스 디플로이먼트 기본 개념 정리
– 클러스터 구축 및 초기 설정 입문 가이드