Tech Artifacts/K8s Networking

K8s Networking - 04. calico

Calico가 BGP로 Pod를 잇는 법 — 그리고 WireGuard가 가져온 변화

한 클러스터가 두 데이터센터에 걸쳐 있었다. 트래픽이 노드 간에 평문_으로 흘렀다. 보안 팀이 "Pod 통신을 암호화하라"고 요구했다. 팀은 Calico의 WireGuard 모드를 켰다 — 노드 간 Pod 트래픽이 커널 수준에서 암호화됐다. 코드 한 줄 안 고치고. 이것이 Calico를 *네트워크 플러그인 이상으로 쓰는 사례다.

이 글이 푸는 것은: Calico가 03장의 두 접근(오버레이/언더레이)을 BGPIPIP/VXLAN 모드로 어떻게 구체화하고, WireGuard로 암호화를 어떻게 덧붙이는가다. 그리고 왜 Calico가 "CNI 하나"가 아니라 "CNI + 정책 엔진" 두 역할인지.

03장의 연장선에서 Calico 보기 — 두 모드와 정책

03장에서 CNI의 두 근본 접근을 봤다 — 오버레이(캡슐화)와 언더레이(직접 라우팅). Calico는 그 두 접근을 모두 모드로 제공하면서, 거기에 한 가지를 더 얹는다: 고급 네트워크 정책.

flowchart TD
    CAL["Calico"] --> DP["데이터플레인 (CNI)<br/>IPAM + 라우팅"]
    CAL --> PE["정책 엔진<br/>NetworkPolicy + CRD(고급)"]
    DP --> M1["모드: BGP / IPIP / VXLAN<br/>+ WireGuard(암호화)"]
    PE --> L["L3/L4/L7 격리"]

이 "데이터플레인 모드"와 "정책"이 _독립_이라는 게 Calico의 핵심 설계다. 데이터플레인은 VXLAN으로, 정책은 CRD 고급 모드로 — 각각 따로 고른다. 그래서 "Calico 깔았다"가 아니라 "어느 모드로"까지 봐야 한다. 이 글은 데이터플레인에 집중하고, 정책은 07장에서.

BGP 모드 — 03장의 언더레이를 Calico가 구현하는 법

03장에서 언더레이는 "물리 라우터에게 Pod 대역을 가르쳐준다"였다. Calico는 이걸 BGP(Border Gateway Protocol)로 구현한다 — 각 노드가 BGP로 이웃(다른 노드 또는 TOR 라우터)에게 "내 노드엔 10.244.1.0/24 Pod 대역이 있다"고 광고하면, 모든 노드가 _모든 Pod 대역으로 가는 경로_를 학습한다. (Calico docs - Networking)

flowchart LR
    N1["노드1<br/>10.244.1.0/24"] -. "BGP 광고<br/>나는 10.244.1.0/24를 가짐" .-> N2["노드2<br/>10.244.2.0/24"]
    N1 -->|"Pod 패킷 그대로"| R["물리 라우터/스위치"]
    R --> N2

캡슐화 없이 Pod 패킷이 그대로 전달되므로, 성능·가시성이 좋다. tcpdump로 패킷을 보면 Pod IP가 그대로 보인다 — 디버깅이 쉽다. CPU 오버헤드도 없다(캡슐화/캡슐해제 연산이 없으니까).

제약: 물리 네트워크가 BGP를 지원·허용해야 한다. 클라우드 VPC는 보통 노드 간 BGP를 막거나 제한한다(클라우드 라우터는 사용자 정의 라우팅을 허용하지 않음). 그래서 클라우드에선 BGP 대신 IPIP/VXLAN 모드로 간다. 온프렘 베어메탈이나 자체 데이터센터에서 BGP가 빛을 발한다.

BGP 토폴로지 — 대규모에서 피어가 폭증한다

BGP 모드가 대규모에서 부딪히는 벽은 _토폴로지_다. 가장 단순한 구성은 full mesh — 모든 노드가 모든 노드와 BGP 피어를 맺는다. 노드 N개면 피어 연결이 N×(N-1)/2개. 노드 100개면 ~5000개의 피어, 노드 1000개면 비현실적이다. (Calico docs - BGP route reflectors)

그래서 대규모에선 route reflector(RR) 패턴을 쓴다 — 몇 개의 노드(또는 전용 RR)가 중계 역할을 맡는다. 모든 노드가 RR하고만 피어를 맺고, RR이 라우팅 정보를 모아 전파. 피어 수가 선형으로 줄어든다.

이 토폴로지 설계가 "BGP 모드를 대규모에서 쓸 수 있는가"의 핵심이다. full mesh는 소규모(수십 노드)까지. 그 이상은 RR 또는 아예 VXLAN으로. 이 결정을 안 하면 노드 추가 시 BGP 피어가 폭증해 통신이 불가능해진다.

IPIP / VXLAN 모드 — 03장의 오버레이를 Calico가 구현하는 법

BGP가 안 되는 환경(클라우드 등)에서는 03장의 오버레이(캡슐화)로 돌아간다. Calico는 두 캡슐화 형식을 지원한다:

  • IPIP(IP-in-IP): Pod 패킷을 추가 IP 헤더 하나로 감쌈. 단순하지만 일부 클라우드(AWS)가 지원하지 않는다.
  • VXLAN: Pod 패킷을 UDP로 감쌈. 범용. 대부분의 클라우드에서 동작.
BGP 모드:  [Pod IP 헤더][페이로드]
IPIP:      [노드 IP 헤더][Pod IP 헤더][페이로드]
VXLAN:     [노드 IP][UDP][VXLAN][Pod 이더넷][페이로드]

캡슐화 모드는 어디서든 동작하지만 오버헤드가 있다. Calico 모드 선택의 기준은 단순하다 — "물리 네트워크가 BGP를 허용하는가?" 허용하면 BGP(성능·가시성), 아니면 캡슐화(범용성).

WireGuard 모드 — 라우팅 위에 암호화를 얹다

도입의 사례로 돌아가자. 보안 팀이 "Pod 통신을 암호화하라"고 했다. 전통적 해법은 애플리케이션에 mTLS를 넣거나 서비스 메시를 까는 것이었는데, 둘 다 코드나 인프라 변경이 크다. Calico는 WireGuard(커널 수준의 가벼운 VPN)를 데이터플레인 위에 얹어, 노드 간 Pod 트래픽을 코드 변경 없이 암호화한다. (Calico docs - WireGuard 암호화)

중요한 점: WireGuard는 BGP/VXLAN 모드와 함께 쓴다. "BGP로 라우팅하되 WireGuard로 암호화" — 라우팅 모드를 바꾸지 않고 암호화만 덧붙이는 계층.

  • 장: 코드 변경 없이 Pod 통신 암호화. 커널 수준이라 빠르다. 규정(PCI-DSS, HIPAA 등)의 "전송 구간 암호화" 요구를 충족.
  • 단: 노드 에만 암호화. 같은 노드 안 Pod 통신은 암호화되지 않는다(로컬 브리지를 거치므로). WireGuard 커널 모듈 의존.
# Calico WireGuard 활성화 (Calico 설치 후, felixConfiguration)
kubectl patch felixConfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'

정확한 활성화 명령/지원 버전은 Calico docs에서 실측. 1.36 호환 Calico 버전 명시 필요.

WireGuard가 IPsec 같은 기존 암호화 대비 가벼운 이유: 단순한 현대 암호학(ChaCha20-Poly1305) + 커널 통합. (WireGuard) 성능 손실이 적으면서 규정을 충족할 수 있어, Calico가 IPsec 대신 WireGuard를 택했다.

IPAM — 왜 블록 기반인가, 라우팅 집약를 위해

왜 Calico IPAM이 블록 기반*일까? *라우팅 집약 때문이다. BGP 모드에서 노드 1000개가 각자 자기 Pod IP를 개별적으로 광고하면 라우팅 테이블이 폭발한다. Calico는 각 노드에 CIDR 블록(예: /24)을 할당하고, 그 블록에서 Pod IP를 준다 — 블록이 가득 차면 새 블록을 요청. Calico는 IP 풀(IPPool CRD)로 전체 대역과 블록 크기를 관리한다. (Calico docs - IPAM)

이 블록 설계가 _라우팅 집약_을 가능하게 한다. 각 노드가 /24 블록 하나만 BGP로 광고하므로, 노드 1000개여도 라우팅 테이블이 /24 단위 1000줄로 줄어든다 — Pod별 라우팅이 아니라 블록별. 03장의 host-local IPAM보다 정교한 블록 관리가 BGP 모드의 전제다.

블록 모델이 만드는 흥미로운 특성: Pod가 같은 노드에서 재시작되면 _같은 IP 블록_에서 IP를 받을 확률이 높다(블록이 노드에 _affinity_를 갖기 때문). 이것이 어느 정도의 IP "안정성"을 준다(완전한 고정은 아님). 반면 Pod가 다른 노드로 재스케줄되면 다른 블록의 IP를 받는다. 완전한 IP 고정이 필요하면 StatefulSet이 별도로 필요하다.

아키텍처 — felix, typha, bird가 각각 무슨 일을 하나

Calico는 여러 컴포넌트로 이루어진다. 각자 무슨 일을 하는지, 왜 필요한지 보자.

flowchart TD
    APIS["kube-apiserver"] --> TC["typha<br/>(스케일, 중개)"]
    TC --> F1["felix (노드1)<br/>라우팅/iptables 규칙"]
    TC --> F2["felix (노드2)"]
    F1 --> BIRD1["bird (BGP 데몬, 노드1)"]
    F2 --> BIRD2["bird (BGP 데몬, 노드2)"]
    BIRD1 <-. BGP 피어 .-> BIRD2
  • felix: 각 노드의 에이전트. 라우팅/iptables/정책 규칙을 커널에 깐다. 데이터플레인을 실제로 프로그래밍하는 주체.
  • bird(선택): BGP 데몬. 노드 간 BGP 피어링. felix가 라우팅 정보를 bird에게 넘기면 bird가 BGP로 광고.
  • typha(대규모): felix가 apiserver를 직접 watch하지 않게 중개해 apiserver 부하를 감소. (Calico 아키텍처)
  • kube-controllers: 클러스터 수준 컨트롤러(IPAM 등).

typha의 역할을 03장 관점에서 보면 명확하다 — felix N개가 각자 apiserver를 watch하면 apiserver 부하가 N배. typha가 한 번 watch해 felix들에게 중개하면 apiserver 부하가 1배. 대규모 클러스터(수백 노드 이상)의 필수 컴포넌트. 이것이 "CNI도 규모 대응 설계가 필요하다"는 사례다.

Calico eBPF 모드 — kube-proxy까지 대체하는 옵션

여기까지 Calico의 데이터플레인은 iptables 기반이었다. Calico는 eBPF 데이터플레인 모드도 지원한다 — iptables 대신 eBPF로 Service 라우팅·정책을 처리. 이점은 05장 Cilium과 같은 맥락이다: 대규모에서 iptables 병목(규칙 순차 탐색)을 회피한다. (Calico docs - eBPF dataplane)

이것이 "Calico는 iptables, Cilium은 eBPF"라는 단순화가 틀린 이유다. Calico도 eBPF 모드가 있어, iptables의 Calico와 eBPF의 Calico는 다른 데이터플레인_이다. 차이가 있다면 Cilium은 eBPF를 기본/주력으로 하는 반면, Calico는 iptables가 기본이고 eBPF가 옵션이라는 점. 어느 쪽이든 "_현재 모드"가 데이터플레인의 성격을 결정한다.

BGP graceful restart — 노드 재부팅 중에 라우팅이 끊기면

BGP 모드에서 한 노드가 재부팅되면 — 그 노드의 BGP 세션이 끊기고, 다른 노드들이 "10.244.1.0/24로 가는 경로가 사라졌다"고 판단해 트래픽을 드롭한다. BGP graceful restart가 이 찰나를 완화한다:

  • 노드가 BGP 세션을 의도적으로 끊을 때 "잠시 후 돌아온다"고 알린다.
  • 다른 노드들이 일정 시간 그 경로를 유지한다(즉시 철회하지 않는다).
  • 재부팅 후 세션이 복구되면 경로가 다시 활성화된다.

이것이 "유지보수 중 트래픽 손실 최소화"의 BGP 메커니즘이다. graceful restart가 없으면 노드 재부팅 찰나에 그 노드의 Pod로 향하던 트래픽이 드롭된다. Calico BGP 모드의 운영 품질에 영향을 주는 설정.

Calico 배포 방식 — operator vs manifest

Calico 설치에 두 방식이 있다:

  • Tigera operator: Calico 전용 operator가 Calico 구성 요소를 관리. 선언적(Installation CRD). 업그레이드/설정 변경이 operator 경유. 권장.
  • 직접 manifest: YAML apply로 Calico 구성 요소를 직접 배포. 단순하지만 업그레이드/설정 변경이 수동.

operator가 권장되는 이유: Calico가 felix/typha/bird 등 여러 구성 요소 + IPPool/BGPConfiguration 등 CRD로 이루어져, operator가 이 _전체_를 일관되게 관리. 직접 manifest는 컴포넌트 간 버전 불일치 위험이 있다.

직접 확인하기 (CNI 교체 클러스터 필요)

기본 kind는 kindnetd CNI. Calico 테스트는 disableDefaultCNI: true kind config로 클러스터를 만든 뒤 Calico를 설치. k8s-verify 스킬 참조.

# Calico 설치 후
kubectl get pods -n calico-system -o wide
kubectl get ippool -o yaml | grep -E 'cidr|ipipMode|vxlanMode'

확인할 것: IPPool의 ipipMode/vxlanMode/bgp 설정이 현재 모드를 알려준다.

# 노드 라우팅 테이블에서 Pod 대역 확인
docker exec <node> ip route | grep -E '10.244|tunl0|vxlan'

확인할 것: BGP 모드면 Pod 대역이 직접 경로로, IPIP/VXLAN이면 tunl0/vxlan.calico 인터페이스로 나타난다.

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"Calico는 항상 BGP다" 모드에 따라 BGP/IPIP/VXLAN. 클라우드는 보통 VXLAN
"WireGuard 모드가 BGP를 대체한다" 같이 쓴다. BGP로 라우팅 + WireGuard로 암호화
"Calico는 CNI만 한다" 정책 엔진도
"IPIP와 VXLAN은 같다" IPIP=IP헤더 1개, VXLAN=UDP+이더넷. 클라우드 호환성 다름
"Calico IPAM은 동적 할당이다" 블록 기반. 노드마다 블록 할당(라우팅 집약 목적)
"BGP는 클라우드에서 쓴다" 반대. 온프렘 베어메탈에서 빛남. 클라우드는 VXLAN
"typha는 선택이라 안 써도 된다" 소규모 생략 가능하나 수백 노드 이상에선 필수(apiserver 부하)
"WireGuard는 같은 노드 Pod도 암호화" 아님. 노드 간만. 같은 노드는 로컬 브리지(평문)
"Calico는 iptables 고정이다" eBPF 모드도 지원. 현재 모드가 데이터플레인 성격을 결정

요약 — 이 글의 결론

  • Calico는 03장의 두 접근을 모두 구체화 — BGP(언더레이)와 IPIP/VXLAN(오버레이)을 모드로. 거기에 고급 정책을 독립적으로 얹는다.
  • BGP 모드가 Calico 시그니처. Pod 대역을 물리 라우팅(캡슐화 없음, 성능/가시성 좋음). 단 BGP 지원 네트워크 필요 — 온프렘 빛, 클라우드 제약.
  • BGP 토폴로지가 대규모 관건 — full mesh(소규모) vs route reflector(대규모). 토폴로지 설계 없이 노드 추가하면 BGP 피어 폭증.
  • IPIP/VXLAN 모드로 클라우드에서도 동작(캡슐화). 어디서든 되지만 오버헤드.
  • WireGuard 모드가 코드 변경 없이 Pod 통신 암호화(규정 충족). 라우팅 모드와 함께 쓴다. 노드 간에만. IPsec 대비 가벼운 현대 암호학.
  • 블록 기반 IPAM이 라우팅 집약(노드당 /24 광고)을 가능하게 한다. BGP 모드의 전제.
  • felix(노드 에이전트, 커널 프로그래밍) + typha(대규모 apiserver 중개) + bird(BGP)가 아키텍처 핵심. typha가 03장 list-watch 부하를 회피하는 수단.
  • eBPF 모드도 지원 — iptables 병목 회피. "Calico=iptables" 단순화는 틀림.

생각해 볼 문제

  1. AWS VPC에서 Calico를 BGP 모드로 설정했다. 왜 Pod 통신이 안 될 가능성이 높은가?
  2. WireGuard로 노드 간 Pod 트래픽을 암호화했다. 같은 노드의 두 Pod 통신은 암호화되는가? 왜?
  3. Calico IPAM 블록 크기를 /24로 잡았다. 노드에 Pod가 256개 넘게 필요하면?
  4. tcpdump로 노드 인터페이스를 잡았다. VXLAN 모드에선 Pod IP가 보이는가? IPIP에선?
  5. typha 없이 노드 500개 클러스터에서 Calico를 돌리면 생기는 문제는?
  6. 노드 1000개 클러스터에서 Calico BGP를 full mesh로 짰다. 피어 연결 수는? 왜 비현실적인가?
  7. Calico 정책(CRD)이 K8s 기본 NetworkPolicy보다 강력한 점을 한 가지 짚어 보라.
  8. 데이터플레인 모드(VXLAN)와 정책 모드(CRD 고급)를 독립적으로 선택하는 게 왜 유용한가?

참고

'Tech Artifacts > K8s Networking' 카테고리의 다른 글

K8s Networking - 06. network-policy  (0) 2026.07.14
K8s Networking - 05. cilium ebpf  (0) 2026.07.14
K8s Networking - 03. cni model  (0) 2026.07.14
K8s Networking - 02. coredns  (0) 2026.07.14
K8s Networking - 01. service kube-proxy  (0) 2026.07.13
은 관리자 편집기 위젯 라벨이 됨 -->