LabHub
배우기 러닝패스 코스

CKAD — Kubernetes Application Developer

Between Configuration and Behaviour

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

프로브·Job·QoS·설정 주입은 전부 kubelet 이 하는 일입니다. kubelet 이 없으면 YAML 을 쓰는 연습은 되지만 그것이 무엇을 하는지는 끝까지 못 봅니다.

Concept map: 그 중 어느 것도 실제로 일어나지 않습니다. · 계속 미룬다 · 다시 만든다 · 엔드포인트에서 뺀다

왜 동작으로 봐야 하나

CKAD 는 "애플리케이션이 클러스터 위에서 어떻게 살아가는가" 를 묻습니다. 프로브, Job, QoS, 설정 주입이 전부 그 이야기입니다.

그런데 앞 모듈들이 도는 가짜 클러스터에는 kubelet 이 없어서 그 중 어느 것도 실제로 일어나지 않습니다. 프로브를 걸어도 돌지 않고, Job 을 만들어도 완료되지 않으며, ConfigMap 은 마운트되지 않습니다.

프로브 세 가지의 관계

프로브 묻는 것 실패하면
startupProbe 기동이 끝났나 나머지 둘을 계속 미룬다
livenessProbe 살아 있나 컨테이너를 다시 만든다
readinessProbe 요청을 받을 수 있나 엔드포인트에서 뺀다

startupProbe 가 성공할 때까지 나머지 둘은 아예 시작하지 않습니다. 이것이 없으면 선택지가 둘뿐입니다.

Job 의 두 값

completions몇 번 성공해야 하나, parallelism몇 개를 동시에 돌리나 입니다. 헷갈리면 Job 이 영영 안 끝나거나 필요 이상으로 자원을 씁니다.

backoffLimit 은 재시도 간격이 지수적으로 늘어나므로, 기본값 6 이면 포기하기까지 몇 분이 걸립니다. 그 사이 실패한 파드가 계속 쌓입니다.

QoS 는 지정하는 것이 아니다

qosClass 필드를 직접 쓸 수 없습니다. requestslimits 의 조합으로 kubelet 이 매깁니다. 그리고 노드에 메모리가 모자라면 BestEffort 부터 쫓겨납니다.

설정 주입은 언제 반영되나

ConfigMap 과 Secret 을 바꾸면 파드가 바로 그것을 보느냐는, 어떻게 주입했느냐로 갈린다. 환경 변수로 넣은 값은 컨테이너가 시작할 때 한 번 복사되므로 이후 변경이 절대 반영되지 않는다. 파드를 다시 만들어야 한다. 반면 볼륨으로 마운트한 파일은 kubelet 이 주기적으로 갱신해 주므로 잠시 뒤에 새 내용으로 바뀌지만, 애플리케이션이 그 파일을 다시 읽지 않으면 아무 소용이 없다.

그래서 실무에서 자주 쓰는 방법은 두 가지다. 설정 내용의 해시를 파드 템플릿의 주석에 넣어, 설정이 바뀌면 템플릿도 바뀌어 롤링 업데이트가 저절로 일어나게 하는 것. 또는 애플리케이션이 파일 변경을 감지해 다시 읽게 만드는 것. 앞의 것이 훨씬 단순하고 되돌리기도 쉬워서 대부분의 경우에 낫다.

한 가지 더 알아 둘 것은 없는 것을 참조했을 때의 차이다. 환경 변수로 없는 ConfigMap 을 참조하면 컨테이너를 만들지 못해 CreateContainerConfigError 가 뜨지만, 볼륨으로 참조하면 마운트가 끝나지 않아 파드가 ContainerCreating 에서 멈춘다. 같은 오타인데 증상이 다르므로, 이 대응을 알아 두면 조사 시간이 크게 줄어든다.

프로브 값은 무엇을 보고 정하나

프로브를 거는 것보다 어떤 값을 넣을지가 실제로 어렵다. 기본값을 그대로 쓰다가 서비스를 스스로 죽이는 일이 드물지 않으므로, 각 값이 무엇을 바꾸는지 알아 두어야 한다.

잘못 잡으면
periodSeconds 얼마나 자주 묻나 짧으면 앱에 부담, 길면 장애 감지가 늦다
timeoutSeconds 응답을 얼마나 기다리나 기본 1초라, 부하가 걸리면 멀쩡한 앱이 실패로 잡힌다
failureThreshold 몇 번 연속 실패하면 조치하나 1이면 일시적인 지연에도 재시작한다

가장 자주 사고를 내는 조합은 liveness 의 타임아웃이 짧은 것이다. 부하가 몰려 응답이 느려지면 프로브가 실패하고, 컨테이너가 재시작되고, 남은 파드로 부하가 몰려 그쪽도 느려지고, 결국 전부 재시작을 반복한다. 부하가 원인인 지연을 재시작으로 해결하려는 셈이라 상황이 스스로 악화된다. 그래서 liveness 는 관대하게, readiness 는 민감하게 잡는 것이 원칙이다. 트래픽에서 잠시 빠지는 것은 되돌릴 수 있지만 재시작은 되돌릴 수 없다.

무엇을 확인할지도 중요하다. liveness 는 프로세스가 스스로 회복할 수 없는 상태인지만 봐야 하므로, 의존하는 데이터베이스에 닿는지까지 확인하면 안 된다. 데이터베이스가 잠시 흔들렸을 때 애플리케이션 파드가 전부 재시작되고, 그러면 커넥션이 한꺼번에 다시 몰려 상황이 더 나빠진다. 의존성 확인은 readiness 의 몫이다. 그쪽에서 실패하면 트래픽만 빠지고, 데이터베이스가 돌아오면 조용히 복귀한다.

기동이 느린 앱에 startupProbe 를 쓸 때는 failureThreshold × periodSeconds 가 곧 허용 기동 시간이라는 점만 기억하면 된다. 30초마다 20번이면 10분까지 기다려 준다는 뜻이고, 그 안에 뜨지 못하면 그때부터 재시작이 시작된다.

실무에서 진짜 중요한 것

기동이 느린 앱에는 startupProbe 를 답으로 씁니다. 이것이 없으면 선택지가 둘뿐입니다 — liveness 를 느슨하게 해서 진짜 죽었을 때 오래 방치하거나, 촘촘하게 해서 기동 중에 죽여 영원히 재시작하거나. 세 번째 프로브가 그 딜레마를 없애려고 생겼습니다.

backoffLimit 기본값 6 은 생각보다 깁니다. 재시도 간격이 지수적으로 늘어나 포기하기까지 몇 분이 걸리고, 그 사이 실패한 파드가 계속 쌓입니다. 빨리 실패해야 하는 Job 은 값을 낮춰야 합니다.

QoS 는 쓰는 것이 아니라 매겨지는 것입니다. requestslimits 의 조합으로 kubelet 이 정하고, 노드에 메모리가 모자라면 BestEffort 부터 쫓겨납니다. 중요한 워크로드에 requests 를 안 적는 것은 "먼저 죽여도 된다" 고 적는 것과 같습니다.

다음 실습에서 이것들을 전부 동작으로 확인합니다.