Cilium 은 네트워크의 다양한 계층에서 보안 기능을 제공합니다. Layer 3에서는 Identity-Based 보안을 제공하고, Layer 4에서는 Port Level 보안을, Layer 7에서는 Application protocol Level 보안을 지원합니다.
Layer 3 Identity-Based
Security-ID
저번 시간에 공유드렸듯이, 쿠버네티스 환경에서 파드들의 IP 가 수시로 변하기 때문에 ip 기반 접근 통제 보다는 id 기반 접근 정책을 통해 파드들의 통신을 제어합니다. Cilium 에서는 모든 엔드포인트에 대해 고유한 ID 가 할당됩니다. 이 ID 는 클러스터 내에서 유일한 ID 로 구성되며, 동일한 Security 정책을 사용하는 엔드포인트들은 동일한 ID 를 공유하게 됩니다.
위와 같이 ciliumendpoint 들을 조회했을 때, 동일한 보안 정책을 부여받는 엔드포인트끼리 동일한 security ID 값을 부여받는 것을 알 수 있습니다.
파드 또는 컨테이너가 시작되면 Cilium 은 컨테이너 런타임에서 수신한 이벤트를 기반으로 엔드포인트를 생성합니다. 다음 단계로 Cilium 은 생성된 엔드포인트의 ID 를 확인하며, 포드나 컨테이너의 Labels가 변경될 때마다 ID 가 재확인되고 필요에 따라 자동으로 수정됩니다. Labels 변경 시 endpoint가 waiting-for-identity 상태로 전환되어 새로운 identity 를 할당받게 됩니다. 이로 인해 security labels 와 관련된 네트워크 정책도 자동으로 재적용됩니다. 예를 들어 간단한 pod을 생성하면 초기 security identity 가 할당되고, kubectl label 명령으로 labels 를 변경하면 새로운 identity 값으로 업데이트되는 것을 확인할 수 있습니다.
참고로 security label 중 reserved 문자열 접두사가 붙은 라벨들은 Cilium 에서 관리하지 않는 네트워크 엔드포인트와의 통신을 허용하기 위해 예약된 라벨들입니다.
엔드포인트 기반 정책
두 엔드포인트가 Cilium에 의해 관리되고 레이블이 할당되어 있는 경우 유용합니다. 이 방법의 장점은 IP 주소가 정책에 하드코딩되지 않고 유연하게 대처가 가능하다는 점입니다.
cilium 에서 아래에 해당하는 프로토콜 중 L7 정책을 통해 L7 어플리케이션 트래픽들을 제어할 수 있습니다.
// L7Rules is a union of port level rule types. Mixing of different port
// level rule types is disallowed, so exactly one of the following must be set.
// If none are specified, then no additional port level rules are applied.
type L7Rules struct {
// HTTP specific rules.
//
// +optional
HTTP []PortRuleHTTP `json:"http,omitempty"`
// Kafka-specific rules.
//
// +optional
Kafka []PortRuleKafka `json:"kafka,omitempty"`
// DNS-specific rules.
//
// +optional
DNS []PortRuleDNS `json:"dns,omitempty"`
}
3계층 및 4계층 정책과 달리 7계층 규칙 위반 시 패킷을 드랍하지 않고 애플리케이션 프로토콜별 접근 거부 메시지를 제공합니다. 예를 들어, HTTP 의 경우 403 Forbidden 을, DNS 의 경우, DNS 거부 응답을 보냅니다. 7 계층 정책은 Envoy 프록시를 통해 트래픽을 검사 후 처리를 진행합니다.
HTTP 정책의 경우 Path, Method, Hosts, Headers 를 정책의 구성요소로 사용합니다.
참고로 쿠버네티스에서 DNS 정책을 적용할 때, service.namespace.svc.cluster.local에 대한 쿼리는 명시적으로 허용되어야 합니다. (matchPattern: *.*.svc.cluster.local.) 마찬가지로, FQDN을 완성하기 위해 DNS 검색 목록에 의존하는 쿼리는 전체가 허용되어야 합니다. 예를 들어, servicename로 성공하는 쿼리는 또는 servicename.namespace.svc.cluster.local.로 허용되어야 합니다.
Cilium NetworkPolicy
Cilium 은 표준 Kubernetes 의 Network Policy 보다 더 많은 기능을 제공합니다.
Cilium Network Policy 정책은 statefull 하며 응답 패킷은 자동으로 허용됩니다. 보안 정책은 수신 또는 송신 시점에 적용되어 네트워크 트래픽을 제어합니다. 정책이 로드되지 않은 경우 모든 통신을 기본으로 허용합니다. 하지만 첫 번째 정책 규칙이 로드되는 즉시 정책 적용이 자동으로 활성화되며, 이후부터는 허용 목록 방식으로 동작하여 명시적으로 허용되지 않은 패킷은 드랍됩니다. 마찬가지로 L4 정책이 적용되지 않은 엔드포인트는 모든 포트와의 통신이 허용되지만, 하나 이상의 L4 정책이 연결되면 명시적으로 허용하지 않는 한 모든 연결이 차단됩니다.
마찬가지로, 2개의 레플리카를 10번에 걸쳐 생성하므로 20 개의 오브젝트를 생성 요청하는데 10 개의 bust 만 쓸 수 있으므로 1초씩 진행됩니다. jobIterations: 20, qps: 2, burst:20, objects.replicas: 2
같은 원리로 처음에 조건이 부합하여 한 번에 처리가 진행이 되다가 슬슬 처리가 늦어지는 것을 알 수 있습니다.
시나리오 2: 노드 1대에 최대 파드 배포 (150개)
jobIterations: 100, qps: 300, burst: 300, replicas: 1로 설정하여 최대 150개 파드 배포 테스트 해보겠습니다.
5개의 노드가 아직 pending 상태로 남아있습니다. 원인 파악을 위해 99번 파드에 대해 자세히 살펴보겠습니다.
"Too many pods. preemption: 0/1 nodes are availale:" 라는 메세지를 볼 수 있고, node 의 상태를 확인해보니
모든 가용영역을 다 사용한 것을 볼 수 있습니다.
기본적으로 쿠버네티스는 노드당 110개의 파드만 허용합니다. 더 많은 파드를 배포하려면 다음과 같이 설정을 변경해야 합니다.
# 현재 설정 확인
kubectl get cm -n kube-system kubelet-config -o yaml
# maxPods 설정 변경
docker exec -it myk8s-control-plane bash
cat /var/lib/kubelet/config.yaml
apt update && apt install vim -y
vim /var/lib/kubelet/config.yaml
# maxPods: 150 추가
systemctl restart kubelet
systemctl status kubelet
exit
시나리오 3: 파드 300개 배포 시도
jobIterations: 300, qps: 300, burst: 300 objects.replicas: 1로 설정하여 최대 300개 파드 배포 테스트를 해보겠습니다.
파드 배포하기 전, 넉넉하게 400 개의 maxPod 를 설정하고 테스트를 수행합니다.
# maxPods 설정 변경
docker exec -it myk8s-control-plane bash
cat /var/lib/kubelet/config.yaml
apt update && apt install vim -y
vim /var/lib/kubelet/config.yaml
# maxPods: 400 추가
systemctl restart kubelet
systemctl status kubelet
exit
maxPod 를 최대치까지 늘렸는데 containerCreating 된 상태의 pod 들이 존재합니다. pod 로그들을 통해 원인을 파악해보면,
배포 시에 PodCIDR 대역 문제가 발생하는 것을 볼 수 있습니다.("no IP addresses available in range set") 현재 PodCIDR 이 /24 대역(252개 IP)이므로 300개 파드를 생성할 수 없습니다.
K8S Performance
최대 수용 규모와 내부 구조
쿠버네티스의 퍼포먼스를 이해하려면, 쿠버네티스의 내부 구조와 처리 절차들을 이해하고 있어야 합니다.
먼저 쿠버네티스 v1.33 기준 단일 클러스터 최대 수용 규모와 절차들은 다음과 같습니다.
노드 수: 5,000대 이하
노드당 파드 수: 110개 이하
총 파드 수: 150,000개 이하
총 컨테이너 수: 300,000개 이하
Kubernetes는 실제 사용자의 워크로드를 실행하는 Data Plane과 클러스터 전체를 관리하는 Control Plane으로 구성됩니다. Control Plane은 주로 kube-apiserver와 etcd로 구성되며, 이 두 컴포넌트에서 문제가 발생하면 리소스 관리에 심각한 영향을 미칩니다.
etcd 는 데이터 일관성을 위해 Range 요청 처리 중 Store 에 Lock을 걸고 해당 데이터를 복제합니다. 이 과정에서 대량의 메모리가 필요하며, Zero Copy 로 구현할 경우 성능 저하가 심각합니다. 심지어 etcd 관련 api 제약사항이 아래와 같습니다.
etcd 에는 Range API 만 존재하여 대용량 데이터를 한 번에 처리해야 함
kube-apiserver 도 이를 처리할 대안이 제한적
2초간 100건의 동일한 요청이 들어오면 메모리 급증 불가피
문제 발생 조건들을 살펴보면 한 번에 조회 가능한 리소스 개수가 매우 많고, 해당 리소스를 조회하는 요청이 매우 많았습니다.
kube-apiserver는 spec.nodeName에 대한 인덱싱이 없어 etcd에서 모든 Pod를 검색해야 하므로, 결국 전체 데이터를 로드하게 됩니다.
해결 방안
1. API Limit/Continue 활용
limit 과 continue 를 활용해서 한 번에 조회하는 양을 제한하여 메모리 사용량을 덜 사용합니다.
# kubectl의 실제 요청 예시
kubectl get po -v6
# GET /api/v1/namespaces/default/pods?limit=500
# GET /api/v1/namespaces/default/pods?continue=eyJ2IjoibWV0YS5rOHMuaW8vdjEiLCJydiI6MzkzNDcsInN0YXJ0IjoidGVzdC01NzQ2ZDRjNTlmLTJuNTUyXHUwMDAwIn0&limit=500
2. ResourceVersion/ResourceVersionMatch 활용
Strong Consistency 가 필요하지 않은 경우 etcd 에 요청하지 않고 api server 에 존재하는 캐시된 데이터를 사용합니다.
# resourceVersion="0"으로 요청하면 kube-apiserver 캐시에서 데이터 반환
curl "127.0.0.1:8001/api/v1/pods?resourceVersion=0"
이 방법을 사용하면 etcd 부하는 완전히 제거되지만, kube-apiserver 의 OOM은 여전히 발생할 수 있습니다.
100 개의 네임스페이스에 각각 Deployment, ConfigMap, Secret, Service 를 QPS 100 으로 대량 생성하고 생성된 리소스들을 패치하여 API 서버의 UPDATE 성능을 측정합니다. 이후 모든 리소스를 순차적으로 삭제하여 DELETE API 성능까지 종합적으로 테스트하는 예제입니다.
모니터링
다른 클라이언트로부터 받는 kube-apiserver 요청의 개수를 모니터링하고 싶다면, API 서버 QPS를 리소스별, 요청 타입별, 응답 코드별 모니터링을 진행하면 됩니다.
최근 5분간 Kubernetes API 서버가 처리한 요청에 대한 집계
# 응답 코드별 집계
sum by(verb) (irate(apiserver_request_total{job="apiserver"}[5m]))
# 리소스, 응답 코드, verb 별 집계
sum by(resource, code, verb) (irate(apiserver_request_total{job="apiserver"}[5m]))
# 최종
sum by(resource, code, verb) (rate(apiserver_request_total{resource=~".+"}[5m]))
or
sum by(resource, code, verb) (irate(apiserver_request_total{resource=~".+"}[5m]))
Cilium Performance
cilium 의 퍼포먼스를 이해하려면, cilium 의 내부 구조와 처리 절차들을 이해하고 있어야 합니다.
Cilium 내부 구조
cilium 개발자들은 kube-apiserver 나 clustermesh 에서 이벤트를 받아 ebpf 프로그램으로 처리하는데, 각 구간마다 장애가 발생한다면 어떻게 처리해야 할지 고민이 되었습니다.
이벤트가 적절한지 검증이 필요했고, 장애가 발생할 경우 재시도 로직이 필요했고, 상태를 저장할 필요가 있었습니다.
stateDB 와 효율적인 업데이트를 위한 Reconciler 컴포넌트를 중간에 두어 처리할 수 있게 하였습니다.
그래서 위와 같이 상태를 저장하는 StateDB 와 안정적인 운영을 위한 reconciler 컴포넌트(재시도 로직 및 헬스, 메트릭 체크) 를 도입하게 되었습니다.
cilium 을 설치하기 위해 disableDefaultCNI: true 옵션과 kubeProxyMode: none 옵션을 사용해서 CNI 없이 클러스터 구성을 하고 이후 cilium CNI 플러그인을 설치해서 네트워킹을 활성화합니다. 모니터링을 위한 prometheus 와 grafana 도 설치합니다.
참고로 control-plane 을 3개를 띄우면 HA Proxy 를 구성할 수 있다고 합니다.
# Prometheus Target connection refused bind-address 설정 : kube-controller-manager , kube-scheduler , etcd , kube-proxy
kind create cluster --name myk8s --image kindest/node:v1.33.2 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
networking:
apiServerAddress: "127.0.0.1"
apiServerPort: 6443
disableDefaultCNI: true
kubeProxyMode: none
EOF
#
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3e0a95aff7a6 kindest/haproxy:v20230606-42a2262b "haproxy -W -db -f /…" 12 minutes ago Up 12 minutes 127.0.0.1:6443->6443/tcp myk8s-external-load-balancer
981cf7c18ac6 kindest/node:v1.33.2 "/usr/local/bin/entr…" 12 minutes ago Up 12 minutes 127.0.0.1:49776->6443/tcp myk8s-control-plane2
40b5b2ebe5bf kindest/node:v1.33.2 "/usr/local/bin/entr…" 12 minutes ago Up 12 minutes 127.0.0.1:49775->6443/tcp myk8s-control-plane
6aa3d5b5b1ab kindest/node:v1.33.2 "/usr/local/bin/entr…" 12 minutes ago Up 12 minutes 127.0.0.1:49777->6443/tcp myk8s-control-plane3
Cilium 테스트
cilium 에서는 네트워크 관련 성능 및 기능 테스트 도구를 제공합니다. cilium connectivity -h 명령어로 테스트 관련 명령어를 확인할 수 있습니다.
cilium connectivity -h
Connectivity troubleshooting
Available Commands:
perf Test network performance
test Validate connectivity in cluster
test 명령어 같은 경우에는 네트워크 정책이 없을 때 outside 로 통신이 되는지 여부, 모든 ingress deny 하고 나서 통신이 되는지 여부 등 통신 pass / fail 의 점검을 할 수 있습니다. perf 명령어는 같은 노드 내부에서 통신 성능 테스트를 하거나, host to pod 경유 흐름에서 통신 성능 테스트를 수행합니다.
ciliumEndpointSlice.enabled=true 옵션으로 CES 를 사용할 수 있으며, kubectl get ciliumendpointslices.cilium.io -A 명령어로 watch 대상을 확인할 수 있습니다. 해당 옵션의 활성화 여부에 따라 차이가 많이 나는 것을 확인할 수 있습니다.
eBPF Host Routing , Netkit
cilium 은 ebpf 를 통해 네트워크 스택을 우회하고 host routing 을 지원하고 있습니다. ingress 의 경우 cpu 의 큐잉을 거치지 않지만, egress 의 경우 어쩔 수 없이 cpu 의 큐잉을 사용하고 있습니다.
netkit 디바이스를 사용하면 veth 가 쌍으로 존재해 egress 의 경우에도 ebpf 로 처리할 수 있어 컨테이너 네트워크의 오버헤드가 사라졌다라고 홍보하고 있습니다. (?)
위 명령어로 netkit 을 사용할 수 있지만 kernel 이 6.7 이상이어야 하고, CONFIG_NETKIT 옵션 활성화가 필요합니다.
cilium 성능 모니터링 메트릭
Cilium 상태 확인
# Cilium 상태 점검
cilium status
# BPF 맵 상태 확인
cilium bpf map list
# 메트릭 엔드포인트 확인
cilium metrics
주요 성능 메트릭
# BPF Map 연산 모니터링
cilium_bpf_map_ops_total
# 데이터패스 패킷 처리
cilium_datapath_packets_total
# 정책 계산 시간
cilium_policy_regeneration_time_stats_seconds
# 엔드포인트 상태 변화
cilium_endpoint_state_count
특징:nodeSelector 를 통해 특정 노드들을 선택하여 광고 가능 (일관된 BGP 설정 관리)
CiliumBGPPeerConfig
역할: CiliumBGPClusterConfig 에 등록된 클러스터 내 bgp 인스턴스의 BGP 피어링 설정
특징: neighbor 설정과 peering 상세 설정을 분리하여 여러 피어에서 동일한 설정 공유 가능
CiliumBGPAdvertisement
역할: BGP 라우팅 테이블에 주입할 광고 유형 정의 (Pod CIDR, Service IP 등 다양한 타입 지원)
65000 AS 인 192.168.1.200 라우터와 neighboor 설정하고 ipv4의 podCIDR 대역을 홍보하는 설정입니다.
모든 설정이 완료되면, k8s control plane 노드에서 bgp peers 설정을 확인할 수 있습니다.
라우터 노드에서도 k8s 의 podCIDR 대역이 라우팅 된 것을 확인할 수 있습니다.
하지만 아직도 다른 네트워크 간 통신이 되지 않고 있습니다. BGP 프로토콜로 인해 다른 neighbor 에 있는 podCIDR 대역을 받아왔지만,
커널 라우팅 테이블에는 자동으로 올라가지 않았습니다. 여전히 라우팅 테이블에 podCIDR 대역이 보이지 않네요.
Cilium의 BGP는 GoBGP 기반으로 구성되어 있는데 기본적으로 disable-telemetry, disable-fib 상태로 빌드가 되기 때문에, 수신한 경로를 Linux 커널(FIB) 에 바로 주입하지 않는 것으로 추정됩니다. 여러 개의 podCIDR 대역들을 네트워크 장비에 전달하면 그 장비가 직접 라우팅을 진행하면 되기 때문에 Cilium 입장에서는 받을 필요가 없을 수도 있을 것 같습니다.
실무 환경에서는 default gateway 가 존재할 것이기 때문에 통신은 무난히 진행될 것으로 생각됩니다. 다만, 실습 환경에서는 k8s 대역을 eth1 인터페이스로, 인터넷을 eth0 인터페이스로 사용하고 있기 때문에 podCIDR 대역을 eth1 통해서 라우팅 될 수 있도록 조정이 필요합니다.
# k8s 파드 사용 대역 통신 전체는 eth1을 통해서 라우팅 설정
ip route add 172.20.0.0/16 via 192.168.10.200
sshpass -p 'vagrant' ssh vagrant@k8s-w1 sudo ip route add 172.20.0.0/16 via 192.168.10.200
sshpass -p 'vagrant' ssh vagrant@k8s-w0 sudo ip route add 172.20.0.0/16 via 192.168.20.200
이제 정상적으로 통신이 되고 있습니다.
Service IP BGP 설정
지금까지 알아본 BGP 설정은 podCIDR 대역을 홍보한 설정이지만, 저번 시간에 알아본 Service IP (External IP) 는 어떻게 설정해야 할까요?
위에서 BGP peering 설정은 완료했으니 "metadata.labels.advertise: bgp" 라벨만 고정으로 해서 Service 유형의 CiliumBGPAdvertisement 를 통해 서비스 IP 를 광고해주면 됩니다.
정상적으로 router 노드에 Service IP 가 라우팅 되는 것을 확인할 수 있습니다. 로드밸런싱도 정상적으로 수행이 될까요?
router 노드에서 서비스로 통신 시도 시 트래픽이 골고루 분산되는 것처럼 보입니다. 현재 3개의 replica 구성을 2개의 replica 구성으로 변경해보았습니다.
2개의 replica 로 줄어들면서 k8s-ctr 노드와 k8s-w0 노드에만 webpod 가 세팅이 되어 있지만, bgp 라우팅에는 3개의 노드가 아직 구성되어 있습니다. 이유는 Service 의 externalTrafficPolicy 속성이 cluster(default) 이기 때문입니다. cluster 타입을 local 로 변경하면 pod 를 가지고 있는 노드만 광고하게 됩니다. (아래 정보에서 2번 방식에서 1번 방식으로 변경된 것입니다.)
1. Router 에서 BGP ECMP MultiPath 로 인해 Cilium BGP Peer 중 하나의 노드로 전달 2. 해당 트래픽의 원래 목적지인 k8s-ctr 의 파드로 요청을 전달 3. 해당 노드의 파드가 요청을 처리하고 응답 리턴을 위해서, NAT 를 수행했던 노드(k8s-w1) 로 다시 전달 4. NAT 를 수행했던 연결 정보를 확인해서, Reverse NAT 를 수행해서 최종 응답을 리턴
1. Router 에서 BGP ECMP MultiPath 로 인해 Cilium BGP Peer 중 하나의 노드로 전달 2. 해당 트래픽의 원래 목적지인 k8s-ctr 의 파드로 요청을 전달
이 때, Router 로 바로 리턴하기 위해서(DSR) 최초 접속했던 클라이언트의 정보를 GENEVE 헤더에 감싸 전달
(클라이언트의 세션에 대한 유지를 위해서 Maglev 알고리즘을 사용)
3. 해당 노드의 파드가 요청을 처리하고 GEVEVE 헤더 정보를 활용하여 Reverse NAT 을 수행해서 응답을 바로 리턴
kubectl patch service webpod -p '{"spec":{"externalTrafficPolicy":"Local"}}'
pod 가 있는 노드만 라우팅 설정된 것을 볼 수 있습니다. 하지만, 아직도 부하 분산이 되지 않고 있습니다. 한 쪽에 있는 pod 에만 트래픽이 몰리고 있는데요. 이는 리눅스에서 네트워크 분산을 책임지는 ECMP Hash 정책이 기본적으로 L3(목적지 IP 기반) 해시를 사용하기 때문입니다.
IPAM 은 IP 를 관리하는 방법으로 Cilium 에서는 Cluster Scope 방식과 Kubernetes Host Scope 방식이 존재합니다.
Cluster Scope - 기본 모드
Cilium 에서 기본으로 제공하는 IP 주소 관리 방식으로 각 노드마다 별도의 PodCIDR 을 할당하고, 각 노드에서 host-scope allocator 를 사용해 실제 IP 를 배분합니다.
Kubernetes Host Scope 모드와 다르게 Cilium operator 가 v2.CiliumNode 리소스를 통해 IP 를 관리하는 것을 볼 수 있습니다.
실제 운영 환경에서는 Kubernetes 설정을 변경하기 어려운 경우가 많은데, PodCIDR 을 배포하도록 구성되지 않은 환경에서도 podCIDR 을 배포하도록 구성할 수 있다는 장점이 있습니다. 또한 Cilium 이 직접 관리하기 때문에 더 세밀한 제어가 필요한 경우에도 유연한 설정이 가능합니다.
동작 방식
Cilium 에이전트가 시작될 때, v2.CiliumNode 객체를 통해 podCIDRs 범위가 사용 가능해질 때까지 기다립니다.
v2.CiliumNode 리소스의 spec.ipam.podCIDRs 필드에는 IPv4 및/또는 IPv6 PodCIDR 범위가 설정됩니다. 이 정보가 준비되면 각 노드의 Cilium 에이전트가 해당 범위 내에서 파드들에게 IP 주소를 할당하기 시작합니다.
운영하다 보면 IP 주소가 부족해지는 상황이 발생할 수 있습니다. 이때 주의해야 할 점이 있습니다.
이미 할당된 IP 주소들과의 충돌을 방지하기 위해 기존 clusterPoolIPv4PodCIDRList의 요소들은 절대 변경하면 안 됩니다.
새로운 요소를 리스트에 추가해야 합니다. (최소 마스크 길이는 /30이지만, 권장하는 최소 길이는 /29입니다.)
(allocator 가 각 CIDR 블록마다 네트워크 주소와 브로드캐스트 주소용으로 2개의 IP를 예약하기 때문입니다.)
clusterPoolIPv4MaskSize 도 마찬가지로 변경할 수 없습니다.
트러블슈팅
가끔 IP 할당에 문제가 생길 수 있습니다. 이때는 다음 명령어로 확인할 수 있습니다.
kubectl get ciliumnodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.ipam.operator-status}{"\n"}{end}'
이 명령어는 각 노드의 상태를 보여주며, status.ipam.operator-status 필드의 Error 항목을 통해 문제를 파악할 수 있습니다.
가장 흔한 문제 중 하나는 노드 네트워크와 파드 CIDR이 겹치는 경우입니다. 기본 파드 CIDR은 10.0.0.0/8 인데, 만약 노드 네트워크도 같은 범위를 사용한다면 다른 노드로의 연결이 끊어집니다. 모든 egress 트래픽이 다른 노드가 아닌 해당 노드의 파드를 대상으로 한다고 가정하기 때문입니다. 해결 방법은 두 가지입니다:
clusterPoolIPv4PodCIDRList를 충돌하지 않는 CIDR로 명시적으로 설정하기
노드에 다른 CIDR 사용하기
Kubernetes Host Scope
Kubernetes Host Scope IPAM 모드는 Kubernetes 자체의 PodCIDR 할당 메커니즘을 그대로 활용(Kubernetes의 네이티브 기능)
하여 각 노드에 IP 를 배분합니다.
ipam: kubernetes 설정으로 활성화되며, 클러스터의 각 개별 노드에 주소 할당을 위임합니다.
동작 방식
Cilium 에이전트가 시작될 때, Kubernetes v1.Node 객체를 통해 PodCIDR 범위가 사용 가능해질 때까지 기다립니다.
이는 활성화된 모든 주소에 대해 적용되며, 두 가지 방법으로 정보를 받아올 수 있습니다.
v1.Node 리소스 필드를 통한 방법
가장 일반적인 방법으로, v1.Node 리소스의 다음 필드들을 사용합니다:
spec.podCIDR: IPv4 및/또는 IPv6 PodCIDR 범위
spec.podCIDRs: IPv4 또는 IPv6 PodCIDR 범위 (배열 형태)
이 방법을 사용하려면 kube-controller-manager 를 --allocate-node-cidrs 플래그와 함께 실행해야 합니다. kubernetes 에게 podCIDR 범위를 할당해야 한다고 알려주는 플래그입니다.
v1.Node 어노테이션을 통한 방법
두 번째 방법은 어노테이션을 활용하는 것입니다. 이 방법은 주로 spec.podCIDRs를 아직 지원하지 않는 구버전 Kubernetes와 함께 IPv4와 IPv6를 모두 사용할 때 유용합니다. 사용되는 어노테이션들은 다음과 같습니다:
network.cilium.io/ipv4-pod-cidr: IPv4 PodCIDR 범위
network.cilium.io/ipv6-pod-cidr: IPv6 PodCIDR 범위
network.cilium.io/ipv4-cilium-host: cilium host 인터페이스의 IPv4 주소
network.cilium.io/ipv6-cilium-host: cilium host 인터페이스의 IPv6 주소
network.cilium.io/ipv4-health-ip: cilium-health 엔드포인트의 IPv4 주소
network.cilium.io/ipv6-health-ip: cilium-health 엔드포인트의 IPv6 주소
network.cilium.io/ipv4-ingress-ip: cilium-ingress 엔드포인트의 IPv4 주소
network.cilium.io/ipv6-ingress-ip: cilium-ingress 엔드포인트의 IPv6 주소
설정 방법
ConfigMap을 통한 설정
Kubernetes hostscope 를 구성하는 ConfigMap 옵션들은 다음과 같습니다.
ipam: kubernetes: Kubernetes IPAM 모드를 활성화합니다. 이 옵션을 활성화하면 enable-ipv4가 true일 때 k8s-require-ipv4-pod-cidr이 자동으로 활성화되고, enable-ipv6가 true일 때 k8s-require-ipv6-pod-cidr이 자동으로 활성화됩니다.
k8s-require-ipv4-pod-cidr: true: Cilium 에이전트가 Kubernetes 노드 리소스를 통해 IPv4 PodCIDR이 사용 가능해질 때까지 기다리도록 지시합니다.
k8s-require-ipv6-pod-cidr: true: Cilium 에이전트가 Kubernetes 노드 리소스를 통해 IPv6 PodCIDR이 사용 가능해질 때까지 기다리도록 지시합니다.
Helm을 통한 설정
Helm 차트를 사용할 때는 다음과 같이 설정할 수 있습니다.
ipam: kubernetes: --set ipam.mode=kubernetes
k8s-require-ipv4-pod-cidr: true: --set k8s.requireIPv4PodCIDR=true (이는 --set ipam.mode=kubernetes와 함께 사용해야 함)
k8s-require-ipv6-pod-cidr: true: --set k8s.requireIPv6PodCIDR=true (이는 --set ipam.mode=kubernetes와 함께 사용해야 함)
IPAM 변경 금지
Cilium 공식문서에 따르면 이미 IPAM 이 운영 중인 환경에서는 가급적 IPAM 모드를 변경하지 않는 것을 권장하고 있습니다.