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기능 사용 방법은 마스커레이딩을 참고하세요.