Multi-Pool IPAM

Multi-Pool IPAM (Multi-Pool)

Multi-Pool IPAM 모드는 사용자가 정의한 작업 부하 애노테이션과 노드 라벨에 따라 여러 다양한 IPAM 풀에서 PodCIDR을 할당하는 것을 지원해요. 이 글에서는 풀 선택 우선순위, CiliumPodIPPool 리소스, 구성·마스커레이딩 동작을 알아볼게요.

출처: Multi-Pool

본문

Multi-Pool IPAM 모드는 사용자가 정의한 작업 부하 애노테이션과 노드 라벨에 따라 여러 다양한 IPAM 풀에서 PodCIDR을 할당하는 것을 지원합니다.

아키텍처

Multi-Pool IPAM 모드로 실행할 때 Cilium은 다음 우선순위 순서로 Pod의 풀을 선택합니다:

  • Pod나 Pod의 네임스페이스에 ipam.cilium.io/ip-pool= 애노테이션을 사용해 Pod의 풀을 명시적으로 이름 지정할 수 있습니다. ipam.cilium.io/ipv4-pool= 및 ipam.cilium.io/ipv6-pool= 애노테이션을 사용해 IPv4와 IPv6에 서로 다른 풀을 지정할 수 있어요.
  • spec.podSelector 및/또는 spec.namespaceSelector 로 라벨 셀렉터를 정의해 어떤 Pod가 풀에서 IP를 받을 수 있는지 지정할 수 있습니다. 두 셀렉터가 모두 정의되면, Pod가 풀에서 IP를 할당받으려면 Pod와 그 네임스페이스가 각각의 셀렉터와 일치해야 해요.
  • Pod 라벨 외에도 Cilium이 편의를 위해 추가하는 다음 두 합성 라벨과 일치시킬 수도 있습니다:
    • io.kubernetes.pod.namespace – Pod의 네임스페이스
    • io.kubernetes.pod.name – Pod의 이름

IP 할당 시점에 Cilium이 풀을 모르는 경우(레이스 조건이나 잘못된 구성으로 인해) Pod는 기본 풀에서 IP를 할당받습니다. 이 동작이 바람직하지 않다면, Pod 또는 네임스페이스에 ipam.cilium.io/require-pool-match="true" 애노테이션을 설정해 Pod가 기본이 아닌 풀과 일치할 때까지 IP 할당을 차단할 수 있어요.

Pod는 주어진 IP 패밀리에 대해 정확히 하나의 풀과 일치해야 합니다. 두 개 이상의 풀과 일치하면 IP 할당이 실패하고 오류가 기록됩니다. 따라서 풀에 겹치는 셀렉터가 없도록 해야 해요.

Pod나 네임스페이스에 명시적 IP 풀 애노테이션이 없거나, Pod나 네임스페이스가 어떤 셀렉터와도 일치하지 않으면 Pod의 IP는 default 라는 풀에서 할당됩니다.

애노테이션은 Pod가 생성될 때만 고려됩니다. 이미 실행 중인 Pod의 ip-pool 애노테이션을 변경해도 효과가 없어요.

CiliumNode 리소스는 추가 spec.ipam.pools 섹션으로 확장됩니다:

  • spec.ipam.pools.requested: 이 노드에 대한 IPAM 풀 요청 목록. 각 항목은 풀과 요청된 IP 주소 수를 지정합니다. 이 필드는 해당 노드에서 실행되는 Cilium 에이전트가 소유하고 작성합니다. 요청을 이행하기 위해 Cilium operator가 읽습니다.
  • spec.ipam.pools.allocated: 이 노드에 할당된 CIDR 목록과 할당된 풀. Cilium operator는 이 필드에 새 PodCIDR을 추가합니다. Cilium 에이전트는 해제했고 더 이상 사용하지 않는 PodCIDR을 제거합니다.

IP 풀은 클러스터 전체 CiliumPodIPPool 사용자 지정 리소스로 관리됩니다. 각 CiliumPodIPPool 은 per-node PodCIDR이 할당되는 클러스터 전체 CIDR을 포함합니다:

apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: green-pool
spec:
  ipv4:
    cidrs:
      - 10.20.0.0/16
      - 10.30.0.0/16
    maskSize: 24
  ipv6:
    cidrs:
      - fd00::/104
    maskSize: 120

새 풀은 런타임에 추가할 수 있습니다. 각 풀의 CIDR 목록도 런타임에 확장할 수 있어요. 사용 중인 CIDR은 제거하면 안 되며, Cilium 노드가 여전히 사용 중인 기존 풀은 삭제하면 안 됩니다. 사용 중인 풀을 업데이트해야 하는 경우, 업데이트 중 중단을 최소화하려면 이 #update-existing-ciliumpodippools 절차를 따르세요. 풀의 마스크 크기는 불변이며 모든 노드에 동일합니다. spec.allowFirstIP 및 spec.allowLastIP 필드도 불변입니다. 기본적으로 CiliumPodIPPool 에서 할당된 각 CIDR의 첫 번째와 마지막 주소는 예약되어 할당할 수 없습니다. 풀을 만들 때 spec.allowFirstIP 또는 spec.allowLastIP 를 true 로 설정해 두 주소 중 하나를 독립적으로 할당 가능하게 만들 수 있어요. 주소가 3개 미만인 풀(/31, /32, /127, /128)에는 이 한계가 없습니다.

구성

Multi-Pool IPAM은 ipam.mode=multi-pool Helm 값으로 활성화할 수 있어요. Cilium operator가 시작 시 CiliumPodIPPools 사용자 지정 리소스를 자동으로 만들게 하려면 ipam.operator.autoCreateCiliumPodIPPools Helm 값을 사용하세요. 이 값은 위에서 설명한 CiliumPodIPPools CRD 스키마를 따르는 맵을 포함합니다.

ipam:
  mode: multi-pool
  operator:
    autoCreateCiliumPodIPPools:
      default:
        ipv4:
          cidrs:
            - 10.10.0.0/16
          maskSize: 24
      other:
        ipv4:
          cidrs:
            - 10.20.0.0/16
          maskSize: 24

Note

이 모드를 Cilium에서 활성화하는 실용적인 튜토리얼은 CRD-Backed by Cilium Multi-Pool IPAM을 참고하세요.

기존 CiliumPodIPPools 업데이트

기존 CiliumPodIPPools 를 업데이트하는 것은 몇 가지 한계가 있습니다. 새 IPv4 또는 IPv6 CIDR을 추가해 풀을 확장하는 것은 가능하지만, 이미 사용 중인 CIDR을 삭제하거나 업데이트하는 것은 불가능해요. 이 제한은 일부 Pod가 같은 노드에서 여전히 이전 IP 풀을 사용하는 동안 Pod가 새 범위에서 IP를 받는 것을 방지합니다. 그러나 기존 CiliumPodIPPools 의 사용 중 CIDR을 업데이트하는 것 외에 다른 선택이 없다면, 다음 단계를 참조로 사용하세요.

IPAM 모드로 multi-pool 을 사용하는 Kubernetes 클러스터가 있다고 가정해 봅시다. 목표는 기존 기본 풀 CIDR을 다른 값으로 변경하고 Pod가 새 CIDR에서 IP 주소를 받도록 하는 것입니다. 풀의 CIDR을 변경하려면 기존 작업 부하의 IP를 재할당해야 해요. 이를 달성하려면 클러스터를 두 개의 노드 그룹으로 나눠 이전 CIDR을 사용하는 노드에서 새 CIDR을 사용하는 노드로 작업 부하를 마이그레이션할 수 있습니다.

아래 단계를 명확히 하기 위해 kind-worker 와 kind-control-plane 두 노드만 있는 kind 기반 클러스터를 고려해 봅시다. 이 클러스터에는 두 개의 복제본(노드당 하나씩)으로 nginx를 실행하는 배포와 default 풀을 설명하는 단일 CiliumPodIPPool 리소스가 있습니다:

apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: default
spec:
  ipv4:
    cidrs:
    - 10.10.0.0/16
    maskSize: 24

클러스터를 나누려면 먼저 CIDR을 먼저 업데이트하고 싶은 노드의 하위 집합을 골라 노드 그룹 1이라고 부르세요. 노드 그룹 1보다 나중에 CIDR을 업데이트할 다른 노드는 노드 그룹 2라고 부르겠습니다. 이 예시에서 노드 그룹 1은 kind-worker 만으로 구성되고, 노드 그룹 2는 kind-control-plane 노드만 포함합니다:

$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE                 NOMINATED NODE   READINESS GATES
nginx-66686b6766-9t4cp   1/1     Running   0          34s   10.10.1.191   kind-worker          <none>           <none>
nginx-66686b6766-jnvrx   1/1     Running   0          34s   10.10.0.77    kind-control-plane   <none>           <none>

기존 풀을 이전 10.10.0.0/16 대신 10.20.0.0/16 CIDR을 사용하도록 업데이트하세요.

cat <<EOF | kubectl apply -f -
apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: default
spec:
  ipv4:
    cidrs:
    - 10.20.0.0/16
    maskSize: 24
EOF

Cilium operator가 노드에서 여전히 사용 중이지만 풀에서 제거된 각 CIDR 블록에 대해 경고를 보고하는 것을 주목하세요:

$ kubectl -n kube-system logs deploy/cilium-operator | grep "CIDR from pool still in use by node"
...
time=2025-11-01T11:24:13.076246842Z level=warn msg="CIDR from pool still in use by node" module=operator.operator-controlplane.leader-lifecycle.legacy-cell cidr=10.10.0.0/24 poolName=default node=kind-control-plane
time=2025-11-01T11:24:13.076274725Z level=warn msg="CIDR from pool still in use by node" module=operator.operator-controlplane.leader-lifecycle.legacy-cell cidr=10.10.1.0/24 poolName=default node=kind-worker
...

Cilium operator를 재시작합니다.

kubectl -n kube-system rollout restart deploy/cilium-operator

대안으로, helm 값의 autoCreateCiliumPodIPPools 를 통해 기존 풀을 업데이트한 다음 기존 CiliumPodIPPools 를 삭제하고 Cilium operator를 재시작해 새 CiliumPodIPPools 를 자동으로 만들 수 있습니다.

노드 그룹 1을 cordon하고 노드 그룹 1에서 Pod를 축출합니다.

kubectl cordon kind-worker

kubectl drain kind-worker --ignore-daemonsets

kind-worker에서 실행되는 nginx Pod는 업데이트된 풀의 IP 주소로 kind-control-plane에 다시 스케줄됩니다:

$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP             NODE                 NOMINATED NODE   READINESS GATES
nginx-66686b6766-2svdm   1/1     Running   0          3m49s   10.20.11.182   kind-control-plane   <none>           <none>
nginx-66686b6766-jnvrx   1/1     Running   0          20m     10.10.0.77     kind-control-plane   <none>           <none>

노드 그룹 1에 대한 CiliumNodes 를 삭제하고, 노드 그룹 1에서 실행되는 Cilium 에이전트를 재시작하고, 노드 그룹 1을 uncordon합니다.

kubectl delete cn kind-worker

kubectl -n kube-system delete pod --field-selector spec.nodeName=kind-worker --selector="app.kubernetes.io/name=cilium-agent"

kubectl uncordon kind-worker

노드 그룹 2를 cordon하고 노드 그룹 2에서 Pod를 축출합니다.

kubectl cordon kind-control-plane

kubectl drain kind-control-plane --ignore-daemonsets

kind-control-plane에서 실행되는 두 nginx Pod 모두 업데이트된 풀의 IP 주소로 kind-worker에 다시 스케줄됩니다:

$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP             NODE                 NOMINATED NODE   READINESS GATES
nginx-66686b6766-2svdm   1/1     Running   0          3m49s   10.20.11.182   kind-control-plane   <none>           <none>
nginx-66686b6766-jnvrx   1/1     Running   0          20m     10.10.0.77     kind-control-plane   <none>           <none>

노드 그룹 2에 대한 CiliumNodes 를 삭제하고, 노드 그룹 2에서 실행되는 Cilium 에이전트를 재시작하고, 노드 그룹 2를 uncordon합니다.

kubectl delete cn kind-control-plane

kubectl -n kube-system delete pod --field-selector spec.nodeName=kind-control-plane --selector="app.kubernetes.io/name=cilium-agent"

kubectl uncordon kind-control-plane

모든 실행 중인 Pod는 이제 업데이트된 풀의 IP 주소를 가지며, 새 작업 부하의 IP 주소도 업데이트된 풀에서 받게 됩니다.

(선택) 작업 부하가 클러스터의 노드에 균등하게 분산되도록 Pod를 다시 스케줄합니다.

노드별 기본 풀

Cilium은 노드의 라벨에 따라 노드에 특정 IP 풀을 할당할 수 있습니다. 이 기능은 다른 노드가 각 데이터센터의 서브넷과 정렬된 IP 범위를 요구하는 멀티 데이터센터 환경에서 특히 유용해요. 예를 들어 DC1의 노드는 10.1.0.0/16 범위를 사용하고 DC2의 노드는 10.2.0.0/16 범위를 사용할 수 있습니다.

특히 특정 노드 라벨과 일치하는 노드의 CiliumNodeConfig 리소스에 ipam-default-ip-pool 을 설정해 노드별 기본 풀을 설정할 수 있습니다.

apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: dc1-pool
spec:
  ipv4:
    cidrs:
      - 10.1.0.0/16
    maskSize: 24
apiVersion: cilium.io/v2
kind: CiliumNodeConfig
metadata:
  name: ip-pool-dc1
  namespace: kube-system
spec:
  defaults:
    ipam-default-ip-pool: dc1-pool
  nodeSelector:
    matchLabels:
      topology.kubernetes.io/zone: dc1
apiVersion: cilium.io/v2alpha1
kind: CiliumPodIPPool
metadata:
  name: dc2-pool
spec:
  ipv4:
    cidrs:
      - 10.2.0.0/16
    maskSize: 24
apiVersion: cilium.io/v2
kind: CiliumNodeConfig
metadata:
  name: ip-pool-dc2
  namespace: kube-system
spec:
  defaults:
    ipam-default-ip-pool: dc2-pool
  nodeSelector:
    matchLabels:
      topology.kubernetes.io/zone: dc2

할당 매개변수

Cilium 에이전트는 각 풀에서 IP를 사전 할당하도록 구성할 수 있습니다. 이 동작은 ipam-multi-pool-pre-allocation 플래그로 제어할 수 있어요. 이는 = 형식의 키-값 맵을 포함하며, 여기서 preAllocIPs 는 로컬 노드에 사전 할당할 IP 수를 지정합니다. 각 주소 패밀리에 대해 같은 수의 IP가 사전 할당됩니다. 즉 IPv4와 IPv6 CIDR을 모두 포함하는 풀은 preAllocIPs 개의 IPv4 주소와 preAllocIPs 개의 IPv6 주소를 사전 할당한다는 뜻이에요.

이 플래그는 기본적으로 default=8 이며, 이는 default 풀에서 8개의 IP를 사전 할당한다는 의미입니다. ipam-multi-pool-pre-allocation 맵에 항목이 없는 다른 모든 풀은 preAllocIPs 가 0이라고 가정합니다. 즉 해당 풀에 IP가 사전 할당되지 않습니다.

사용 중인 IP 수와 대기 중인 IP 할당 요청 수에 따라 Cilium 에이전트는 preAllocIPs 보다 많은 IP를 사전 할당할 수 있어요. Cilium 에이전트가 각 풀에서 필요한 절대 IP 수를 계산하는 공식은 다음과 같습니다:

neededIPs = roundUp(inUseIPs + pendingIPs + preAllocIPs, preAllocIPs)

여기서 inUseIPs 는 현재 사용 중인 IP 수, pendingIPs 는 대기 중인 Pod(노드에 스케줄됐지만 아직 IP를 받지 못한 Pod)가 있는 IP 수, preAllocIPs 는 버퍼로 사전 할당하고 싶은 최소 IP 수, 즉 ipam-multi-pool-pre-allocation 맵에서 가져온 값입니다.

할당된 PodCIDR로의 라우팅

CiliumPodIPPools 에서 할당된 PodCIDR은 Cilium BGP Control Plane (MultiPool IPAM)으로 네트워크에 공지할 수 있습니다. 대안으로 autoDirectNodeRoutes Helm 옵션을 사용해 L2 네트워크의 노드 간 자동 라우팅을 활성화할 수 있어요.

마스커레이드 동작

Multi-pool IPAM과 BGP 컨트롤 플레인을 결합할 때, 그러한 풀의 연결을 마스커레이드하지 않는 것이 유용할 수 있어요. Pod IP가 BGP를 통해 언더레이 네트워크에 광고되고 반환 트래픽이 다시 찾아갈 수 있으므로, pod 소스 IP가 Pod가 있는 노드의 IP로 마스커레이드되는 것이 바람직하지 않을 수 있습니다.

마스커레이드 대상과 비-마스커레이드 pod 목적지 IP 사이에 겹침이 있을 수 있으므로 목적지 IP만으로( --ipvX-native-routing-cidr 플래그 또는 ip-masq-agent 규칙을 통해) 마스커레이드하지 말아야 할 Pod를 식별하는 것이 항상 가능한 것은 아니에요. 이런 경우 eBPF 기반 마스커레이딩이 활성화되면, 모든 비-기본 풀에 대한 마스커레이딩을 비활성화하는 --only-masquerade-default-pool 플래그를 사용해 IP 풀을 마스커레이딩에서 제외할 수 있습니다. 대안으로 CiliumPodIPPool 리소스에 ipam.cilium.io/skip-masquerade="true" 애노테이션을 붙여 풀별로 구성할 수 있어요.

플래그나 애노테이션을 사용하면 Pod가 클러스터 밖의 엔드포인트에 연결할 때 소스 IP가 보존되어, 언더레이 네트워크에서 다른 풀의 Pod와 구분할 수 있습니다. 그러면 Pod가 네트워크 인프라의 방화벽이나 NAT 규칙과 일치할 수 있어요.

Pod가 IP를 할당받은 후 플래그나 애노테이션을 변경해도 Pod가 다시 스케줄될 때까지는 그 Pod의 마스커레이드 동작이 바뀌지 않습니다.

한계

Multi-Pool IPAM 모드로 실행되는 Cilium에는 다음 한계가 적용됩니다:

Warning

겹치는 CIDR을 가진 IPAM 풀은 지원되지 않습니다. Cilium이 IPCache를 통해 엔드포인트의 보안 아이덴티티를 결정하는 방식 때문에 각 pod IP는 클러스터에서 고유해야 해요.

  • iptables 기반 마스커레이딩은 egressMasqueradeInterfaces 가 설정되어야 합니다 (마스커레이딩 구현 모드와 GitHub issue 22273 참고). 대안으로 eBPF 기반 마스커레이딩이 완전히 지원되므로 대신 사용할 수 있어요. 사용된 IPAM 풀이 공통 네이티브 라우팅 CIDR에 속하지 않으면, 여러 분리된 비-마스커레이딩 CIDR을 정의할 수 있는 ip-masq-agent 를 사용하고 싶을 수 있습니다. ip-masq-agent 기능 사용 방법은 마스커레이딩을 참고하세요.

더 알아보기 (Learn more)