쿠버네티스 배포 흐름 한 번에 정리 (1) 큰 그림 — 모두 API 서버만 본다
이 시리즈를 쓰는 이유
매일 배포를 하는데도 “helm upgrade를 치면 그 사이에 누가 무엇을 하느냐”를 말로 설명하려 하면, 나는 늘 “새 파드가 생성되고…”에서 init 컨테이너로 건너뛰었다. 그 사이에 있는 API 서버, 컨트롤러, 스케줄러가 통째로 빠졌다. 운영은 하는데 내부 동작은 비어 있었다는 뜻이다.
그래서 “코드 push부터 사용자 요청이 파드에 닿기까지”를 한 번에 정리하기로 했다. 목표는 두 가지다.
- 쿠버네티스를 깊이 몰라도, 이 시리즈를 다 읽고 나면 배포 한 번이 일어나는 과정이 머릿속에 한 줄로 이어지게 한다
- 내가 잊을 때마다 다시 와서 5분 안에 복기할 수 있게 한다
| 편 | 내용 | 한 줄 |
|---|---|---|
| (1) 큰 그림 | 규칙 3개와 단계 7개 | 모두 API 서버만 본다 |
| (2) 파드가 만들어지기까지 | ①트리거 ②체인 ③웹훅 ④노드 | 장부에 올리고, 끼워 넣고, 띄운다 |
| (3) 트래픽을 받기까지 | ⑤Ready ⑥교체 ⑦요청 흐름 | Running이라고 트래픽을 받는 게 아니다 |
| (4) 장애 때 거꾸로 쓰기 + 복기 | 증상표, 틀렸던 것, 셀프 체크 | 흐름을 거꾸로 따라가면 원인이 나온다 |
예시는 내가 운영하는 구성(Jenkins/ArgoCD 배포, Istio 사이드카, Vault Agent Injector)을 기준으로 했다. 교과서 예시보다 실제로 보이는 이름과 엮어야 기억에 남기 때문이다. 리소스 이름은 공개 글이라 일반적인 이름(
app네임스페이스,api-backend,web-nginx)으로 바꿨다.
먼저 용어 다섯 개
이것만 알면 시리즈 전체를 읽을 수 있다. 처음 보는 사람을 위해 넣었고, 아는 사람은 건너뛰면 된다.
| 용어 | 한 줄 설명 | 비유 |
|---|---|---|
| Pod(파드) | 컨테이너가 실제로 도는 가장 작은 단위. 컨테이너 여러 개가 한 파드에 같이 있을 수 있다 | 요리 한 접시 |
| ReplicaSet(RS) | “이 모양의 파드를 N개 유지하라”는 오브젝트 | 같은 요리 N접시 주문서 |
| Deployment | ReplicaSet을 관리하며 버전 교체(롤링 업데이트)를 맡는 오브젝트 | 메뉴판. 메뉴가 바뀌면 새 주문서를 만든다 |
| Service / EndpointSlice | 파드 여러 개 앞에 붙는 고정 주소 / 그 주소 뒤에 실제로 어떤 파드가 있는지의 목록 | 대표 전화번호 / 지금 전화 받을 수 있는 직원 명단 |
| 사이드카 | 앱 컨테이너 옆에 같이 붙어 도는 보조 컨테이너 (Istio의 istio-proxy, Vault의 vault-agent) | 요리 옆에 붙는 반찬 |
한 줄
모두 API 서버에 쓰고, 각자 API 서버를 지켜보다가, 자기 차례가 오면 자기 일만 한다.
시리즈 전체가 이 한 줄의 반복이다. “누가 하나요?”라는 질문이 오면 답은 항상 이 꼴이다.
○○가 API 서버에서 △△가 바뀐 걸 보고 □□를 한다.
규칙 세 개
규칙 1. etcd에 쓰는 건 API 서버뿐이다
etcd는 클러스터의 모든 상태가 저장되는 데이터베이스다. 컨트롤러도, 스케줄러도, kubelet도, Helm도, ArgoCD도 etcd를 직접 건드리지 않고 전부 API 서버를 거친다.
- 왜: 인증·인가·검증을 한 곳에서 하기 위해서다. 누구나 etcd를 직접 건드리면 통제할 지점이 없다. 2편에서 볼 “웹훅”도 이 한 곳에 끼어 있기 때문에 모든 파드에 빠짐없이 적용된다
규칙 2. 컨트롤러는 조정 루프(reconcile loop)를 돈다
1
원하는 상태(spec)를 본다 → 실제 상태(status)를 본다 → 다르면 차이를 줄인다 → 반복
- 왜: “한 번 명령하고 끝”이 아니라 “계속 맞춰 가는” 구조라서, 파드가 죽어도 누가 다시 만들라고 시키지 않아도 다시 생긴다
- ArgoCD도 같은 원리다. Git이 원하는 상태, 클러스터가 실제 상태다. 쿠버네티스를 이해하면 ArgoCD가 덤으로 이해된다
규칙 3. 컴포넌트끼리 서로 부르지 않는다
Deployment 컨트롤러는 ReplicaSet 컨트롤러에게 “파드 만들어”라고 호출하지 않는다. API 서버에 ReplicaSet을 쓸 뿐이고, ReplicaSet 컨트롤러는 API 서버를 watch(감시)하다 새 ReplicaSet을 보고 움직인다.
- 왜: 서로 직접 호출하면 하나가 죽었을 때 연쇄로 멈춘다. API 서버라는 게시판 하나만 보게 하면, 각자 독립적으로 죽고 살아나도 상태는 게시판에 남아 있다
- 이 규칙 때문에 3편의 “종료 순간 함정”이 생긴다. 서로 기다려 주지 않기 때문이다
비유 하나 — 식당 주문 장부
규칙 세 개를 한 장면으로 묶으면 이렇다.
| 쿠버네티스 | 식당 | 하는 일 |
|---|---|---|
| API 서버 + etcd | 주문 접수대 + 장부 | 장부에 쓰는 건 접수대만 한다 |
| Deployment 컨트롤러 | 점장 | “메뉴가 바뀌었네 → 새 주문서(ReplicaSet)를 장부에 올리자” |
| ReplicaSet 컨트롤러 | 주방장 | “이 요리는 2접시여야 하는데 0접시네 → 접시(Pod) 2개를 장부에 올리자” |
| 어드미션 웹훅 | 장부에 올리기 직전의 검수원 | “이 접시엔 반찬(사이드카)을 꼭 붙여야 해” |
| 스케줄러 | 자리 배정 담당 | “자리가 남는 테이블(노드)에 배정” |
| kubelet | 테이블 담당 요리사 | “내 테이블에 배정된 접시를 실제로 만든다” |
아무도 서로에게 말을 걸지 않는다. 모두 장부만 본다. 이 장면 하나만 기억하면 나머지는 따라온다.
단계 7개 — 전체 지도
배포 한 번은 이 7단계로 나뉜다. ①~④는 2편, ⑤~⑦은 3편에서 다룬다
외우는 문장: 트리거 → 체인 → 웹훅 → 노드 → Ready → 교체 → 요청
| 단계 | 한 줄 | 다루는 편 |
|---|---|---|
| ① 트리거 | 배포 도구가 Deployment를 바꿔 API 서버에 보낸다 | 2편 |
| ② 체인 | Deployment → ReplicaSet → Pod가 “장부에” 올라간다 | 2편 |
| ③ 웹훅 | 저장 직전에 init 컨테이너와 사이드카가 끼워진다 | 2편 |
| ④ 노드 | 스케줄러가 노드를 고르고 kubelet이 실제로 띄운다 | 2편 |
| ⑤ Ready | readiness를 통과해야 트래픽 대상 목록에 들어간다 | 3편 |
| ⑥ 교체 | 새 파드가 준비된 뒤 옛 파드를 내린다 | 3편 |
| ⑦ 요청 | 사용자 요청이 Gateway → nginx → 백엔드로 간다 | 3편 |
⑦을 꼭 붙인다. “코드 push부터 요청이 파드에 닿기까지“라는 질문에 ①~⑥만 말하면 절반만 답한 것이다. 나는 처음에 ⑦을 통째로 빼먹었다.
“누가?”의 사슬 — 10개 주체를 한 장에
7단계를 움직이는 주체를 순서대로 세우면 아래와 같다. 빨간 번호는 내가 처음 설명할 때 빠뜨린 단계다. 빠뜨린 것이 거의 다 보라색(컨트롤 플레인)이다. 내가 직접 설정하지 않고 쿠버네티스가 알아서 해 주는 부분이라 의식하지 못했던 것이다.
모든 화살표는 “API 서버에 쓴다 → 다음 주체가 watch로 알아챈다”의 반복이다
1편 한 장 요약
- 한 줄: 모두 API 서버에 쓰고, 각자 지켜보다가, 자기 차례에 자기 일만 한다
- 규칙 3개: etcd엔 API 서버만 쓴다 / 컨트롤러는 조정 루프 / 서로 부르지 않는다(watch)
- 비유: 식당 장부. 점장(Deployment) → 주방장(RS) → 검수원(웹훅) → 자리 배정(스케줄러) → 요리사(kubelet)
- 단계 7개: 트리거 → 체인 → 웹훅 → 노드 → Ready → 교체 → 요청
읽고 나서 바로 확인 — Deployment 컨트롤러는 ReplicaSet 컨트롤러에게 어떻게 일을 시키나?
시키지 않는다. API 서버에 ReplicaSet을 쓰면, ReplicaSet 컨트롤러가 watch로 보고 반응한다. (규칙 3)
다음 편: (2) 파드가 만들어지기까지 — helm upgrade 한 줄이 컨테이너 다섯 개짜리 파드가 되는 과정