IPAM ( IP AddreessMnagement )
FeatureKubernetes Host ScopeCluster Scope (default)Multi-Pool (Beta)CRD-backedAWS ENI…
| Tunnel routing | ✅ | ✅ | ❌ | ❌ | ❌ |
| Direct routing | ✅ | ✅ | ✅ | ✅ | ✅ |
| CIDR Configuration | Kubernetes | Cilium | Cilium | External | External (AWS) |
| Multiple CIDRs per cluster | ❌ | ✅ | ✅ | N/A | N/A |
| Multiple CIDRs per node | ❌ | ❌ | ✅ | N/A | N/A |
| Dynamic CIDR/IP allocation | ❌ | ❌ | ✅ | ✅ | ✅ |
- Kubernetes 호스트 범위 IPAM 모드는ipam: Kubernetes 에서 활성화되며 클러스터의 각 개별 노드에 주소 할당을 위임
- IP는 Kubernetes에 의해 각 노드에 연결된 PodCIDR 범위에서 할당
- 이 모드에서는 Cilium 에이전트가 Kubernetes v1.Node 객체를 통해 PodCIDR 범위가 다음 방법 중 하나를 통해 활성화된 모든 주소 패밀리에 대해 제공될 때까지 시작 시 대기
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
"--service-cluster-ip-range=10.96.0.0/16",
"--cluster-cidr=10.244.0.0/16",
# ipam 모드 확인
cilium config view | grep ^ipam

IPAM 모드 kubernetes
샘플 애플리케이션 배포
# 샘플 애플리케이션 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webpod
spec:
replicas: 2
selector:
matchLabels:
app: webpod
template:
metadata:
labels:
app: webpod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- sample-app
topologyKey: "kubernetes.io/hostname"
containers:
- name: webpod
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webpod
labels:
app: webpod
spec:
selector:
app: webpod
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
# k8s-ctr 노드에 curl-pod 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl-pod
labels:
app: curl
spec:
nodeName: k8s-ctr
containers:
- name: curl
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
Hubble 확인
# hubble ui 웹 접속 주소 확인 : default 네임스페이스 확인
NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
echo -e "http://$NODEIP:30003"
# hubble relay 포트 포워딩 실행
cilium hubble port-forward&
hubble status
# flow log 모니터링
hubble observe -f --protocol tcp --to-pod curl-pod
hubble observe -f --protocol tcp --from-pod curl-pod
hubble observe -f --protocol tcp --pod curl-pod
tcpdump와 termshark 로 트래픽 확인
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s webpod | grep Hostname; sleep 1; done'
# tcpdump 확인 : 파드 IP 확인
tcpdump -i eth1 tcp port 80 -nn
17:23:25.920613 IP 10.244.0.144.39112 > 10.244.1.180.80: Flags [P.], seq 1:71, ack 1, win 502, options [nop,nop,TS val 3745105977 ecr 1971332111], length 70: HTTP: GET / HTTP/1.1
#
tcpdump -i eth1 tcp port 80 -w /tmp/http.pcap
#
termshark -r /tmp/http.pcap

Cilium Cluster Scope & 마이그레이션
- 클러스터 범위 IPAM 모드는 각 노드에 노드별 PodCIDR을 할당하고, 각 노드에서 호스트 범위 할당기를 사용해 IP를 배정
- Kubernetes 호스트 범위 모드와 유사하나, Kubernetes가 v1.Node 리소드를 통해 PodCIDR을 할당하는 대신 Cilium 연산자가 v2.CiliumNode CRD로 관리
- Kubernetes의 노드별 PodCIDR 구성에 의존하지 않음
- 최소 마스크 길이는 /30이며 권장 최소 마스크 길이는 /29, 네트워크 및 브로드캐스트 주소로 2개 예약
- 기본 Pod CIDR은 10.0.0.0/8이며 clusterPoolIPv4PodCIDRList로 설정 가능
# 반복 요청 해두기
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s webpod | grep Hostname; sleep 1; done'
# Cluster Scopre 로 설정 변경
helm upgrade cilium cilium/cilium --version 1.17.6 --namespace kube-system --reuse-values \
--set ipam.mode="cluster-pool" --set ipam.operator.clusterPoolIPv4PodCIDRList={"172.20.0.0/16"} --set ipv4NativeRoutingCIDR=172.20.0.0/16
kubectl -n kube-system rollout restart deploy/cilium-operator # 오퍼레이터 재시작 필요
kubectl -n kube-system rollout restart ds/cilium
# 변경 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
cilium config view | grep ^ipam
ipam cluster-pool
* helm version 1.18 출시로 helm upgrade 를 현재 version 1.17.6 으로 고정

kubectl get ciliumnode -o json | grep podCIDRs -A2
kubectl get ciliumendpoints.cilium.io -A

kubectl delete ciliumnode k8s-w1
kubectl -n kube-system rollout restart ds/cilium
kubectl get ciliumnode -o json | grep podCIDRs -A2
kubectl get ciliumendpoints.cilium.io -A
#
kubectl delete ciliumnode k8s-ctr
kubectl -n kube-system rollout restart ds/cilium
kubectl get ciliumnode -o json | grep podCIDRs -A2
kubectl get ciliumendpoints.cilium.io -A
노드의 pod-cidr static routing 적용 확인
ip -c route
sshpass -p 'vagrant' ssh vagrant@k8s-w1 ip -c route

Rollout Restart
kubectl get pod -A -owide | grep 10.244.
kubectl -n kube-system rollout restart deploy/hubble-relay deploy/hubble-ui
kubectl -n cilium-monitoring rollout restart deploy/prometheus deploy/grafana
kubectl rollout restart deploy/webpod
kubectl delete pod curl-pod
curl Pod 배포 후 Pod IP 변경 확인
# k8s-ctr 노드에 curl-pod 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl-pod
labels:
app: curl
spec:
nodeName: k8s-ctr
containers:
- name: curl
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF

Routing
Encapsulation
- Encapsulation 모드는 Cilium의 기본 네트워킹 인프라에서 최소한의 요구 사항으로 자동 실행
- 모든 클러스터 노드는 UDP 기반 VXLAN(기본 포트 8472) 또는 Geneve(기본 포트 6081)를 이용해 터널 메시(mesh)를 형성하고 모든 트래픽을 캡슐화
- 기본 네트워크는 IPv4 지원 및 캡슐화된 패킷 허용 방화벽(UDP 8472/VXLAN, UDP 6081/Geneve)이 필요
- 장점
- 단순성: PodCIDR을 인식할 필요 없이 노드 간 연결만 가능하면 토폴로지 제약 없음
- 정체성 맥락 전송: 캡슐화 프로토콜로 보안 ID 같은 메타데이터 전달, 원격 노드에서의 추가 조회 최소화
- 단점
- MTU 오버헤드: VXLAN 기준 패킷당 약 50바이트 헤더 추가로 최대 처리량 저하, 점보 프레임 활성화로 완화 가능
- 설정
- tunnel-protocol: vxlan 또는 geneve 선택
- tunnel-port: 캡슐화 프로토콜용 포트 지정 (기본 8472/VXLAN, 6081/Geneve)
Native_Routing
- routing-mode: native 설정으로 캡슐화 없이 Linux 커널 라우팅 하위시스템을 통해 패킷 전달
- Cilium은 로컬 엔드포인트를 벗어나는 모든 패킷을 커널에 위임하여 네이티브 패킷 전달 모드 활성화
- 클러스터 네트워크는 PodCIDR을 직접 라우팅할 수 있어야 함
- PodCIDR 라우팅 방안 1:
- 각 노드가 모든 노드의 Pod IP를 인식해 커널 라우팅 테이블에 삽입
- 동일 L2 네트워크에서는 auto-direct-node-routes: true로 자동 경로 삽입
- PodCIDR 라우팅 방안 2:
- 네트워크 내 라우터가 모든 PodCIDR 경로를 관리
- 노드는 해당 라우터를 가리키는 기본 경로만 설정 (클라우드 제공자 통합 사용)
- 주요 설정
- ipv4-native-routing-cidr: x.x.x.x/y 로 네이티브 라우팅 CIDR 지정
- auto-direct-node-routes: true 로 동일 L2 시 노드별 경로 자동 생성
노드 간 Pod 통신 상세 확인 ( Native Routing )
PodIP 확인
export WEBPODIP1=$(kubectl get -l app=webpod pods --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].status.podIP}')
export WEBPODIP2=$(kubectl get -l app=webpod pods --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].status.podIP}')
echo $WEBPODIP1 $WEBPODIP2
Masquerading
- Pod에 할당된 RFC1918 개인 주소(예: 10.0.0.0/8)는 외부에서 라우팅 불가하므로 Cilium은 클러스터를 벗어나는 모든 트래픽의 소스 IP를 노드 IP로 자동 masquerade
- Masquerade 비활성화:
enable-ipv4-masquerade: false
enable-ipv6-masquerade: false
- 기본 동작: 노드의 IP 할당 CIDR 내 목적지는 masquerade 제외
- 외부 네트워크에서 Pod IP 라우팅 가능 시 ipv4-native-routing-cidr: 10.0.0.0/8 (또는 IPv6용 ipv6-native-routing-cidr) 지정하면 해당 CIDR 내 목적지도 비-masquerade 처리
- 구현 방식
- eBPF-based (bpf.masquerade=true)
- BPF Masquerading 프로그램을 실행하는 장치에서만 적용
- BPF Host-Routing 모드 자동 활성화
- 지원 프로토콜: TCP, UDP, ICMP
- 출력 장치가 프로그램 실행 시 포드 → 외부 패킷을 해당 장치 IP로 masquerade
- 디바이스 자동 선택(BPF NodePort 감지) 또는 devices Helm 옵션으로 수동 지정 가능
- iptables-based
- iptables NAT 규칙을 통해 masquerade
- eBPF-based (bpf.masquerade=true)
- eBPF 마스커딩 시 클러스터 노드의 External IP로 향하는 트래픽은 예외 처리되어 masquerade되지 않음
CoreDNS
Pod의 DNS 설정 정보 확인
kubectl exec -it curl-pod -- cat /etc/resolv.conf

cat /var/lib/kubelet/config.yaml | grep cluster -A1

Pod에서 DNS 질의 확인
kubectl exec -it curl-pod -- curl kube-dns.kube-system.svc:9153/metrics | grep coredns_cache_ | grep -v ^#

도메인 질의
kubectl exec -it curl-pod -- nslookup webpod
kubectl exec -it curl-pod -- nslookup -debug webpod
kubectl exec -it curl-pod -- nslookup -debug google.com
coredns logging 활성화


NodeLocaDNS
- NodeLocal DNSCache는 각 노드에서 DaemonSet 형태로 DNS 캐싱 에이전트를 실행해 클러스터 DNS 성능을 향상
- 기존 ClusterFirst 모드의 Pods는 kube-dns 서비스 IP를 통해 iptables DNAT 및 연결 추적을 거쳐 CoreDNS로 쿼리 전달
- NodeLocal DNSCache는 Pods가 동일 노드의 로컬 캐시 에이전트에 직접 쿼리하도록 하여 iptables DNAT과 연결 추적을 회피
- 로컬 캐시 미스 시 기본 “cluster.local” 접미사에 대해 kube-dns 서비스에 재쿼리
- UDP 쿼리를 TCP로 업그레이드해 tail 지연시간(최대 30초) 감소 및 연결 추적 테이블 과부하 방지
- 노드 단위 DNS 요청에 대한 메트릭과 가시성 제공
- 네거티브 캐싱을 활성화해 kube-dns 서비스로의 쿼리 수 추가 절감
IPtables vs IPVS 에 따라 구현 기법이 달라짐
1. ipvs
- NodeLocal DNSCache 적용 시 Pod의 DNS 요청이 CoreDNS ClusterIP(예: 10.96.0.10)가 아니라 NodeLocal 캐시 IP(예: 169.254.25.10)로 전송됨
- IPVS kube-proxy 모드에서는 iptables의 NOTRACK 룰을 무시하고 Load Balancing을 수행하므로 ClusterIP 기반 DNS 요청을 NodeLocal로 강제 전환 불가
- NodeLocal DNSCache 적용 여부를 변경하려면 kubelet의 DNS 서버 설정을 수정하고 kubelet 및 모든 Pod을 재시작해야 함
- IPVS 환경에서는 Pod가 ClusterIP를 우회해 NodeLocal DNSCache를 사용할 수 없어 적용이 제한됨
설치
kubedns 는 coredns 서비스의 ClusterIP를 변수 지정
kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
domain='cluster.local' ## default 값
localdns='169.254.20.10' ## default 값
echo $kubedns $domain $localdns

NodeLocalDNS 설정 변경
sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
설치
kubectl apply -f nodelocaldns.yaml

NodeLocalDNS log,debug 추가

curl Pod 삭제 후 재생성 했을 때 NodeLocalDNS 미사용 확인

Cilium Local RedirectPolicy
- IP 주소와 Port/Protocol tuple 또는 Kubernetes Service 로 향하는 포드 트래픽을 eBPF를 사용하여 노드 내 백엔드 포드로 로컬로 리디렉션할 수 있도록 하는 Cilium의 로컬 리디렉션 정책을 구성하는 방법
- 백엔드 Pod의 네임스페이스는 정책의 네임스페이스와 일치
Cilium Upgrade
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set localRedirectPolicy=true
kubedns=$(kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP})
sed -i "s/__PILLAR__DNS__SERVER__/$kubedns/g;" node-local-dns.yaml
vi -d nodelocaldns.yaml node-local-dns.yaml

변경된 NodeLocalDNS apply
kubectl apply -f node-local-dns.yaml
변경된 NodeLocalDNS cm 확인

CIliumLocalRedirectPolicy Apply


Cilium-dbg 명령어로 lrp list 확인

NodeLocalDNS 로그를 통해 질의 응답 확인
kubectl exec -it curl-pod -- nslookup www.google.com
