K8s 망가뜨리기 — 범인은 내 YAML · 세 사건을 복구하고 보고하기 · 이론
세 사건을 복구하고 보고하기
한 줄 요약
롤백 명령의 성공이 복구의 끝은 아닙니다. 돌아갈 정상 버전을 확인하고 실제 요청을 다시 보내야 합니다.
왜 이게 필요했나
컨테이너 명령의 작은 변경이 앱을 시작하자마자 종료시켰습니다. 새 파드는 계속 재시작하고, 담당자는 방금 전 버전으로 돌아가려고 합니다. 그런데 “이전 버전”이 정말 정상이었을까요? 직전 실험에서 readiness가 잘못된 템플릿을 만들었다면 단순히 한 단계 뒤로 가는 것이 또 다른 장애로 돌아가는 길일 수 있습니다. 복구 목표는 기억이나 순번이 아니라 확인된 정상 상태로 정해야 합니다.
이 실습에서는 정상 readiness 복구를 확인한 뒤 그 Deployment 리비전을 기록합니다. 다음으로 명령을 의도적인 종료 코드 17을 내는 짧은 셸로 바꿉니다. 17은 이 교육 시나리오의 표식이지 쿠버네티스가 정한 공통 오류 코드가 아닙니다. 종료 코드가 다르거나 ImagePullBackOff라면 이번에 의도한 장애와 다른 원인이므로 같은 답으로 취급하지 않습니다.
어떻게 동작하나
Deployment 리비전은 Pod 템플릿이 바뀌어 롤아웃이 일어날 때 갱신됩니다. Service 선택자 변경은 Deployment 템플릿 변경이 아닙니다. replica 수만 바꾸는 것도 새로운 Pod 템플릿을 만드는 것과 다릅니다. 따라서 “kubectl 명령 한 번마다 리비전이 하나 증가한다”는 규칙을 만들어 외우면 틀립니다. rollout history와 각 리비전의 내용을 조회해 무엇이 달라졌는지 확인하세요.
정상 템플릿 확인 → 정상 revision 기록명령 변경 → 프로세스 종료 → 재시작과 backoff증거 보존 → 원인·복구 목표 선택정상 revision 지정 rollback현재 Pod·Ready Endpoint·실제 HTTP 다시 확인CrashLoopBackOff는 재시작을 기다리는 상태를 나타내는 단서입니다. 그 단어만으로 OOM인지 잘못된 명령인지 설정 파일 누락인지 알 수 없습니다. lastState.terminated의 exitCode와 reason, restartCount, 이전 컨테이너 로그를 함께 읽습니다. 이번 명령은 stderr에 intentional-exit-17을 남기고 17로 종료합니다. 이전 실행 로그에 그 표식이 있고 종료 코드도 맞아야 이번 실험을 정확히 재현한 것입니다.
현재 컨테이너의 로그가 비어 있다고 앱이 아무것도 출력하지 않았다고 단정하지 마세요. 재시작 직후라면 관심 있는 로그는 이전 실행에 있을 수 있습니다. kubectl logs --previous는 이 차이를 확인할 때 쓰입니다. 다만 로그가 영구 보존되는 감사 저장소는 아닙니다. 파드 교체와 로그 보존 정책 때문에 사라질 수 있으므로 복구 전에 필요한 부분을 관측 파일에 남기는 것입니다.
롤백할 리비전을 알고 있다면 --to-revision으로 지정할 수 있습니다. 정상 Pod 템플릿으로 되돌아가더라도 Service는 별도 객체입니다. Service targetPort가 여전히 9999라면 파드가 Ready로 돌아와도 Service 요청은 계속 실패할 수 있습니다. 그래서 마지막 채점은 보고서 파일이나 Deployment 상태만 검사하지 않습니다. 실제 Pod IP와 Service IP로 요청을 두 차례 보내 정해진 응답을 확인합니다.
복구 성공을 확인하는 요청도 무엇을 검증했는지 분명해야 합니다. 이 코스의 GET /는 데이터베이스나 외부 결제를 사용하지 않는 작은 HTTP 응답입니다. 운영의 복구 판정에서는 대표 업무 경로, 오류율, 지연, 의존성, 데이터 정합성을 함께 확인해야 할 수 있습니다. 우리의 성공 응답은 이 작은 서비스의 연결 경로가 회복됐다는 근거이며 모든 고객 업무의 정상화를 보증하지 않습니다.
현장에서 만나는 모습
장애 보고서는 명령어 실행 일지가 아닙니다. 어떤 영향이 있었는지, 어느 계층을 의심했고 무엇으로 좁혔는지, 무엇을 바꿨으며 어떤 요청으로 복구를 확인했는지가 연결돼야 합니다. “파드를 재시작하니 해결”이라는 기록은 다음 장애 때 같은 행동을 반복하게 하지만, 선택자 불일치였다는 증거가 있으면 배포 전 선택자와 라벨을 비교하는 예방 검사를 만들 수 있습니다.
이번 보고서는 selector·readiness·crash 세 사건에 대해 cause·prevention·evidence를 연결하는 JSON입니다. 자유 산문의 문체를 기계가 점수 매기지 않고, 학습해야 할 원인 구분과 근거 연결을 명시적으로 검사합니다. service-selector에는 compare-selector-labels, readiness-path에는 probe-real-endpoint, process-command에는 smoke-test-command라는 예방 코드를 사용합니다. 지시문에 코드의 뜻이 나와 있으므로 문자열을 추측하는 과제가 아닙니다.
그렇다고 보고서의 코드가 맞으면 실험을 했다는 강한 증명이 되는 것은 아닙니다. 학습자는 자신의 VM에서 root이고 관측 JSON을 편집할 수 있습니다. 저장된 관측은 교육용 실험 노트이며 위조 불가능한 인증서가 아닙니다. 마지막 단계의 현재 HTTP 검증은 적어도 파일만 정상으로 꾸미고 서비스를 고치지 않은 경우를 거절하기 위한 것입니다. 시험의 신뢰 경계를 솔직하게 설명하는 것도 운영 문서의 품질입니다.
같은 서비스가 시간에 따라 정상→고장→복구를 오가므로 모든 단계가 현재 상태만 보면 문제가 생깁니다. 복구 후 전체 채점을 누를 때 “고장 상태를 만들라”던 과거 단계가 실패하는 것입니다. 이 코스는 앞 7단계의 관측을 각각 파일로 보존하고, 마지막 단계에서 그 연결과 현재 복구를 함께 봅니다. 관측 명령은 학생이 실행하는 기록 도구이고 채점기는 그 파일을 수정하거나 장애를 주입하지 않습니다.
예방책도 테스트로 확인해야 합니다. 포트가 잘못된 Service를 일부러 넣었는데 정상 판정이 그대로 통과한다면 검사는 아무것도 지키지 못합니다. 정상 예제 하나가 통과한다는 사실은 “무조건 통과”하는 검사에서도 참입니다. 작성자는 정상 정답뿐 아니라 선택자만 고친 채 프로브가 틀린 상태, 종료 코드가 다른 상태, Ready지만 Service HTTP가 실패하는 상태를 거절하는지도 확인해야 합니다.
다음 실습에서 할 것
8단계 동안 정상 기준과 세 종류의 장애·복구를 관측하고, 마지막에 원인·예방·근거를 묶습니다. 마지막 성공 후 전체 채점을 다시 실행해 과거의 증거가 보존되는지 확인하세요. 실습은 약 50분이며 임시 VM입니다. 시간이 더 필요하면 세션 만료 전에 연장하고, 중요한 관측은 세션 종료 전에 따로 보관하세요. 이 코스의 실험을 운영 클러스터에 그대로 복사하지 마세요.