Theme:

서로 다른 노드에 있는 Pod이 IP 주소만으로 직접 통신할 수 있다면, 그 네트워크는 누가 어떻게 구성하는 걸까요?

Kubernetes 클러스터에서 curl 10.244.2.5:8080을 실행하면 다른 노드의 Pod에 요청이 도달합니다. docker-compose에서는 같은 호스트의 컨테이너끼리만 통신했는데, Kubernetes는 노드가 달라도 마치 같은 네트워크에 있는 것처럼 동작합니다. 이것을 가능하게 하는 것이 Kubernetes 네트워크 모델과 CNI 플러그인입니다.

Kubernetes 네트워크 모델의 세 가지 원칙

Kubernetes는 네트워크에 대해 세 가지를 요구합니다.

  1. 모든 Pod은 NAT 없이 다른 Pod과 통신할 수 있어야 한다
  2. 모든 노드는 NAT 없이 모든 Pod과 통신할 수 있어야 한다
  3. Pod이 보는 자신의 IP는 다른 Pod이 보는 IP와 동일해야 한다

이 원칙을 "플랫 네트워크"라고 부릅니다. 구현 방식은 정하지 않고 요구사항만 정의했기 때문에, 다양한 CNI 플러그인이 각자의 방식으로 이를 구현합니다.

CNI (Container Network Interface)

CNI는 컨테이너 런타임이 네트워크를 설정할 때 호출하는 표준 인터페이스입니다. Pod이 생성되면 kubelet이 CNI 플러그인을 호출하여 네트워크를 구성합니다.

PLAINTEXT
Pod 생성 → kubelet → CNI 플러그인 호출 → veth 쌍 생성 → IP 할당 → 라우팅 설정

CNI 플러그인이 하는 일을 구체적으로 보면 다음과 같습니다.

  1. veth pair 생성: 컨테이너 내부와 호스트를 연결하는 가상 네트워크 인터페이스
  2. IP 주소 할당: IPAM(IP Address Management)을 통해 Pod에 IP를 부여
  3. 라우팅 규칙 설정: 다른 노드의 Pod으로 가는 경로를 설정

같은 노드 내 통신

같은 노드에 있는 Pod끼리의 통신은 비교적 단순합니다.

PLAINTEXT
Pod A (10.244.1.2) → veth → 가상 브리지 (cbr0/cni0) → veth → Pod B (10.244.1.3)

각 Pod은 veth pair를 통해 노드의 가상 브리지에 연결되어 있고, 같은 브리지에 연결된 Pod끼리는 L2 스위칭으로 직접 통신합니다.

SHELL
# 노드에서 네트워크 인터페이스 확인
ip link show | grep veth
# veth1234@if3: ...
# veth5678@if5: ...

# 가상 브리지 확인
bridge link show

다른 노드 간 통신

노드가 다를 때는 CNI 플러그인에 따라 방식이 달라집니다. 크게 두 가지 접근법이 있습니다.

오버레이 네트워크 (Encapsulation)

기존 노드 네트워크 위에 가상 네트워크를 구성합니다. Pod 패킷을 다른 프로토콜(VXLAN, Geneve 등)로 감싸서 노드 간 전송합니다.

PLAINTEXT
Pod A → veth → 브리지 → VXLAN 캡슐화 → 노드 네트워크 → VXLAN 디캡슐화 → 브리지 → veth → Pod B

장점은 기존 네트워크 인프라를 변경할 필요가 없다는 것이고, 단점은 캡슐화/디캡슐화 오버헤드가 있다는 것입니다.

네이티브 라우팅 (No Encapsulation)

노드의 라우팅 테이블을 직접 수정하여 Pod 네트워크를 라우팅합니다.

PLAINTEXT
Pod A → veth → 브리지 → 노드 라우팅 → 상대 노드 라우팅 → 브리지 → veth → Pod B

오버헤드가 없어 성능이 좋지만, 네트워크 인프라(라우터)가 Pod 서브넷을 이해해야 합니다.

주요 CNI 플러그인 비교

Calico

가장 널리 사용되는 CNI 플러그인입니다.

  • 기본 방식: BGP 라우팅 (네이티브)
  • 대안 방식: VXLAN 오버레이 (BGP 사용 불가 시)
  • Network Policy: 자체 구현으로 강력한 네트워크 정책 지원
  • 성능: 네이티브 라우팅 시 매우 우수
YAML
# Calico 설치 (Operator 방식)
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    bgp: Enabled
    ipPools:
      - cidr: 10.244.0.0/16
        encapsulation: None  # 네이티브 라우팅
        natOutgoing: Enabled

Cilium

eBPF 기반의 차세대 CNI 플러그인입니다.

  • 기본 방식: eBPF (커널 수준 패킷 처리)
  • 특징: kube-proxy 대체 가능, L7 정책 지원
  • Network Policy: Kubernetes 표준 + 확장 CiliumNetworkPolicy
  • 관찰: Hubble로 네트워크 흐름 시각화
SHELL
# Cilium 설치 (Helm)
helm install cilium cilium/cilium \
  --namespace kube-system \
  --set kubeProxyReplacement=true \
  --set hubble.enabled=true

Cilium의 eBPF 방식은 iptables를 우회하므로 Service 수가 많은 대규모 클러스터에서 성능 이점이 큽니다.

Flannel

가장 단순한 CNI 플러그인입니다.

  • 기본 방식: VXLAN 오버레이
  • 특징: 설정이 간단, 학습용/소규모 클러스터에 적합
  • Network Policy: 미지원 (Calico와 조합하여 Canal로 사용)
SHELL
# Flannel 설치
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

CNI 플러그인 선택 가이드

기준CalicoCiliumFlannel
성능우수매우 우수보통
Network Policy지원확장 지원미지원
복잡도중간높음낮음
대규모 클러스터적합매우 적합부적합
학습 곡선중간높음낮음

Pod CIDR과 IP 할당

각 노드에는 Pod CIDR이라는 IP 대역이 할당됩니다.

SHELL
# 노드별 Pod CIDR 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
# node-1  10.244.0.0/24
# node-2  10.244.1.0/24
# node-3  10.244.2.0/24

node-1에 생성된 Pod은 10.244.0.x 대역의 IP를 받고, node-2의 Pod은 10.244.1.x 대역을 사용합니다. CNI의 IPAM 컴포넌트가 이 범위 내에서 IP를 할당합니다.

클러스터 네트워크 디버깅

네트워크 문제가 발생했을 때 확인할 사항입니다.

SHELL
# Pod IP와 노드 확인
kubectl get pods -o wide
# NAME      READY   STATUS    IP           NODE
# app-1     1/1     Running   10.244.1.5   node-1
# app-2     1/1     Running   10.244.2.3   node-2

# Pod 간 연결 테스트
kubectl exec app-1 -- curl -s http://10.244.2.3:8080

# Pod 내부 네트워크 확인
kubectl exec app-1 -- ip addr
kubectl exec app-1 -- ip route

# DNS 확인
kubectl exec app-1 -- nslookup kubernetes.default

# CNI 플러그인 상태 확인
kubectl get pods -n kube-system | grep -E "calico|cilium|flannel"

Container-to-Container 통신

같은 Pod 내의 컨테이너들은 localhost로 통신합니다. Pod은 네트워크 네임스페이스를 공유하기 때문입니다.

YAML
spec:
  containers:
    - name: app
      image: my-app:1.0
      ports:
        - containerPort: 8080
    - name: sidecar
      image: envoy:latest
      # app 컨테이너에 localhost:8080으로 접근 가능

이것이 Pod이 "논리적 호스트"로 불리는 이유입니다. 같은 Pod의 컨테이너는 포트 충돌에 주의해야 합니다.

정리

Kubernetes 네트워크 모델은 "모든 Pod이 NAT 없이 통신"이라는 단순한 원칙을 세우고, CNI 플러그인이 이를 구현합니다. Calico는 BGP 라우팅으로 성능이 우수하고, Cilium은 eBPF로 차세대 네트워킹을 제공하며, Flannel은 단순함이 장점입니다. 네트워크 문제를 디버깅하려면 Pod IP 할당, 노드 간 라우팅, CNI 플러그인 상태를 순서대로 확인하는 것이 효과적입니다.

댓글 로딩 중...