K8s 망가뜨리기 — 범인은 내 YAML · 세 사건을 복구하고 보고하기 · 실습
범인은 내 YAML — 세 사건의 장애 노트
목표
실제 k3s 안에서 선택자 불일치·잘못된 readiness 경로·프로세스 종료를 만들고,
관측 근거를 저장한 다음 최소 변경으로 복구합니다. kubectl get/apply와 YAML의
기본 구조를 아는 중급 학습자를 위한 50분 실험입니다.
왜 중요한가
Running과 Ready와 사용자 요청의 성공은 서로 다른 사실입니다. 정상 Pod가 있어도
Service 선택자나 포트가 틀리면 고객은 실패를 겪습니다. 반대로 프로세스는 살아
있지만 준비 조건을 만족하지 못해 전달 대상에서 제외될 수도 있습니다. 한 번에
필드 하나를 바꾸고 전후를 비교하면 재시작을 반복하는 대신 원인을 설명할 수 있습니다.
세 사건을 구분한 뒤 확인한 정상 리비전으로 돌아오는 것이 이번 실습의 범위입니다.
안전 범위
임시 VM 안의 API https://127.0.0.1:6443과 labhub-mystery 네임스페이스만 사용합니다.
운영 클러스터나 다른 수강생의 환경에서는 실행하지 마세요. 제공 앱은 단일 replica와
Recreate를 사용해 이전 정상 Pod가 장애를 가리지 않게 했습니다. 무중단 운영 권장
설정이라는 뜻은 아닙니다. 앱은 이미지 digest로 고정되어 있습니다.
세션이 종료되면 VM과 관측 파일은 사라집니다. 필요하면 만료 전에 시간을 연장하고
관측을 따로 보관하세요. 관측 파일은 편집 가능한 실험 노트이지 위조 불가능한 증거가 아닙니다.
단계
1. 제공된 /opt/fixtures/k8s-mystery-app.json을 kubectl apply로 적용하세요. labhub-mystery 네임스페이스의 parcel Deployment·Service를 조회하고 Pod와 Service 양쪽의 8080번 / 경로가 본문 labhub-mystery-v1을 반환하는 정상 상태를 baseline으로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe baseline /root/k8s-mystery/01-baseline.json.
2. parcel Service의 selector만 app=missing으로 바꾸세요. Pod 라벨과 Deployment는 유지합니다. Pod는 Ready이고 직접 응답하지만 Service의 선택된 endpoint가 0이고 요청은 실패하는 상태를 selector-broken으로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe selector-broken /root/k8s-mystery/02-selector.json.
3. Service selector를 app=parcel로 복구하세요. 선택된 Ready endpoint 1개와 Pod·Service 양쪽의 정상 HTTP를 확인해 selector-restored로 기록하세요. Deployment를 삭제해 다시 만들지 않습니다. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe selector-restored /root/k8s-mystery/03-selector-fixed.json.
4. Deployment의 parcel 컨테이너 readinessProbe.httpGet.path만 /missing으로 바꾸세요. 새 Pod는 Running이고 /에 직접 응답하지만 Ready=false, 재시작 0, 현재 Pod의 404 이벤트, Ready endpoint 0인 상태를 readiness-broken으로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe readiness-broken /root/k8s-mystery/04-readiness.json.
5. 같은 readiness 경로를 /로 복구하세요. 프로브를 지우거나 항상 성공하는 exec로 교체하지 않습니다. Pod·Service의 정상 HTTP와 Ready endpoint 1개를 readiness-restored로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe readiness-restored /root/k8s-mystery/05-readiness-fixed.json.
6. 정상 Deployment 리비전을 조회해 Deployment annotation labhub.io/known-good-revision에 저장하세요. 그 뒤 parcel의 command를 ["sh","-ec","echo intentional-exit-17 >&2; exit 17"]로 교체합니다. CrashLoopBackOff·직전 종료 코드 17·재시작 1 이상·직전 로그의 intentional-exit-17을 확인하고 crash로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe crash /root/k8s-mystery/06-crash.json.
7. rollout history와 저장한 labhub.io/known-good-revision을 확인하고 그 리비전으로 rollout undo하세요. 기존 Deployment를 유지한 채 정상 명령·프로브 /·Service selector app=parcel·targetPort 8080 및 양쪽 HTTP가 복구된 상태를 rollback으로 기록하세요. 기록 명령: python3 /opt/fixtures/k8s_mystery.py observe rollback /root/k8s-mystery/07-rollback.json.
8. 앞 7개 관측 파일을 유지하고 /root/k8s-mystery/report.json에 selector·readiness·crash 사건별 cause·prevention·evidence를 연결하세요. 원인/예방 코드는 아래 참고에 있으며 evidence는 장애 당시 파일명입니다. 현재 Service도 정상이어야 합니다. 마지막에 전체 채점을 눌러 과거의 관측과 현재 복구를 함께 확인하세요.
참고
관측 도구의 observe는 조회와 파일 저장만 합니다. 요청한 장애를 대신 주입하거나
복구하지 않습니다. 45초 안에 조건을 못 보면 값과 이벤트를 확인하고 다시 관측하세요.
앞 7단계의 채점은 저장한 기록을 읽으므로 복구 후에도 과거 단계가 취소되지 않습니다.
마지막 채점은 동일 Deployment의 기록 연결과 현재 Pod·Service HTTP를 두 차례 확인합니다.
- 선택자 사건: service-selector / compare-selector-labels — 선택자와 Pod 라벨 사전 대조.
- 프로브 사건: readiness-path / probe-real-endpoint — 실제 제공하는 건강 확인 경로 검사.
- 종료 사건: process-command / smoke-test-command — 컨테이너 시작 명령의 작은 실행 시험.
kubectl -n labhub-mystery get pods,svc,endpointslices -o wide로 대상과 상태를 나누어 보세요.kubectl -n labhub-mystery describe pod <이름>의 이벤트는 현재 Pod UID와 연결하세요.kubectl -n labhub-mystery logs <이름> --previous는 직전 컨테이너 로그입니다.- 프로브 삭제, Deployment 재생성, 숫자를 외운 롤백은 문제의 계약을 피하는 행동입니다.
단계 8개
- 택배가 정상 도착하는 기준선
- 택배차는 살아 있는데 배송지가 없다
- 배송 명단만 바로잡기
- 없는 건강검진실 때문에 배달 중지
- 검진 계약을 실제 경로에 맞추기
- 택배 기사가 17번 메모를 남기고 퇴근
- 기억이 아니라 확인한 리비전으로 돌아가기
- 초록불이 아닌 근거로 사건 종결