etcd의 Watcher 메커니즘과 Kube-apiserver의 실시간 동기화 원리
1. 상태 동기화의 본질과 폴링(Polling) 아키텍처의 한계
쿠버네티스와 같은 대규모 분산 시스템에서 수백 개의 컨트롤러와 수천 대의 워커 노드(Kubelet)가 완벽하게 일관된 상태를 유지하려면, 중앙 저장소인 etcd의 변경 사항을 지연 없이 감지해야 합니다. 만약 클라이언트들이 주기적으로 변경 사항이 있는지 물어보는 단순한 폴링(Polling) 방식을 사용한다면, API 서버와 etcd는 막대한 HTTP 요청 오버헤드와 CPU/메모리 부하를 견디지 못하고 붕괴할 것입니다. 폴링 주기를 늘리면 클러스터의 반응성이 심각하게 떨어지고, 주기를 줄이면 부하가 기하급수적으로 증가하는 딜레마에 빠집니다. 이러한 병목을 근본적으로 해결하고 선언적 아키텍처의 핵심인 '실시간 상태 조정(Reconciliation)'을 가능하게 하는 기술적 기반이 바로 etcd의 Watcher 메커니즘입니다.

2. etcd v3 Watch API와 gRPC 멀티플렉싱(Multiplexing)
etcd v3는 이전 버전의 HTTP/1.1 기반 롱 폴링 구조를 완전히 버리고, HTTP/2와 gRPC 기반의 양방향 스트리밍(Bi-directional Streaming) 아키텍처를 전면 도입했습니다. gRPC의 멀티플렉싱 기술을 활용하면, Kube-apiserver와 etcd 사이에 단일 TCP 연결만 맺어두고도 수만 개의 서로 다른 리소스(키 또는 디렉터리)에 대한 실시간 감시 채널을 독립적으로 운영할 수 있습니다.
etcd 내부의 Watcher는 특정 키(Key)나 프리픽스(Prefix)의 리비전(Revision) 변화를 백그라운드에서 지속적으로 모니터링합니다. 클러스터 어딘가에서 새로운 파드가 생성되거나 기존 서비스의 레이블이 변경되어 etcd 물리 스토리지에 새로운 데이터가 커밋되는 즉시, Watcher는 해당 변경 이벤트를 패키징하여 gRPC 스트림을 통해 Kube-apiserver로 지연 없이 푸시(Push)합니다. 이 과정에서 클라이언트의 반복적인 폴링 요청이 일절 개입하지 않으므로 네트워크 대역폭과 서버 리소스 소비가 극적으로 감소합니다.
3. 다중 버전 동시성 제어(MVCC)와 ResourceVersion의 결합
Watch 메커니즘이 이벤트의 누락 없이 완벽하게 동작할 수 있는 이유는 etcd의 핵심 구조인 MVCC(Multi-Version Concurrency Control) 아키텍처 덕분입니다. etcd는 데이터가 업데이트될 때 기존 값을 덮어쓰지 않고 전역 논리 시계인 리비전을 증가시키며 새로운 이력을 누적합니다. 이 리비전 값은 쿠버네티스 리소스의 메타데이터인 ResourceVersion 필드와 1대1로 정확히 매핑됩니다.
Kube-apiserver가 네트워크 파티션이나 일시적인 프로세스 재시작으로 인해 etcd와의 연결이 잠시 끊어졌다고 가정해 보겠습니다. 연결이 복구된 후 API 서버는 etcd에 처음부터 모든 데이터를 다시 달라고 무식하게 요청하지 않습니다. 대신, 자신이 마지막으로 수신했던 ResourceVersion을 명시하여 "이 특정 시점 이후에 발생한 변경 이벤트들만 순서대로 다시 스트리밍해달라"고 요청합니다. 이를 Historical Watch라고 부릅니다. MVCC 구조 덕분에 etcd는 컴팩션(Compaction) 전까지 과거의 리비전 이력을 모두 보관하고 있으므로, 누락된 이벤트를 정확하게 재생(Replay)하여 완벽한 데이터 정합성을 보장할 수 있습니다.
4. Kube-apiserver의 Watch Cache (Cacher 구조)
etcd의 Watcher 메커니즘이 아무리 효율적이라 하더라도, 클러스터 내의 수많은 클라이언트(Scheduler, Controller Manager, Kubelet, 외부 Operator)가 API 서버를 거쳐 etcd에 직접 Watch 연결을 맺는다면 etcd는 순식간에 커넥션 풀 고갈과 메모리 한계에 다다를 것입니다. 이를 완벽하게 방어하기 위해 Kube-apiserver 내부에는 Watch Cache(내부 컴포넌트명 Cacher)라는 인메모리 버퍼 아키텍처가 존재합니다.
API 서버는 etcd와 단일화된 거대한 메인 Watch 스트림을 유지하며 클러스터의 모든 상태 변화를 자신의 메모리 공간인 Watch Cache에 실시간으로 복제합니다. 클라이언트들이 API 서버에 특정 네임스페이스나 리소스에 대한 Watch를 요청하면, API 서버는 이 요청을 etcd로 포워딩하지 않고 자신의 로컬 메모리에 있는 Watch Cache를 데이터 소스로 삼아 이벤트를 분배(Fan-out)합니다. 이를 통해 중앙 백엔드 저장소인 etcd의 부하를 완벽하게 격리하면서도 수천 개의 클라이언트에게 마이크로초 단위의 이벤트 스트리밍을 안정적으로 제공할 수 있습니다.
5. Informer 메커니즘과 조정 루프(Reconciliation Loop)의 완성
Kube-apiserver가 쉴 새 없이 스트리밍해주는 Watch 이벤트를 클라이언트 측에서 누수 없이 효율적으로 소비하기 위해, 쿠버네티스는 Informer라는 핵심 디자인 패턴을 클라이언트 라이브러리(client-go)에 표준으로 구현해 두었습니다.
Informer는 API 서버와 지속적인 Watch 연결을 맺고 이벤트를 수신하여 클라이언트 프로세스의 로컬 메모리에 스레드 세이프(Thread-safe)한 내부 캐시(Local Store)를 완벽하게 구축합니다. 클러스터에서 파드가 생성(Add), 수정(Update), 삭제(Delete)될 때마다 Informer는 즉각적으로 이벤트를 감지하고 내부 캐시를 동기화한 뒤, 개발자가 미리 등록해둔 이벤트 핸들러(Event Handler)를 트리거하여 안전한 내부 작업 큐(Workqueue)에 해당 리소스의 식별자를 밀어 넣습니다.
결과적으로 etcd의 내부 bbolt 스토리지에서 발생한 단 1바이트의 상태 변화는 [etcd gRPC Watch 스트림] -> [Kube-apiserver Watch Cache] -> [클라이언트 Informer] -> [Workqueue]로 이어지는 매끄럽고 견고한 비동기 이벤트 파이프라인을 타고 전달됩니다. 이 파이프라인 덕분에 쿠버네티스의 모든 컨트롤러들은 무거운 데이터베이스 쿼리나 폴링 부하 없이, 자신이 관장하는 리소스의 실제 상태가 사용자가 선언한 명세(Spec)와 달라지는 그 찰나의 순간에 즉각 조정 로직(Reconcile)을 실행하여 클러스터의 완벽한 자가 치유(Self-healing) 아키텍처를 완성하게 됩니다.
'1. K8s Core & Architecture > 1.1. 컨트롤 플레인 (Control Plane) 심층 분석' 카테고리의 다른 글
| 컨트롤러 패턴(Controller Pattern) 완벽 이해: Reconcile 루프의 마법 (0) | 2026.03.16 |
|---|---|
| Kube-controller-manager의 역할: 클러스터의 상태를 유지하는 핵심 루프 (0) | 2026.03.16 |
| etcd 고가용성(HA) 클러스터 구성과 백업/복구 베스트 프랙티스 (0) | 2026.03.15 |
| etcd 심층 분석: 쿠버네티스의 모든 상태 데이터는 어떻게 저장되는가? (0) | 2026.03.15 |
| API 서버의 요청 처리 흐름: Authentication부터 Admission Control까지 (1) | 2026.03.15 |