CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가)
변경과 삭제까지 책임지는 셀프서비스 API
한 줄 요약
좋은 셀프서비스는 생성 이후에도 요청과 실제 상태의 차이를 수렴시키고, 삭제 요청과 실제 회수 완료를 구분하며 다른 팀의 자원을 보존합니다.
왜 이게 필요했나
플랫폼 데모에서는 앱이 한 번 생성되면 박수를 받습니다. 운영에서는 그 뒤가 더 깁니다. 복제수를 늘리고, 새 설정을 반영하고, 사용하지 않는 환경을 지웁니다. 사용자는 “변경했습니다”라는 응답을 보지만 일부 파드는 이전 설정으로 응답할 수 있습니다. 삭제 요청은 성공했는데 비용을 발생시키는 자원이 남을 수도 있습니다. 생성만 자동화하고 나머지를 사람에게 맡기면 운영 부담은 다른 이름으로 돌아옵니다.
개발자가 하위 Deployment를 직접 고친 상황도 생각해 봅시다. App 요청은 2개인데 Deployment를 3개로 바꾸면 무엇이 원본일까요? 이 실습에서는 App이 원본입니다. 컨트롤러는 차이를 관측해 하위 자원을 다시 2개로 돌립니다. 복귀 자체가 버그가 아니라 선언한 계약의 결과입니다. 지속하려는 변경은 원본 요청에 해야 합니다.
본문이 변경 검증의 순서로 든 세 대목을 옮겼다. 하나만 보면 이전 상태나 다른 객체를 보고 속을 수 있다. parcel 과 preview 는 본문 실습의 이름이다.
- 같은 요청인가: UIDKubernetes 의 이름은 재사용할 수 있다. 지우고 같은 이름으로 다시 만들면 이름과 URL 은 같아 보여도 UID 가 다르다. 하위 Deployment 의 UID 도 연결하면 자원을 지우고 다시 만든 우회를 구분할 수 있다.
- 새 선언을 봤는가: 세대metadata.generation 은 원하는 설정의 변경을, status.observedGeneration 은 컨트롤러가 관측한 세대를 나타낸다. 이전 세대에서 Ready 였던 상태가 새 선언 직후에도 잠시 보일 수 있어서 관측 세대가 현재 세대인지 본다.
- 실제 파드가 바뀌었는가: 개수와 응답새 복제본과 가용 복제본 수가 목표와 같은지, 종료 중인 이전 파드가 남아 있는지, 현재 파드가 실제로 preview 응답을 내는지를 함께 본다.
여기서 구분할 것 셋은 서로를 대신하지 못한다. 이름이 같다는 사실이나 Ready 표시 하나로는 새 요청의 새 설정이 실제로 적용됐다고 말할 수 없다.
잠깐, 예측해 보세요 App 은 replicas=2 인데 하위 Deployment 가 3 으로 바뀌었다가 2 로 돌아온 기록이 있다. 컨트롤러가 되돌린 것인지 누가 Deployment 를 지우고 2 개짜리로 다시 만든 것인지, 이름만으로는 구분되지 않는다. 무엇으로 구분할까?
설명 확인 · 채점 없는 자가 점검
하위 Deployment 의 UID 로 구분한다. 본문은 UID 를 연결하면 자원을 지우고 다시 만든 우회를 구분할 수 있다고 말한다. 값만 되돌아간 경우에는 같은 UID 가 유지되고, 지우고 다시 만들었다면 UID 가 달라진다.
어떻게 동작하나
변경 검증은 이전과 같은 UID의 요청인지부터 시작합니다. Kubernetes의 이름은 재사용할 수 있습니다. parcel을 삭제하고 같은 이름으로 새로 만들면 URL과 이름은 같아 보여도 UID가 다릅니다. 이전 앱의 성공 기록을 새 앱의 증거로 사용하면 안 되는 이유입니다. 하위 Deployment의 UID도 함께 연결하면 자원을 지우고 다시 만든 우회를 구분할 수 있습니다.
다음은 세대입니다. Deployment의 metadata.generation은 원하는 설정의 변경을 표현하고 status.observedGeneration은 컨트롤러가 관측한 세대를 나타냅니다. 이전 세대에서 Ready였던 상태가 새 선언 직후에도 잠시 보일 수 있습니다. 그래서 관측 세대가 현재 세대인지, 새 복제본과 가용 복제본 수가 목표와 같은지, 종료 중인 이전 파드가 남아 있는지, 현재 파드가 실제 preview 응답을 내는지를 함께 확인합니다.
실험으로 하위 자원을 3개로 바꾼 뒤 최종 결과가 2개인 것만 제출하면 실제로 차이를 만들었는지 알 수 없습니다. 처음부터 2개였어도 같은 결과이기 때문입니다. 이번 실습의 실험 도우미는 API가 저장한 3개 상태의 패치 응답을 보관합니다. 그 세대보다 뒤의 세대에서 2개로 복귀했는지 확인하여 변화의 앞뒤를 연결합니다. 상태 하나와 사건의 흐름은 다른 종류의 증거입니다.
삭제에도 단계가 있습니다. 삭제 요청을 받은 API 서버는 deletionTimestamp를 표시할 수 있습니다. finalizer가 남아 있으면 객체는 곧바로 사라지지 않습니다. 이 표식은 해당 컨트롤러가 정리 절차를 마칠 기회를 주기 위한 장치입니다. 장애 대응 중 finalizer를 전부 지우면 눈앞의 객체는 없어질 수 있지만, 소유자가 책임져야 할 외부 자원이 남을 수 있습니다.
실습에서는 교육용 보류 표식 하나를 넣어 삭제 중 상태를 관찰합니다. 이 표식은 상위 요청의 부재를 늦출 뿐, 모든 하위 자원이 살아 있다고 보장하지 않습니다. 컨트롤러가 하위 자원을 먼저 회수할 수도 있으므로 매번 실제 상태를 봐야 합니다. 해제할 때는 해당 표식 하나만 제거하고, UID와 resourceVersion을 대조해 사이에 일어난 변경을 덮어쓰지 않습니다.
현장에서 만나는 모습
“조회 결과가 없다”에도 두 뜻이 있습니다. 목록 API가 정상 응답했고 대상이 없는 경우와, 인증·통신 문제로 목록을 얻지 못한 경우입니다. 후자를 빈 목록으로 바꾸면 삭제 검사기가 장애를 성공으로 보고합니다. 실습은 App·Deployment·ReplicaSet·Service·Pod 각각의 목록 조회가 성공하고 대상이 없을 때만 부재를 인정합니다.
범위도 확인해야 합니다. team-a를 정리하면서 team-b까지 삭제하면 내 앱은 확실히 사라집니다. 그러나 그것은 성공한 정리가 아닙니다. 다른 팀 sentinel의 UID와 실제 응답이 그대로인지 대조하여 필요한 자원만 회수했는지 확인합니다. 이 실습은 개인 VM 안의 팀 권한 분리를 다루며, 네임스페이스만으로 모든 네트워크·자원 격리가 완성됐다고 주장하지 않습니다.
다음 실습에서 할 것
같은 요청을 preview·2복제로 변경하고, 하위 자원의 수동 변경이 원본으로 복귀하는 것을 관측합니다. 이어서 삭제 중 상태와 실제 회수를 나누어 기록하고 다른 팀이 보존됐는지 확인합니다. 마지막에는 관측 실패와 모순을 unknown으로 남기는 작은 진단기를 작성합니다. 없는 정보에서 성공을 추정하지 않는 습관이 목표입니다.
본문이 삭제를 단계로 나누는 대목이다. 삭제 요청이 성공했다는 것과 자원이 회수됐다는 것은 다른 관측이다. finalizer 는 교육용 보류 표식을 가리킨다.
- 삭제 요청: deletionTimestamp 가 표시된다삭제 요청을 받은 API 서버는 삭제 중이라는 표시를 남길 수 있다. 요청이 받아들여졌다는 것이지 회수가 끝났다는 뜻은 아니다.
- finalizer 가 남아 있는 동안 객체는 곧바로 사라지지 않는다해당 컨트롤러가 정리 절차를 마칠 기회를 주기 위한 장치이다. 이 표식은 상위 요청의 부재를 늦출 뿐이고, 컨트롤러가 하위 자원을 먼저 회수할 수도 있어서 매번 실제 상태를 본다.
- 정리가 끝나고 표식이 풀려야 비로소 사라진다장애 대응 중 finalizer 를 전부 지우면 눈앞의 객체는 사라져도 소유자가 책임질 외부 자원이 남을 수 있다. 실습에서는 교육용 표식 하나만 풀고 UID 와 resourceVersion 을 대조한다.
여기서 구분할 것 회수가 맞게 됐는지는 범위도 확인한다. team-a 를 정리하면서 team-b 까지 지웠다면 내 앱은 확실히 사라졌어도 성공한 정리가 아니다. 다른 팀 sentinel 의 UID 와 실제 응답이 그대로인지 대조한다.
잠깐, 예측해 보세요 삭제 중인 App 을 조회하니 deletionTimestamp 와 finalizer 가 보이는데 하위 Deployment 는 이미 목록에 없다. 이 상태를 삭제 실패로 기록해야 할까?
설명 확인 · 채점 없는 자가 점검
실패로 단정할 수 없다. 본문은 컨트롤러가 하위 자원을 먼저 회수할 수도 있다고 말한다. 하위가 없다는 것은 정리가 진행 중이라는 관측일 수 있으므로, 그 시점의 실제 상태를 기록하고 상위 App 의 부재까지 확인한다.