1. 개요
Cilium 은 리눅스 커널의 eBPF 로 Pod 네트워크를 처리하는 CNI 플러그인이다. Pod 에 IP 를 붙이는 기본 역할에 더해 네트워크 정책, 서비스 로드 밸런싱(kube-proxy 대체), 통신 관측(Hubble), 외부 요청 분배(Gateway API)를 한 컴포넌트에서 제공한다.
정책은 IP 가 아니라 신원(identity) 으로 판단한다. Pod 의 라벨 묶음마다 숫자 신원을 붙이고, 패킷이 Pod 출입구를 지날 때 그 신원으로 허용·차단을 정한다. Pod IP 가 바뀌어도 정책을 다시 쓸 필요가 없다.
Pod ──veth── [eBPF: 정책·NAT·로드 밸런싱] ── 노드 네트워크
│
└─ Hubble: 흐름 기록 (허용 / 차단 / DNS)
2. 구성 요소
| 구성 요소 | 형태 | 하는 일 |
|---|---|---|
| cilium-agent | DaemonSet (노드마다) | eBPF 프로그램을 커널에 올리고 정책을 집행한다 |
| cilium-operator | Deployment | 클러스터 단위 작업. IP 주소 관리(IPAM), 남은 자원 정리 |
| cilium-envoy | DaemonSet | L7 정책·Gateway 의 HTTP 처리 |
| Hubble | agent 안 + Relay | 흐름 기록 수집과 조회 |
3. IP 주소 관리 모드
| 모드 | Pod IP 의 출처 | 쓰는 곳 |
|---|---|---|
| cluster-pool | Cilium 이 나눠 주는 대역 | 일반 클러스터 |
| kubernetes | 노드의 podCIDR | kubeadm·kind 등 |
| eni | AWS ENI 의 보조 IP (VPC 주소) | EKS. vpc-cni 를 대신할 때 |
ENI 모드에서는 operator 가 노드에 ENI 를 붙이고 IP 를 받아 온다. 그래서 operator 에 EC2 네트워크 인터페이스 권한(IRSA)이 필요하다. 같은 AWS 계정에 다른 클러스터가 있으면, operator 가 ENI 를 정리(GC)하고 인스턴스를 찾는 범위를 태그로 묶어 둔다.
eni:
gcTags: # 이 태그가 있는 ENI 만 정리
io.cilium/cilium-managed: "true"
Environment: hosting
instanceTagsFilter: # 이 태그가 있는 인스턴스만 탐색
- Environment=hosting
4. 네트워크 정책
표준 NetworkPolicy 를 그대로 집행하고, Cilium 전용 CiliumNetworkPolicy 로 더 넓은 규칙을 쓴다.
| 기능 | 표준 NetworkPolicy | CiliumNetworkPolicy |
|---|---|---|
| Pod·Namespace·IP 대역 기준 허용 | 됨 | 됨 |
도메인 이름 기준 외부 허용 (toFQDNs) | 없음 | 됨 |
특수 대상 (world, host, ingress 등 entity) | 없음 | 됨 |
거부 규칙 (egressDeny) — 허용보다 우선 | 없음 | 됨 |
| L7 (HTTP 경로·메서드) | 없음 | 됨 |
| 클러스터 전체 정책 | 없음 | CiliumClusterwideNetworkPolicy |
Cilium Gateway 로 들어온 요청은 일반 Pod 가 아니라 ingress 라는 특수 신원으로 들어온다. 표준 NetworkPolicy 로 Namespace 를 막아 두면 Gateway 요청도 막히므로, 따로 허용한다.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata: {name: allow-cilium-gateway, namespace: team-a}
spec:
endpointSelector: {}
ingress:
- fromEntities: [ingress]
5. kube-proxy 대체와 소켓 LB
kubeProxyReplacement: true 이면 Service 주소를 Pod 주소로 바꾸는 일을 kube-proxy 의 iptables 대신 eBPF 가 한다. 변환은 두 단계에서 일어날 수 있다.
| 단계 | 방식 |
|---|---|
| 소켓 단계 (socket LB) | 앱이 connect() 할 때 호스트 커널 소켓에서 주소를 바꾼다. 패킷이 생기기 전이라 빠르다 |
| 패킷 단계 | Pod 를 나가는 패킷의 주소를 바꾼다 |
gVisor Pod 의 앱은 호스트 커널 소켓을 쓰지 않으므로 소켓 단계 변환을 받지 못한다. 그러면 Service 주소가 그대로 나가 정책에 막힐 수 있다. socketLB.hostNamespaceOnly: true 로 소켓 단계를 호스트에만 적용하면, Pod 트래픽은 패킷 단계에서 변환된다.
kube-proxy 를 빼면 Cilium 이 API 서버에 닿을 다른 길이 없으므로 k8sServiceHost·k8sServicePort 에 API 서버 주소를 직접 적는다. 이 값이 틀리면 agent 가 시작하지 못하고 노드 네트워크가 끊긴다.
6. Hubble
Hubble 은 Cilium 이 처리한 흐름을 기록한다.
| 기록 | 내용 |
|---|---|
| 흐름 | 출발·도착 신원, 포트, 판정(FORWARDED / DROPPED), 차단 이유 |
| DNS | DNS 규칙을 건 Namespace 의 조회 도메인 |
| 지표 | 차단 수, 포트별 연결, SYN 수 등을 Namespace 라벨과 함께 Prometheus 로 |
응답이 없을 때 “대상이 없어서” 인지 “정책 때문에” 인지를 기록으로 구분할 수 있다. 채굴 풀 조회, 일정 간격의 원격 조종 신호, 포트 스캔 같은 악용의 흔적도 흐름과 지표에 남는다.
hubble observe --namespace team-a --verdict DROPPED
hubble observe --namespace team-a --protocol dns
DNS 지표에 query 라벨을 붙이면 도메인마다 지표가 생겨 수가 폭발한다. 도메인은 흐름 기록으로 본다.
7. EKS 기본 CNI 와 비교
| vpc-cni (EKS 기본) | Cilium (ENI 모드) | |
|---|---|---|
| Pod IP | VPC 주소 | VPC 주소 |
| 표준 NetworkPolicy | 지원 (eBPF 에이전트) | 지원 |
| 도메인·거부·L7 정책 | 없음 | 있음 |
| 통신 관측 | 없음 | Hubble |
| Gateway | 별도 설치 | 내장 |
| 운영 | AWS 관리형 add-on | 직접 설치·업그레이드 |
8. 확인 명령
kubectl -n kube-system get ds cilium # 노드마다 agent 가 떠 있는지
kubectl -n kube-system exec ds/cilium -- cilium status # agent 상태 요약
kubectl -n kube-system exec ds/cilium -- cilium-dbg config | grep -i sock # 소켓 LB 설정
9. 관련 문서
- CNI — Cilium 이 따르는 규격
- gVisor — 함께 쓸 때의 소켓 LB 주의점
- Service — kube-proxy 대체가 처리하는 주소 변환
- conntrack — kube-proxy(iptables) 방식의 연결 추적
- IMDS — 정책으로 막아 두는 메타데이터 주소
- eBPF (예정) — Cilium 의 패킷 처리 방식
- NetworkPolicy (예정) — 표준 Pod 방화벽