LabHub
배우기 러닝패스 코스

CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가) · Synced인데 서비스는 왜 멈췄을까 · 이론

초록불 하나로 배포를 끝내지 않기

LabHub 에서 이어서 보기

한 줄 요약

배포 완료는 하나의 상태값이 아니라 **의도한 커밋, 반영된 리소스, 준비된 프로세스,
실제 요청 결과가 서로 맞는지** 확인한 결론입니다. GitOps는 잘못된 Git도 충실하게
반영합니다. 따라서 Git과 일치한다는 것과 사용자가 서비스를 쓸 수 있다는 것은 다릅니다.

왜 이게 필요했나

가상의 플랫폼 팀이 주문 API의 readiness 경로를 바꿨습니다. 문법 검사와 API 서버의
검증은 통과했습니다. Argo CD 화면에는 Synced가 표시됐지만 새 파드는 준비되지 않았고,
Service에서 준비된 엔드포인트가 사라졌습니다. 이때 “Git과 같으니 네트워크 문제겠지”라고
추측하면 방금 배포한 잘못을 놓칩니다. 반대로 모든 장애에 롤백부터 하면 데이터 변경이나
외부 의존성 문제를 더 복잡하게 만들 수 있습니다. 먼저 무엇이 바뀌었는지 증거를 모읍니다.

GitOps 컨트롤러는 업무 정답을 아는 심판이 아닙니다. 선언된 상태를 적용하고 관찰하는
컨트롤러입니다. 애플리케이션의 건강 판단도 리소스 종류별 규칙에 의존합니다.
Healthy가 표시돼도 잘못된 가격을 반환하거나 특정 사용자만 실패하는 API는 남을 수
있습니다. 학습 환경의 간단한 HTTP 검사 역시 실제 서비스의 모든 기능을 증명하지 않습니다.

어떻게 동작하나

네 층을 연결해서 본다

| 질문 | 확인할 증거 | 이것만으로 알 수 없는 것 |
| --- | --- | --- |
| 무엇을 배포하려 했나 | Application의 targetRevision, Git 커밋과 변경 내역 | 실제 반영 여부 |
| 그 커밋과 같나 | status.sync.revision과 Synced | 파드 준비·요청 성공 |
| 컨트롤러가 준비됐다고 보나 | health, Deployment 세대·레플리카·조건 | 업무 응답의 정확성 |
| 요청이 실제로 되나 | Service 경유 HTTP 상태·기대한 본문 | 모든 경로·사용자·부하의 성공 |

실습용 단일 레플리카 Deployment라면 원하는 레플리카가 1인지부터 확인합니다.
현재 세대와 observedGeneration이 다르면 컨트롤러가 아직 새 선언을 관찰하지 않았을
수 있습니다. updated·ready·available을 함께 보고, Service의 EndpointSlice에
ready인 엔드포인트가 있는지 확인합니다. 빈 JSON이나 누락된 필드를 0 또는 성공으로
편하게 바꾸면 장애를 숨길 수 있습니다. 필요한 정보가 없을 때는 판정을 보류하거나 실패로
처리하고, 왜 부족한지 표시해야 합니다.

다음은 자신이 만든 격리 실습 환경에서의 진단 명령 예시입니다. demo는 실제
Application 이름과 네임스페이스로 바꿉니다. 운영 환경을 일부러 고장 내는 명령이 아닙니다.

kubectl -n argocd get application demo -o jsonkubectl -n demo get deployment web -o jsonkubectl -n demo get endpointslices -l kubernetes.io/service-name=web -o jsonkubectl -n demo describe pod -l app=web

이 출력으로 최근 조건과 이벤트를 읽은 뒤 실제 요청을 보냅니다. VM 안에서 ClusterIP로
보낸 요청은 그 경로만 검증합니다. 외부 사용자는 DNS·게이트웨이·TLS·인증을 거칠 수
있으므로 운영에서는 사용자와 같은 경로의 관측도 필요합니다. kubectl exec로 파드
자신에게 보낸 요청만 성공한 것을 Service나 외부 경로의 성공으로 바꾸어 말하지 않습니다.

같은 Synced에서 정반대 결과가 나온다

정상 nginx의 readiness가 /를 검사한다고 가정합시다. 이를 존재하지 않는
/not-ready로 바꿔 Git에 커밋합니다. YAML은 유효하고 컨테이너 프로세스도 실행되지만,
HTTP 프로브는 실패합니다. 새 선언이 반영돼 Synced가 돼도 파드는 Ready가 아닙니다.
진행 기한을 넘기면 Deployment의 Progressing 조건과 그 사유를 확인할 수 있습니다.

전용 재현 환경에서는 차이를 뚜렷하게 보기 위해 레플리카 1과 Recreate 전략을 사용할
수 있습니다. 이는 가용성 권장 설정이 아니라 고장 관측을 위한 의도적인 조건입니다.
RollingUpdate에서 이전 파드가 계속 서비스를 제공한다면 새 파드가 실패해도 HTTP가
여전히 200일 수 있습니다. 그래서 “오류 프로브를 배포하면 항상 전체 서비스가 죽는다”도
틀린 일반화입니다. 전략과 남아 있는 정상 파드를 함께 봐야 합니다.

커밋 고정과 복구

main은 브랜치의 최신 커밋을 추적합니다. 특정 커밋 SHA로 고정한 Application은
브랜치에 새 커밋을 푸시했다고 그 새 커밋으로 자동 이동하지 않습니다. 이 경우 의도한
새 SHA로 targetRevision도 갱신해야 합니다. Git SHA를 고정해도 매니페스트의 이미지가
움직이는 태그라면 이미지 내용까지 고정한 것은 아닙니다. 각각의 식별 대상을 구분합니다.

stateless 설정 실수의 한 복구 방법은 나쁜 변경을 git revert로 취소한 새 커밋을
만들고 배포하는 것입니다. 복구 커밋의 내용은 이전 정상 커밋과 같을 수 있지만 SHA는
새롭습니다. 따라서 “최초 SHA와 같아야 복구 성공”이라는 판정은 적절하지 않습니다.
복구 대상으로 정한 새 SHA, 그 SHA의 트리, 클러스터의 관찰 상태와 응답을 맞춰 봅니다.

selfHeal이 켜진 Application에서 Deployment만 직접 고치는 응급 조치는 Git의
잘못된 상태로 되돌아갈 수 있습니다. 이 때문에 Git의 의도도 고쳐야 합니다. 자동 동기화
상태에서의 Argo CD rollback 명령과 Git revert 후 새 커밋 배포는 같은 작업이 아닙니다.
전자는 도구가 제공하는 롤백 동작이고, 후자는 선언 원본을 새 이력으로 고치는 방식입니다.

현장에서 만나는 모습

앞의 가상 장애라면 복구 보고서에는 정상·실패·복구 커밋을 구분해 적습니다. 각 시점의
sync revision, 건강 상태, 프로브 실패 사유, 엔드포인트, HTTP 결과를 함께 남깁니다.
장애 후 Healthy라는 캡처 하나만 남으면 장애 원인과 복구한 변경을 연결하기 어렵습니다.
민감한 요청 본문이나 토큰은 증거 파일에 넣지 않습니다.

승격 검사도 양방향으로 시험합니다. 정상 상태의 응답만 넣지 말고, 이전 커밋의 200,
Synced지만 Degraded인 상태, 엔드포인트 없는 상태, 200이지만 다른 릴리스의 본문,
필수 필드가 빠진 상태를 각각 넣어 거절되는지 확인합니다. 이것은 실제 컨트롤러를 돌리는
시험과 별도입니다. 전자는 판정 로직을, 후자는 실제 환경에서 그 상태가 발생하는지를 봅니다.

이 예는 stateless 웹 설정의 복구입니다. DB 스키마를 되돌리거나 이미 전송된 메시지를
취소하는 절차가 아닙니다. 데이터 변경이 있는 릴리스는 하위 호환성, 백업·복원, 재처리와
중복 처리까지 별도로 설계해야 합니다. 화면의 초록불로 데이터 복구를 주장하지 않습니다.

다음 실습에서 할 것

CNPE의 기존 배송 게이트 실습에서는 파일·선언 검사와 실제 실행 검사를 구분해서
읽으세요. Application 파일을 작성한 것만으로 실제 Argo CD 동기화를 수행했다고
판정하지 않습니다. 다음 VM 실습에서는 실제 Argo CD에서 정상·장애·복구를
직접 관측하고, Git 이력과 지속되는 증거 파일을 남깁니다. 마지막에는 현재 서비스까지
재검사하는 승인 코드를 만듭니다. stateless 웹 복구의 증거를 DB 복구까지 확대하지 마세요.

공식 문서로 더 확인하기