Overview
Kubernetes 운영 환경에서는 노드 유지보수, 애플리케이션 롤링 업데이트, 스케일링 등 다양한 이유로 Pod 종료 및 재배치가 필요하다.
하지만 단순히 `kubectl delete pod` 만으로 처리한다면 서비스 중단이나 데이터 손실 위험이 따를 수 있다.
이 글에서는 Kubernetes에서 제공하는 `cordon, uncordon, drain, scale` 명령어를 중심으로, Pod를 안전하고 신중하게 종료하고 재시작하는 전략을 정리한다.
특히 StatefulSet과 Stateless 앱의 차이, Pod Disruption Budget(PDB), 노드 유지보수 플로우까지 실전적인 관점에서 살펴본다.
Kubernetes Pod를 안전하게 종료
Kubernetes에서 Pod 종료는 유지 관리, 업데이트 또는 확장 작업과 같은 운영 요구 사항에 따라 여러 가지 방법으로 처리할 수 있다. 다음은 `cordon, uncordon, drain, scale` 과 같은 Kubernetes 명령을 사용하여 Pod 종료를 안전하게 관리하는 방법에 대해서 알아본다.
1. kubectl cordon 사용
`cordon` 명령은 노드를 예약할 수 없음으로 표시하여 새 파드가 노드에 예약되는 것을 방지하지만 기존 파드에는 영향을 미치지 않는다. 이는 유지 관리 또는 업그레이드를 위해 노드를 준비할 때 유용하다.
kubectl cordon <node-name>
2. kubectl uncordon 사용
`uncordon` 명령은 유지 관리 또는 업데이트가 완료된 후 노드를 다시 예약 가능으로 표시하는 데 사용된다. `cordon` 명령어의 반대의 명령어다.
kubectl uncordon <node-name>
3. kubectl Drain 사용
노드를 Drain 하려면 Kubernetes의 예약 정책을 준수하면서 노드에서 모든 파드를 안전하게 제거해야 한다. 이는 시스템 업그레이드 또는 종료를 수행하기 전에 매우 중요하다. 특히 상태 저장 애플리케이션(Stateful) 의 경우 실시간 트래픽과 영구 데이터를 신중하게 처리해야 한다.
kubectl drain <node-name> --ignore-daemonsets --delete-local-data
- `--ignore-daemonsets`: 배수 명령이 DaemonSet 관리형 파드를 무시할 수 있도록 허용한다.
- `--delete-local-data`: 영구 볼륨 없이 StatefulSet에서 관리하지 않는 파드를 삭제할 경우 경고한다.
4. kubectl scale 사용
배포, StatefulSet 또는 기타 확장 가능한 리소스를 확장하려면 scale 명령어를 사용하면 된다. 이렇게 하면 복제본 수를 0으로 효과적으로 줄여 배포 또는 StatefulSet에서 관리하는 모든 Pod를 종료할 수 있다.
kubectl scale --replicas=0 deployment <deployment-name> -n <namespace>
kubectl scale --replicas=0 statefulset <statefulset-name> -n <namespace>
StatefulSet 및 Stateless 배포에서 Pod 종료
Pod를 안전하게 종료하고 다시 실행하면 되는 작업에서는 Scale 명령어를 사용해서 Replica를 0으로 만들어주는게 일반적이다.
StatefulSet
Scale down progressively(점진적으로 축소) : StatefulSet에서 복제본 수를 줄이면 가장 높은 순서대로 시작하여 파드의 제어된 종료가 트리거된다. 이는 순서대로 종료해야 하는 응용 프로그램(예: 데이터베이스)에서 중요하다.
MariaDB Galera Cluster 를 예를들자면, 2 → 1 → 0 순차적으로 Scale 후에 종료해줘야 한다.
kubectl scale --replicas=2 sts app-db -n <namespace>
kubectl scale --replicas=1 sts app-db -n <namespace>
# 첫번째로 올라오는 app-db가 가장 최신 데이터임을 말해주는 safe_to_bootstrap을 0에서 1로 변경한다.
kubectl exec -ti -n <namespace> app-db-0 -- sed -i 's/safe_to_bootstrap: 0/safe_to_bootstrap: 1
/' /var/lib/mysql/grastate.dat
kubectl scale --replicas=0 sts app-db -n <namespace>
Stateless
Direct scaling or deletion(직접 확장 또는 삭제) : Stateless 애플리케이션은 내부 상태를 유지하지 않으므로 데이터 일관성에 대한 큰 걱정 없이 축소하거나 Pod를 삭제할 수 있다.
일반적인 Stateless 애플리케이션을 사용중이라면, 아래와 같이 replicas=0으로 바로 scale 해줄 수 있다.
kubectl scale --replicas=0 deploy api -n <namespace>
kubectl rollout restart 설명
Pod를 완전히 삭제하고 scale in/out 하기보다, 기존 리소스를 유지한 채 재시작만 하고 싶을 경우 `kubectl rollout restart` 명령이 유용하다.
예를 들어, ConfigMap이 변경되었거나 애플리케이션이 비정상 동작하는 경우, 다음 명령으로 rolling restart를 수행할 수 있다.
- `kubectl rollout restart deployment <이름> -n <네임스페이스>`
- StatefulSet에도 동일하게 적용할 수 있다.
이 방식은 scale 명령보다 더 안전하며, 가동률을 유지한 채 순차적으로 Pod가 재시작된다.
추가 고려사항
- Pod 중단 예산(PDB): 제거 프로세스 중에 최소한의 Pod가 계속 실행되도록 PDB를 정의한다.
- 단계적 종료: Pod 제거 중에 Kubernetes가 이러한 신호를 보낼 때 SIGTERM 신호를 처리하여 정상적으로 종료되도록 애플리케이션을 구성한다.
- 모니터링: 특히 확장 작업 및 노드 유지 관리 중에 애플리케이션의 상태와 성능을 항상 모니터링한다.
SIGTERM 핸들링 예시
Kubernetes는 Pod를 종료할 때 먼저 SIGTERM 신호를 컨테이너에 전달하고, 일정 시간 기다린 후 강제로 종료(SIGKILL)한다.
이 과정에서 애플리케이션이 SIGTERM을 적절히 처리하지 않으면 요청 중단, 데이터 손실 등 문제가 발생할 수 있다.
Node.js나 Go, Python 등의 백엔드 애플리케이션에서는 SIGTERM 이벤트를 감지하고 종료 전에 DB 연결 해제, 파일 저장, 로깅 등을 처리하는 graceful shutdown 로직을 넣는 것이 권장된다.
또한 `terminationGracePeriodSeconds` 값을 명시적으로 설정하면, 종료 전에 애플리케이션에 정리 시간을 충분히 줄 수 있다. 이 값이 너무 짧으면 graceful shutdown이 완료되기 전에 강제 종료될 수 있으니 유의해야 한다.
Eviction 상태 확인
운영 중 drain, scale, PDB 위반 등으로 인해 Pod가 강제로 종료된 경우, `kubectl get events` 또는 `eviction` 정보를 통해 확인할 수 있다.
특히 다음 명령어는 특정 네임스페이스 또는 전체 클러스터에서 Evicted된 Pod의 기록을 보여준다.
kubectl get events --field-selector reason=Evicted -A
이 기록을 통해 어떤 리소스 부족이나 정책 위반(PDB, 메모리 부족 등)으로 종료되었는지를 파악할 수 있고, 사전 대응이 가능하다.
PDB(Pod Disruption Budget) 란?
PDB(Pod Disruption Budget)은 Kubernetes에서 자발적인 중단(voluntary disruption) 동안 지정한 수 이상의 Pod가 항상 운영 가능하도록 보장하는 정책 리소스이다.
이는 애플리케이션의 고가용성(HA) 을 유지하면서, 운영 작업(예: 노드 유지보수, 롤링 업데이트, 수동 drain 등)이 이루어질 수 있도록 돕는다.
예시 상황
- 운영자가 kubectl drain으로 노드를 유지보수
- 롤링 배포 중 replicaSet의 Pod 교체 발생
- HPA 또는 오토스케일러가 리소스 재조정 수행 중
위와 같은 자발적 조치 중 PDB가 설정되어 있으면 지정된 조건을 만족하지 않으면 Pod 제거가 제한되어 시스템 중단을 방지할 수 있다.
Voluntary vs Involuntary Disruptions
구분 | 설명 |
Voluntary | 운영자나 K8s 시스템이 예측 가능한 이유로 Pod를 중단시키는 경우 (예: cordon, drain, scale) |
Involuntary | 하드웨어 오류, Kubelet 다운, 노드 crash 등 예측 불가능한 원인으로 인한 중단 |
PDB는 voluntary disruption만 제어할 수 있으며, 물리적인 장애 같은 involuntary 중단은 방지하지 못한다.
PDB 작동 방식
- MinAvailable : 중단 중에도 항상 작동해야 하는 최소 가용 파드 수이다.
- MaxUnavailable : 중단 중에 사용할 수 없는 최대 파드 수이다.
PDB는 voluntary disruptions(자발적 중단) 중에도 Kubernetes 시스템이 PDB에서 설정한 제한을 준수하여 애플리케이션 성능과 가용성에 큰 영향을 미치지 않도록 보장한다.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: my-app
- 라벨이 붙은 두 개 이상의 파드가 `app: my-app` 항상 작동 상태를 유지하도록 보장한다.
- 즉 DB가 Scale 되더라도 항상 2개는 유지한다는 것을 의미한다.
실무 팁
- `minAvailable` 또는 `maxUnavailable` 은 둘 중 하나만 설정한다.
- PDB는 반드시 Deployment, StatefulSet, ReplicaSet과 함께 사용해야 하며, 단일 Pod에는 적용되지 않는다.
- 잘못 설정된 PDB는 업데이트 또는 스케일링이 영원히 블로킹될 수 있으므로 주의가 필요하다.
Node 및 Pod 업그레이드 및 유지보수
Node 및 Pod를 업그레이드 및 유지 보수를 할때는 `cordon, uncordon, drain` 명령어가 사용된다.
Drain
노드 유지 관리 또는 업그레이드 : 노드에서 유지 관리를 수행해야 할 때(패치, 업데이트 또는 하드웨어 교체 등) drainPDB를 존중하면서 노드에서 모든 파드를 안전하게 제거하는 데 사용된다. 워크로드가 갑자기 중단되지 않고 가능한 경우 다른 노드로 안전하게 이동되도록 한다.
drain 관련 이슈 예방법
kubectl drain 명령은 기본적으로 노드에서 Pod를 모두 제거하지만, 다음과 같은 경우 예외나 오류가 발생할 수 있다.
- DaemonSet으로 관리되는 Pod는 drain 대상이 아니다.
이 경우 `--ignore-daemonsets` 옵션을 반드시 명시해야 하며, drain 후에도 해당 Pod는 노드에 남아있다. drain 도중 실패 원인 중 가장 흔한 케이스이다.
- Local Volume을 사용하는 Pod는 drain 시 오류가 발생할 수 있다.
`hostPath, local-path, emptyDir` 와 같이 노드 고유 디스크를 사용하는 Pod는 데이터를 잃을 수 있기 때문에 drain이 기본적으로 중단된다.
이때는 `--delete-local-data` 옵션을 함께 명시해야 drain이 강제 진행된다.
- emptyDir 볼륨도 drain의 차단 조건에 해당한다.
emptyDir는 Pod가 삭제되면 함께 사라지기 때문에, 일반적으로 drain 시 문제가 되지 않지만, 특정 K8s 설정이나 drain 전략에 따라 경고가 발생할 수 있다.
`--delete-emptydir-data=true` (v1.30 이후 지원) 옵션을 사용하면 drain 시 emptyDir을 사용하는 Pod도 무시하고 삭제할 수 있다.
- PDB(Pod Disruption Budget) 위반 시 drain은 실패한다.
PDB가 설정된 Deployment 또는 StatefulSet에서 허용 가능한 최소 파드 수가 유지되지 않으면 drain은 중단된다.
이를 방지하기 위해 사전에 `kubectl get pdb` 로 가용 수량을 확인하는 것이 중요하다
Tip: drain이 반복 실패하거나 정책과 충돌할 때는 `kubectl drain --force --grace-period=0` 같은 강제 옵션을 쓰기보다 원인 분석 후 수동 처리하는 것이 더 안전하다.
Cordon & Uncordon
유지 관리를 위한 노드 준비 : 파드를 즉시 Evicted 하지 않고 유지보수를 위해 노드를 준비할 때 `cordon` 을 사용하여 노드를 예약 불가능으로 표시한다. 이는 노드에서 새로운 파드가 예약되는 것을 방지하지만 기존 파드에는 영향을 미치지 않는다.
노드를 서비스에 반환 : 유지보수가 끝나면 `uncordon` 을 사용하여 노드 예약 가능을 다시 표시하여 노드에서 파드를 예약할 수 있도록 한다.
정리
`kubectl drain` 명령에는 암시적으로 `cordon` 작업이 포함된다. 노드에서 `drain` 을 사용하면 Kubernetes는 먼저 노드를 예약할 수 없는 것으로 표시하여(cordon이 수행하는 작업) 해당 노드에 새 Pod가 예약되지 않도록 한다. 그런 다음 PDB(Pod Disruption Budget) 및 종료 유예 기간 준수 등 지정된 정책에 따라 노드에서 모든 Pod를 안전하게 제거한다.
kubectl Drain을 사용할 때의 일반적인 작업 흐름은 다음과 같다.
- 노드 차단: 새 파드가 예약되지 않도록 노드를 예약 불가능으로 자동 설정한다.
- Evicted Pods: 노드에서 모든 Pod를 안전하게 제거한다. DaemonSet에 의해 제어되는 Pod가 있으면 이동되지 않는다. 대신, DaemonSet가 이를 허용하도록 구성된 경우 삭제된다.
- 파드 종료 대기: 달리 지정하지 않는 한 이 명령은 명령줄을 해제하기 전에 모든 파드가 안전하게 종료될 때까지 기다린다.
즉, `drain` 또는 `cordon` 사용 이후 모두 유지 관리가 완료되고 노드가 클러스터에 다시 참여할 준비가 되면 `uncordon` 명령을 실행하여 노드를 다시 예약 가능하게 만들어야 한다.
마무리: 운영자라면 꼭 알아야 할 Pod 종료 전략
Kubernetes의 강력한 스케줄링 기능은 자동 복구뿐 아니라 안전한 종료와 재배치에도 큰 역할을 한다.
단순히 리소스를 제거하거나 재시작하는 것이 아닌, 다음과 같은 포인트를 반드시 고려해야 한다.
- 서비스 중단 없이 점진적 종료 (StatefulSet, PDB 고려)
- 노드 유지보수 전 `drain + cordon`, 복귀 시 `uncordon`
- 애플리케이션의 SIGTERM 처리 로직 구현 여부
- DaemonSet / Local volume 사용 여부
운영자가 `cordon → drain → scale → uncordon` 흐름을 이해하고 상황에 맞게 적용한다면, 무중단 배포와 유지보수는 물론 장애 대응에도 한층 능숙해질 수 있다.
단순한 리소스 삭제가 아닌, 신중하고 체계적인 Pod Lifecycle 관리가 진짜 실무 역량이다.
Reference
'Container Orchestration > Kubernetes' 카테고리의 다른 글
Kubernetes Garbage Collection (0) | 2024.08.30 |
---|---|
Kubernetes 생태계 표준화와 Container Interface(CRI, CSI, CNI) (0) | 2024.07.15 |
Ingress Nginx란? (0) | 2024.07.04 |
Kubernetes Volumes 및 StorageClass: CSI 드라이버 사용 가이드 (0) | 2024.04.28 |
Kubernetes Affinity 및 Scheduling 설정 가이드 (0) | 2024.04.19 |