Kubernetes 네트워킹의 진화: CNI부터 Cilium까지, 그리고 eBPF가 바꾼 풍경
네트워킹, 쿠버네티스가 의도적으로 손대지 않은 영역
쿠버네티스를 처음 만졌을 때 가장 신기했던 게 네트워킹이었습니다. Pod이 알아서 IP를 받고, 서로 통신하고, Service가 로드밸런싱까지 해주거든요. 그런데 운영하면서 안쪽을 들여다보니 묘했습니다. 쿠버네티스 자체는 네트워킹을 거의 구현하지 않더라고요.
쿠버네티스가 Pod을 스케줄링할 때 깔고 가는 가정은 의외로 단순합니다.
"이 Pod는 IP 주소를 받아야 하고, 다른 모든 것과 통신할 수 있어야 한다"
딱 이 선언만 있습니다. 그리고 정작 이걸 실현하는 일들, 그러니까
- Linux 네트워크 네임스페이스 생성
- 라우팅 테이블 구성
- VXLAN 같은 오버레이 구축
- iptables 규칙 프로그래밍
- IP 주소 할당 관리
이 무거운 작업들은 쿠버네티스가 직접 하지 않습니다. 전부 다른 누군가에게 위임했죠. 그게 바로 Container Network Interface입니다.
처음엔 이게 설계 미완성처럼 보였어요. 핵심 기능을 왜 밖으로 빼놨지? 그런데 한참 지나고 나서야 이게 의도된 선택이었다는 걸 이해하게 됐습니다.
Container Network Interface, 네트워킹을 외부에 위임하는 약속
Container Network Interface는 사양이자 플러그인 생태계입니다. 쿠버네티스와 실제 네트워킹 시스템 사이의 계약을 정의하는 거죠. 흐름으로 보면 이렇습니다.
- 쿠버네티스: "이 Pod에 네트워킹 좀 설정해줘"
- CNI 플러그인: 실제 구성 작업 수행
- 플러그인: "완료했어, 네트워킹 정보는 이거야"
런타임에서는 단순한 라이프사이클로 동작합니다.
- ADD: 새 Pod을 위한 네트워킹 생성
- DELETE: Pod 종료 시 네트워킹 제거
- CHECK: 네트워크 상태 확인
kubelet이 Pod을 만들면 CNI 바이너리를 실행하고, 플러그인은 이런 일들을 처리합니다.
- IP 주소 할당 (IPAM)
- 가상 이더넷 쌍(veth) 생성
- 인터페이스를 Pod 네임스페이스에 배치
- 라우팅 및 정책 규칙 구성
CNI가 없으면 쿠버네티스 네트워킹은 그냥 멈춥니다. Pod은 IP를 못 받고, Service는 트래픽을 어디로도 보내지 못하죠. 실제로 클러스터를 새로 깔다가 CNI 설치를 빼먹으면 Pod이 ContainerCreating에서 영원히 멈춰 있는 걸 보게 됩니다. 그 순간 "아, 쿠버네티스가 네트워킹을 안 한다"는 말이 체감으로 들어오죠.
쿠버네티스는 왜 플러그인 모델을 선택했을까
초기 쿠버네티스 시대를 떠올려보면, 네트워킹 업체들이 각자 자기 솔루션을 들고 나왔습니다. 클라우드 제공업체는 네이티브 통합을 원했고, 가상화 업체는 SDN 스택을 밀었고, 오픈소스 커뮤니티는 오버레이를 만들었습니다. 다들 자기가 표준이 되고 싶어 했죠.
쿠버네티스는 여기서 흥미로운 선택을 합니다. 승자를 고르지 않았어요. 대신 구현이 아니라 인터페이스를 표준화했습니다.
이 결정이 길게 보면 세 가지 이점을 만들었습니다.
1. 휴대성
같은 애플리케이션이 환경을 옮겨 다닐 수 있습니다.
- 로컬 클러스터 (간단한 오버레이)
- 관리형 클라우드 (제공업체 네트워크 연결)
- 베어메탈 (고성능 라우팅)
2. 공급업체 중립성
네트워킹 혁신이 쿠버네티스 코어 바깥에서 일어날 수 있게 됐습니다. 새로운 기술이 등장해도 쿠버네티스 본체를 뜯어고칠 필요가 없죠.
3. 지속적인 진화
CNI 사양이 기본 IP 할당에서 시작해 대역폭 제어, 다중 네트워크 연결, 네이티브 커널 가속 같은 고급 기능까지 꾸준히 확장됐습니다.
컨설팅하면서 여러 클러스터를 봤는데, 같은 쿠버네티스를 쓰면서도 네트워킹 스택은 다 달랐습니다. 이게 가능했던 게 바로 이 플러그인 모델 덕분이에요. 코어를 건드리지 않고도 각자 환경에 맞는 선택을 했던 거죠. 핵심을 비워두고 인터페이스만 고정한 결정이, 결과적으로 생태계 전체의 실험 속도를 끌어올렸습니다.
전통적인 CNI의 한계, 그리고 iptables라는 벽
Flannel 같은 기존 CNI 구현을 들여다보면, 서로 다른 노드의 Pod 간 트래픽은 대략 이렇게 흐릅니다.
- CNI 플러그인이 노드 서브넷에서 IP 할당
- veth 쌍이 Pod을 호스트 네트워크에 연결
- 라우팅 및 포워딩 규칙 설치
- 교차 노드 트래픽을 터널(보통 VXLAN)에 캡슐화
Pod A가 Pod B로 패킷을 보낼 때를 풀어보면,
- 패킷이 가상 인터페이스를 통해 Pod을 떠남
- 노드가 VXLAN 패킷 안에 캡슐화
- UDP를 통해 목적지 노드로 이동
- 목적지에서 탈캡슐화해서 대상 Pod으로 전달
규모가 작을 때는 이게 잘 돕니다. 문제는 클러스터가 커질 때예요. 전통적인 CNI는 라우팅 결정, 로드밸런싱, 정책 시행을 전부 iptables에 기댑니다. 그런데 iptables는 규칙을 순차적으로 평가하는 구조라, Pod 수가 늘면 규칙도 같이 불어납니다.
- 수백 개 Pod → 수백 개 규칙
- 수천 개 Pod → 수천 개 규칙
- 대형 클러스터 → 측정 가능한 패킷 지연과 CPU 오버헤드
패킷 하나가 커널 안에서 긴 규칙 체인을 끝까지 순회해야 한다. 이게 아키텍처 차원의 병목이었습니다. 규모가 어느 선을 넘으면 이 체인 순회 비용이 무시할 수 없는 수준으로 올라오죠. Pod 하나하나는 멀쩡한데 전체로 보면 네트워크가 무거워지는, 어디서 많이 본 패턴입니다.
Cilium과 eBPF, 수행 방식 자체를 바꾸다
여기서 오해를 하나 짚고 갈 필요가 있습니다. Cilium은 CNI를 대체하는 게 아닙니다. CNI 사양을 구현하는 또 하나의 플러그인이에요. 차이는 무엇을 하느냐가 아니라 어떻게 하느냐에 있습니다.
iptables 대신 Cilium은 extended Berkeley Packet Filter를 씁니다. Linux 커널 안에 직접 내장된 프로그래머블 프레임워크죠.
핵심 차이점
- iptables: 긴 순차 규칙 체인을 위에서부터 평가
- extended Berkeley Packet Filter: 전략적인 커널 훅 지점에서 컴파일된 프로그램을 즉시 실행
eBPF 프로그램은 패킷 처리의 여러 단계에 끼어들 수 있습니다.
- 패킷이 네트워크 스택에 도달하기 전
- 인그레스/이그레스 처리 중
- 애플리케이션별 소켓 수준
Cilium은 이 지점들을 활용해서 라우팅, 서비스 로드밸런싱, 네트워크 정책, 관찰가능성을 구현합니다.
결정적인 변화는 여기입니다. 수천 개 규칙을 순회하는 대신, 패킷이 O(1) 조회로 최적화된 커널 맵을 참조합니다. 규칙 체인을 걷는 모델에서, 맵을 한 번 찌르는 모델로 바뀐 거죠. Pod이 늘어도 조회 비용이 선형으로 따라 늘지 않는다는 게 핵심입니다.
Cilium 아키텍처 들여다보기
모든 쿠버네티스 노드가 Cilium 에이전트를 DaemonSet으로 띄우고, 다섯 가지 역할을 맡습니다.
| 역할 | 설명 |
|---|---|
| CNI 서버 | kubelet의 ADD/DELETE 호출에 응답 |
| IP 주소 관리 | 클러스터/클라우드 통합으로 Pod 주소 할당 |
| eBPF 매니저 | 컴파일된 프로그램을 커널 훅에 로드 |
| Kubernetes Watcher | 클러스터 상태를 eBPF 맵으로 동기화 |
| Hubble | 네트워크 플로우 실시간 가시성 제공 |
구조의 무게중심은 Watcher와 eBPF 맵 사이에 있습니다. 수천 개 규칙을 매번 다시 프로그래밍하는 대신, Cilium은 쿠버네티스 상태를 커널 데이터 구조에 계속 동기화합니다. 쿠버네티스가 "이 Pod이 추가됐다"고 말하면, 그 변화가 곧장 커널 맵에 반영되는 식이죠.
전통적인 CNI가 정적인 라우터처럼 동작한다면, Cilium은 커널 안에서 돌아가는 프로그래머블 네트워크 OS에 가깝습니다. 정책을 거는 게 아니라, 네트워크 동작 자체를 코드로 정의하는 감각이에요.
실제 트래픽 플로우의 변화
Pod 간 통신
Pod을 떠나는 트래픽이 eBPF 정책 프로그램에서 평가되고, iptables 체인을 건너뛰어 목적지로 바로 라우팅됩니다. 지연시간이 마이크로초 수준으로 떨어지죠.
쿠버네티스 서비스 로드밸런싱
Pod이 Service IP에 붙으면 Cilium이 커널 공간에서 직접 로드밸런싱을 합니다. kube-proxy도, NAT 체인도, 규칙 폭발도 없습니다. kube-proxy를 걷어낼 수 있다는 건 운영하는 입장에서 꽤 큰 이야기예요. 그동안 규칙 동기화 지연이나 conntrack 이슈로 골치 아팠던 영역이 통째로 사라지니까요.
Layer-7 보안 정책
Cilium은 IP 주소만 보는 게 아니라 애플리케이션 동작 기반 정책까지 강제할 수 있습니다.
- 특정 도메인에만 HTTPS 트래픽 허용
- 알 수 없는 외부 목적지 거부
- HTTP 경로 수준 제어 적용
L3/L4에서 끝나던 정책이 L7까지 올라온다는 건, 보안 경계를 IP가 아니라 "무엇을 하려는 트래픽인가"로 그릴 수 있다는 뜻입니다.
관찰가능성, 의외로 가장 큰 강점
개인적으로 Cilium에서 가장 체감이 컸던 부분이 여기입니다. 기존 쿠버네티스 네트워크 디버깅은 패킷 캡처와 추측의 반복이었어요. tcpdump 띄워놓고, 어느 노드에서 뭐가 막혔는지 감으로 좁혀가는 식이었죠.
Cilium은 Hubble로 실시간 플로우 텔레메트리를 열어줍니다.
- 어떤 Pod이 어느 Service와 통신했는가
- 정책이 트래픽을 허용했는지 거부했는지
- DNS 및 HTTP 가시성
- 히스토리컬 플로우 추적
"네트워크가 왜 실패했지?"라고 묻는 자리에서, 네트워크 동작을 직접 들여다보는 자리로 옮겨간 셈입니다. 장애 대응할 때 이 차이는 생각보다 큽니다. 추측의 단계가 통째로 줄어드니까요.
프로덕션 도입 전 점검 항목
프로덕션에 Cilium을 올리기 전에 확인할 것들.
- 충분한 eBPF 지원을 갖춘 Linux 커널 버전
- 최신 Cilium 릴리스
- kube-proxy 교체 활성화
- Hubble을 통한 관찰가능성
- 적절한 라우팅 또는 BGP 통합
첫 번째 항목은 특히 베어메탈이나 오래된 OS 이미지를 쓰는 환경에서 발목을 잡기 쉽습니다. 커널이 eBPF 기능을 충분히 받쳐주지 못하면 Cilium의 장점이 반쪽이 되거든요. 도입을 검토한다면 커널 버전부터 보는 게 순서입니다.
운영 관점에서 흥미로운 건, 기능이 늘어나는데 오히려 복잡성은 줄어드는 경우가 많다는 점입니다. 네트워킹, 로드밸런싱, 보안이 따로 놀던 걸 하나의 플랫폼으로 모으니까요. 관리할 컴포넌트 수가 줄면 장애 표면도 같이 줄어듭니다.
마무리하며
돌아보면 쿠버네티스가 네트워킹 승자를 고르지 않은 그 결정이 출발점이었습니다. 코어를 비워두고 CNI라는 인터페이스만 고정한 덕분에, 그 빈 공간에서 혁신이 자랄 수 있었죠. 수년간 오버레이 터널과 iptables 규칙에 의존하던 네트워킹이, 이제는 프로그래머블 커널 로직으로 구현되는 단계까지 왔습니다.
Cilium이 주목받는 이유는 의외로 단순합니다. 커널 기능이 마침내 무르익어서, 쿠버네티스가 처음부터 약속했던 속도와 확장성을 네트워킹이 실제로 따라갈 수 있게 됐거든요.
2026년 현재 많은 팀이 Cilium으로 옮겨가고 있는 흐름도, 결국 이 맥락 위에 있다고 봅니다. 비워둔 자리에서 더 나은 구현이 자라났고, 그게 지금 표준의 자리로 올라오는 중인 거죠.