새벽에 알람이 울리지 않게 만들기
지금까지 배운 것만으로도 애플리케이션을 배포하고, 스케일링하고, 도메인으로 노출하는 데는 문제가 없습니다. 그런데 딱 여기까지만 하고 프로덕션에 올리면, 거의 예외 없이 새벽에 알람이 울립니다. 이 장에서 다루는 세 가지 — 프로브, 리소스 제한, 배포 전략 — 는 전부 "장애가 생기지 않게 막는" 게 아니라 "장애가 생겼을 때 사용자가 눈치채지 못하게 만드는" 것에 가깝습니다. 장애는 어차피 생깁니다.
liveness와 readiness, 이름은 비슷한데 하는 일이 다르다
4장에서 Running 상태가 곧 "요청을 받을 준비가 됐다"는 뜻은 아니라고 했습니다. 이 간극을 메우는 게 프로브입니다.
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5- livenessProbe가 계속 실패하면 kubelet은 이 컨테이너가 "죽었다고 보고 재시작"시킵니다. 애플리케이션이 데드락에 빠져 응답을 멈췄을 때 이걸 감지해서 강제로 되살리는 용도입니다.
- readinessProbe가 실패하면 컨테이너를 재시작하지는 않지만, Service의 대상 목록(6장의 endpoints)에서 즉시 빠집니다. 트래픽만 안 보내는 겁니다.
이 둘을 헷갈려서 readiness가 필요한 자리에 liveness만 넣는 실수가 흔합니다. 예를 들어 애플리케이션이 시작할 때 캐시를 미리 데워야 해서 30초쯤 걸린다고 합시다. liveness만 있고 readiness가 없다면, 이 30초 동안 Pod은 Running이자마자 Service 대상에 즉시 포함되어 아직 준비 안 된 상태로 트래픽을 받아버립니다. readiness를 따로 두면 이 30초 동안은 트래픽에서 빠져 있다가, 준비가 끝난 시점부터 정확히 트래픽을 받기 시작합니다.
리소스 요청량과 제한량 — 숫자를 안 적으면 생기는 일
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"requests는 스케줄러가 "이 Pod을 어느 노드에 둘지" 결정할 때 쓰는 값입니다(2장에서 본 스케줄러의 역할이 바로 이겁니다). 노드에 이 값만큼의 여유가 없으면 그 노드에는 배정되지 않습니다. limits는 실제로 컨테이너가 쓸 수 있는 상한선입니다.
이 값을 아예 안 적으면 어떻게 될까요? 스케줄러는 "이 Pod이 얼마나 필요한지 모른다"고 보고 사실상 자원을 거의 안 쓴다고 취급해버립니다. 그래서 한 노드에 실제로는 감당 못 할 만큼의 Pod이 몰려서 배정되고, 트래픽이 몰리는 시점에 노드 전체의 메모리가 바닥나면서 그 노드에 떠 있던 관련 없는 다른 서비스들까지 함께 장애를 겪는 일이 벌어집니다. 리소스 값을 적지 않는 건 "내 애플리케이션은 자원을 안 쓴다"는 거짓 선언을 하는 것과 같습니다.
반대로 limits.memory를 너무 낮게 잡으면 애플리케이션이 정상 동작 중에도 그 상한을 넘는 순간 강제 종료(OOMKilled)됩니다.
kubectl get pod my-api-xxx
# my-api-xxx 0/1 OOMKilled 3 (restarts)
kubectl describe pod my-api-xxx
# Last State: Terminated, Reason: OOMKilled이 값을 처음부터 정확히 맞추기는 어렵습니다. 실제 운영 중인 애플리케이션의 CPU·메모리 사용량을 모니터링 도구(Prometheus 등)로 관찰한 뒤 여유를 두고 조정해나가는 게 현실적인 접근입니다.
롤링 업데이트 세부 조정
5장에서 본 무중단 배포는 기본 설정으로도 동작하지만, 그 "서서히 바꾸는" 속도는 조정할 수 있습니다.
spec:
strategy:
rollingUpdate:
maxSurge: 1 # 목표 복제본 수보다 최대 몇 개까지 더 띄울 수 있는가
maxUnavailable: 0 # 배포 중 최대 몇 개까지 줄어들 수 있는가
type: RollingUpdatemaxUnavailable: 0으로 두면 배포 중 사용 가능한 Pod 수가 절대 목표치 아래로 안 내려갑니다 — 대신 항상 maxSurge만큼 여유 자원이 더 필요합니다. 반대로 maxSurge: 0, maxUnavailable: 1로 두면 여유 자원 없이도 배포할 수 있지만, 배포 중 잠깐이라도 가용 Pod 수가 줄어드는 걸 감수해야 합니다. 클러스터 자원이 빠듯하다면 후자, 트래픽이 많아 여유 Pod을 줄이면 안 되는 서비스라면 전자를 씁니다. 정답은 없고 여러분 서비스의 트래픽 패턴과 클러스터 자원 상황이 결정합니다.
readiness probe가 이 롤링 업데이트와 맞물리는 지점도 중요합니다. 새로 뜬 Pod은 readiness probe를 통과하기 전까지는 "가용한 Pod"으로 세지 않습니다. 즉 readiness probe 없이 롤링 업데이트를 하면, 아직 준비 안 된 새 Pod을 이미 가용하다고 착각하고 옛 Pod을 먼저 줄여버려서 그 사이에 실제로 트래픽을 받을 수 있는 Pod 수가 순간적으로 부족해지는 상황이 생길 수 있습니다. 이 장 첫머리의 readiness probe와 여기 롤링 업데이트 설정은 따로 노는 두 기능이 아니라 한 쌍으로 붙어 있는 안전장치입니다.