cilium에서는 마스커레이드를 BPF를 사용하거나 iptables를 사용할 수 있습니다. 마스커레이드는 클러스터 내부 파드(Pod)에서 외부 서비스(인터넷 등)로 나갈 때, 출발지 IP를 노드(Node)의 IP로 변경하여 응답 패킷이 돌아올 수 있도록 해 주는 기능입니다. bpf 마스커레이드를 설정하면 host routing도 bpf로 설정해야합니다.
마스커레이드 설정 확인하기
kubectl exec -it -n kube-system ds/cilium -c cilium-agent -- cilium status | grep Masquerading
기본적으로, 파드에서 보내는 모든 패킷 중 목적지가 ipv4-native-routing-cidr 범위 밖에 있는 IP 주소인 경우 마스커레이딩됩니다. 단, 클러스터의 노드 아이피는 제외입니다.
cilium config view | grep ipv4-native-routing-cidr
ebpf 테스트 환경 설정하기
외부 파드 대신 kind와 동일 테트워크 대역에 whoami 컨테이너를 띄워보도록 하겠습니다.
docker run -d --rm --name nginx --network kind nginx:latest
이 컨테이너의 아이피 주소를 확인해보겠습니다.
docker inspect nginx | jq '.[0].NetworkSettings.Networks.kind.IPAddress' -r
컨테이너에 접속해서 tcpdump를 설치합니다.
docker exec nginx apt update
docker exec nginx apt install tcpdump
테스트 해보기
tcpdump를 켜둔 상태에서 nginx컨테이너를 호출해보겠습니다.
docker exec -it nginx bash
tcpdump -i eth0 -n port 80
이번에는 지난번 네이티브 라우팅 통신흐름때와 다르게 출발지 주소가 노드의 아이피로 되어있는 것을 볼 수 있습니다.
2025.07.31 - [K8S/🔥 network study🔥] - [cilium] native routing 통신 흐름 확인해보기
root@49b71dac045f:/# tcpdump -i eth0 -n port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
11:56:28.960087 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [S], seq 2618216972, win 64240, options [mss 1460,sackOK,TS val 1152497573 ecr 0,nop,wscale 7], length 0
11:56:28.960210 IP 172.17.0.6.80 > 172.17.0.4.53938: Flags [S.], seq 1746492749, ack 2618216973, win 65160, options [mss 1460,sackOK,TS val 2216167557 ecr 1152497573,nop,wscale 7], length 0
11:56:28.960318 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 1152497573 ecr 2216167557], length 0
11:56:28.960601 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [P.], seq 1:76, ack 1, win 502, options [nop,nop,TS val 1152497574 ecr 2216167557], length 75: HTTP: HEAD / HTTP/1.1
11:56:28.960610 IP 172.17.0.6.80 > 172.17.0.4.53938: Flags [.], ack 76, win 509, options [nop,nop,TS val 2216167558 ecr 1152497574], length 0
11:56:28.961323 IP 172.17.0.6.80 > 172.17.0.4.53938: Flags [P.], seq 1:239, ack 76, win 509, options [nop,nop,TS val 2216167558 ecr 1152497574], length 238: HTTP: HTTP/1.1 200 OK
11:56:28.961419 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [.], ack 239, win 501, options [nop,nop,TS val 1152497574 ecr 2216167558], length 0
11:56:28.961684 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [F.], seq 76, ack 239, win 501, options [nop,nop,TS val 1152497575 ecr 2216167558], length 0
11:56:28.961741 IP 172.17.0.6.80 > 172.17.0.4.53938: Flags [F.], seq 239, ack 77, win 509, options [nop,nop,TS val 2216167559 ecr 1152497575], length 0
11:56:28.961812 IP 172.17.0.4.53938 > 172.17.0.6.80: Flags [.], ack 240, win 501, options [nop,nop,TS val 1152497575 ecr 2216167559], length 0
kubelet의 헬스체크 포트로 노드 아이피로 호출해보도록 하겠습니다.
클러스터의 노드 아이피가 목적지이기 때문에 출발지 아이피가 파드 아이피인것을 확인할 수 있습니다.
root@kind-worker:/# tcpdump -i eth0 dst 172.17.0.4 and port 10248 -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:18:02.187353 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [S], seq 4222611365, win 64240, options [mss 1460,sackOK,TS val 3687075721 ecr 0,nop,wscale 7], length 0
12:18:02.187471 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [.], ack 2286795463, win 502, options [nop,nop,TS val 3687075722 ecr 3835880220], length 0
12:18:02.187594 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [P.], seq 0:87, ack 1, win 502, options [nop,nop,TS val 3687075722 ecr 3835880220], length 87
12:18:02.187996 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [.], ack 152, win 501, options [nop,nop,TS val 3687075722 ecr 3835880221], length 0
12:18:02.188188 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [F.], seq 87, ack 152, win 501, options [nop,nop,TS val 3687075722 ecr 3835880221], length 0
12:18:02.188509 IP 10.244.0.101.52844 > 172.17.0.4.10248: Flags [.], ack 153, win 501, options [nop,nop,TS val 3687075723 ecr 3835880221], length 0
ipMasqAgent 설정하기
에이전트는 Fsnotify를 이용해 설정 파일의 변경 사항을 감지하므로, resyncInterval 옵션은 필요하지 않습니다.
이 에이전트는 설정 파일에서 지정된 다음 옵션들을 지원합니다:
- nonMasqueradeCIDRs: 여기 CIDR 목록에 포함된 목적지로의 패킷은 마스커레이딩되지 않습니다.
- masqLinkLocal: false인 경우 non-masquerade 목록에 169.254.0.0/16(RFC 3927 IPv4 링크 로컬 주소)가 추가됩니다.
- masqLinkLocalIPv6: false인 경우 non-masquerade 목록에 fe80::/10가 추가됩니다.
설정 파일이 비어 있으면, 기본적으로 다음 CIDR 범위들이 마스커레이딩 예외로 설정됩니다
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
100.64.0.0/10
192.0.0.0/24
192.0.2.0/24
192.88.99.0/24
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
240.0.0.0/4
설정 예시 (ConfigMap 사용)
apiVersion: v1
kind: ConfigMap
metadata:
name: ip-masq-agent
data:
config: |
nonMasqueradeCIDRs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
masqLinkLocal: true
kubectl -n kube-system exec ds/cilium -- cilium-dbg bpf ipmasq list
iptables 기반 마스커레이드 설정하기
기본적으로 iptables로 마스커레이드를 하게 되면 외부로 나가는 모든 트래픽에 마스커레이드가 되지만
특정 인터페이스에 대해서만도 지정할 수 있습니다.
egress-masquerade-interfaces: eth+ ens+
목적지에 따라 어떤 인터페이스를 선택할지 정하게 하려면 아래의 설정을 키면 됩니다.
enable-masquerade-to-route-source: "true"
확실하게 확인하기 위해 새로 클러스터를 생성해보겠습니다.
helm install cilium cilium/cilium --version 1.17.6 --namespace kube-system \
--set k8sServiceHost=auto --set ipam.mode="kubernetes" \
--set k8s.requireIPv4PodCIDR=true --set ipv4NativeRoutingCIDR=10.244.0.0/16 \
--set routingMode=native --set autoDirectNodeRoutes=true --set endpointRoutes.enabled=true \
--set kubeProxyReplacement=true --set bpf.masquerade=false \
--set endpointHealthChecking.enabled=false --set healthChecking=false \
--set operator.replicas=1 --set debug.enabled=true
iptables 룰 확인해보기
ebpf와 다르게 새로 생긴 건 CILIUM_POST_nat 테이블이 새로 생겼습니다.
룰을 자세히 보면 이렇습니다.
-A CILIUM_POST_nat -s 10.244.2.0/24 -m set --match-set cilium_node_set_v4 dst -m comment --comment "exclude traffic to cluster nodes from masquerade" -j ACCEPT
-A CILIUM_POST_nat -s 10.244.2.0/24 ! -d 10.244.0.0/16 ! -o cilium_+ -m comment --comment "cilium masquerade non-cluster" -j MASQUERADE
-A CILIUM_POST_nat -m mark --mark 0xa00/0xe00 -m comment --comment "exclude proxy return traffic from masquerade" -j ACCEPT
-A CILIUM_POST_nat -s 127.0.0.1/32 -o lxc+ -m comment --comment "cilium host->cluster from 127.0.0.1 masquerade" -j SNAT --to-source 10.244.2.122
첫 번째 룰
- -A CILIUM_POST_nat -s 10.244.2.0/24 -m set --match-set cilium_node_set_v4 dst -m comment --comment "exclude traffic to cluster nodes from masquerade" -j ACCEPT
- 10.244.2.0/24 에서 시작하며 내부 노드로 트래픽을 보내는 경우 마스커레이드를 하지 않는다.
--match-set cilium_node_set_v4 의 리스트는 ipset 명령어로 리스트를 확인할 수 있습니다.
ipset list cilium_node_set_v4
여기서 보이는 아이피는 노드 아이피 입니다.
노드 아이피 리스트
두 번째 룰
- -A CILIUM_POST_nat -s 10.244.2.0/24 ! -d 10.244.0.0/16 ! -o cilium_+ -m comment -comment "cilium masquerade non-cluster" -j MASQUERADE
- 10.244.2.0/24 (노드 내의 파드 아이피)에서 목적지가 cilium_ 으로 시작하는 인터페이스가 아니면서 10.244.0.0/16 (클러스터 전체의 파드 대역)대역이 아닌 곳으로 통신하려고 할 때 노드의 아이피로 마스커레이드를 합니다.
세 번째 룰
- -A CILIUM_POST_nat -m mark --mark 0xa00/0xe00 -m comment - comment "exclude proxy return traffic from masquerade" -j ACCEPT
- Cilium L7 프록시(예: Envoy)를 통과한 리턴 트래픽은 MASQUERADE하지 않음. 프록시 리턴 트래픽은 이미 적절히 처리되었기 때문에 추가 NAT를 피하려는 목적입니다.
네 번째 룰
- -A CILIUM_POST_nat -s 127.0.0.1/32 -o lxc+ -m comment --comment "cilium host>cluster from 127.0.0.1 masquerade" -j SNAT --to-source 10.244.2.122
- 호스트에서 루프백(127.0.0.1) 인터페이스를 통해 lxc+ 로 시작하는 인터페이스(Pod로 가는 트래픽)로 가는 경우 NAT 처리하여 출발지를 해당 노드의 cilium_host 인터페이스로 바꿉니다.
'K8S > 🔥 network study🔥' 카테고리의 다른 글
[cilium] LB-IPAM 로드밸런서 아이피를 관리하기 (2) | 2025.08.09 |
---|---|
[cilium] ipam 모드 비교하기 (작성 중) (0) | 2025.08.03 |
[cilium] native routing 통신 흐름 확인해보기 (0) | 2025.07.31 |
[cilium] hubble exporter 설정하기 (0) | 2025.07.26 |
[cilium] 스타워즈 데모로 cilium 테스트 해보기 (3) | 2025.07.26 |