핵심 한 줄

Load 시리즈는 한 대에서 결제 취소 시스템을 쥐어짰다. 병목이 CPU도 홉도 아닌 **취소 1건당 커밋 수(fsync)**임을 규명했고, 이력 커밋을 줄여 147→220rps까지 올렸다. 한 대의 천장은 DB 커밋이 정한다 — 거기까지가 Load의 결론이다. 이제 두 질문이 남았다: 여러 대로 늘리면 안전한가, 그리고 정말 빨라지나.

Scale 시리즈 — k3s 수평 스케일아웃

1막 · 여러 대로 안전하게 00 · 01 · 02 · 03 · 04 2막 · 늘리면 정말 빨라지나 05 · 06 · 07 · 08


1. Load가 어디까지 갔나

Load 22편을 끝내고 돌아보면, 수직 실측은 두 단계로 진행됐다.

1막(01~11편): AWS에 서버·DB·부하생성기를 분리해 판을 깔고, 취소 시스템에 부하를 걸었다. 코드 리뷰가 “옳다”고 통과시킨 분산락이 한 가맹점에 부하가 몰리는 순간 취소의 99.9%를 거부했다. 데드락인 줄 알았다 — 대기 없이 거부하는 분산락이었다. DB 원자 UPDATE로 교체하자 99.9% 거부가 0%로 내려갔다.

2막(12~15편): 병목 위치가 틀렸다는 걸 관측으로 재규명했다. “동기 홉(risk 호출)이 185 천장의 원인”이라 결론냈는데 — USE 인구조사를 돌리자 두 번 뒤집혔다. risk 홉은 21ms로 무죄였고, 풀 크기도 이미 2코어 DB 공식 상한이었다. 진짜 정체는 취소 1건당 커밋 6개(fsync). 이력 3커밋을 1로 묶자 147→220rps로 올랐다. open-model SLO로 무릎 ~190rps도 쟀다.

요컨대 Load의 결론은 이것이다.

한 대의 천장은 DB fsync가 정한다. 그리고 그 천장을 관측 없이 추측하면 틀린다.

2. 남은 질문 둘

Load는 단일 토폴로지(payment 1대, DB 1대)를 전제했다. 그 전제를 걷으면 질문이 두 개 생긴다.

첫째, 안전한가. 파드가 N개로 늘면, 한 대일 때는 숨어 있던 동시성 결함이 새로 얼굴을 내민다. 여러 파드가 같은 취소 요청을 동시에 처리하면 이중취소가 생기나? 같은 Kafka 이벤트가 두 번 발행되나? 스케줄러가 여러 파드에서 동시에 돌면 중복 재처리가 터지나? Load에서 다진 정합성 보장이 오케스트레이션 레이어에서도 살아남는지 — 이걸 먼저 따진다.

둘째, 정말 빨라지나. Load가 규명한 천장은 “DB커밋 수”였다. 파드를 N개로 늘려도 DB는 그대로다. replica를 늘리면 처리량이 오를까, 아니면 모두 같은 DB 앞에서 막힐까? 직관은 “당연히 빨라진다”고 말한다. 2막에서 그 직관이 뒤집힌다 — 지금은 스포일러만.

3. 왜 k3s인가

관리형 쿠버네티스(EKS, GKE)를 쓰면 오케스트레이션 내부가 블랙박스가 된다. 스케줄링이 어떻게 결정되는지, 롤링 업데이트 중에 어떤 순서로 파드가 교체되는지, 노드가 죽으면 무슨 일이 일어나는지 — API로는 안 보인다.

k3s는 경량 쿠버네티스다. 단일 바이너리, etcd 대신 SQLite(또는 내장 데이터스토어), 컨트롤 플레인까지 노드 위에서 직접 돌아간다. 설치가 curl 한 줄이고, 내부가 열려 있다. 여기서 “자립 리그”를 깐다는 건 — 관리형 서비스 없이, 내가 만든 인프라 위에서 오케스트레이션을 직접 실측한다는 뜻이다.

DB는 클러스터 밖에 고정한다. Load에서 규명한 병목(DB fsync)을 통제변수로 묶어두기 위해서다. 파드를 늘렸을 때 무엇이 변하고 무엇이 그대로인지 — DB를 클러스터 밖에 두면 그 경계가 선명하게 보인다.

실측 환경

Load 시리즈와 동일하게 AWS EC2에 올린다. k3s 컨트롤 플레인 1대 + 워커 노드 N대. DB는 클러스터 외부 단일 인스턴스 고정. 부하생성기는 별도 분리.

4. 이 시리즈의 지도

편무엇을핵심 질문
01k3s 클러스터 구성AWS에 k3s를 까는 데 어디서 막히나
02정합성 검증replica N개에서 이중취소·중복발행 실제로 발생하나
03자가치유(HA)노드 하나가 죽으면 취소 요청이 어떻게 되나
04롤링 업데이트 함정롤링 중 in-flight 취소가 어떻게 처리되나
05처리량 반전replica 3개가 처리량을 올렸다 — 정말?
06병목 재규명처리량이 안 오른 진짜 이유
07오퍼레이터 관점쿠버네티스가 숨긴 운영 비용
08설계 근거각 YAML 줄이 왜 그렇게 써 있나

1막은 안전하게 늘리는 것을 다진다. 빠르게 늘리는 게 아니다 — 취소 시스템에서 이중차감 한 번이 “느려서 못 쓰는 것”보다 나쁘다. 2막은 그렇게 안전하게 늘린 다음에야 처리량 질문을 꺼낸다.

그리고 2막 첫 결과가 예상을 깬다. “replica가 천장을 올렸다”는 첫 숫자가 통째로 뒤집힌다. 원인 진단에 네 단계가 걸린다.

Load를 안 읽었다면

이 시리즈는 Load 00편부터 시작하는 게 자연스럽다. k3s를 쓰는 이유가 “k3s가 좋아서”가 아니라 “Load에서 규명한 천장을 수평 축에서 다시 만나기 위해서”이기 때문이다.

한 줄

Load에서 배운 건 측정이 추측을 이긴다는 것이었다. 그걸 들고 수평으로 간다. 다음 편(01편)에서 리그를 까는 순간부터 첫 번째 벽을 만난다.