Calico CNI란?
Calico CNI는 컨테이너 오케스트레이션 플랫폼에서 사용되는 네트워크 및 보안 솔루션인Calico의 Container Network Interface(CNI) 플러그인입니다. 주로 Kubernetes와 같은 플랫폼에서 파드(Pod) 간의 네트워크 연결을 제공하고, 네트워크 정책을 통해 보안을 관리하는 데 사용됩니다.
Calico 기본 통신 이해
1. 동일 노드 간 파드 통신
노드(k8s-w1)에 파드 2개 생성
# node1-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
nodeName: k8s-w1
containers:
- name: pod1
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
nodeName: k8s-w1
containers:
- name: pod2
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
# pod 생성
kubectl apply -f node1-pod2.yaml
통신 전 네트워크 정보 확인
파드 생성 전 후, 네트워크 인터페이스를 비교합니다.
파드 1 Shell 접속 후 네트워크 인터페이스를 확인해 보면, "호스트 네트워크 네임스페이스"의 네트워크 인터페이스와 파드의 "eth0"와 페어링 됨을 알 수 있습니다.
같은 노드에서 파드 간 통신 확인
ARP (Address Resolution Protocol)는 IP 주소를 MAC 주소로 변환하는 역할
정리
- 동일 노드 내의 파드 간 통신은 내부에서 직접 통신(Tunnel 인터페이스 관여 X)
- iptables에 forward rule에 Allow 정책 설정
- calico 인터페이스의 proxy arp 설정으로 파드가 바라보는 게이트웨이(169.254.1.1)로부터 Mac 정보를 전달
2. 파드 -> 외부(인터넷) 통신
노드(k8s-w1)에 파드 1개 생성
# node1-pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
nodeName: k8s-w1
containers:
- name: pod1
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
# 파드 생성
kubectl apply -f node1-pod1.yaml
외부 통신 확인
pod1 쉘에 접속하여, 외부(8.8.8.8)로 통신을 실행하고, k8s-w1 노드에서 통신을 확인합니다.
- 파드 IP : 172.16.158.3
- 노드 IP : 192.168.10.101
- 외부 인터넷 IP : 8.8.8.8
정리
- 외부 통신 시 소스 IP 변경
- ens5 인터페이스를 통해 외부로 나갈 때, 소스 IP가 192.168.10.101로 나타납니다.
- conntrack로 확인한 통신 흐름
- conntrack 명령어로 테이블을 확인하면, 출발지 파드 IP 172.16.158.3에서 목적지 8.8.8.8로 통신할 때,
- 응답 패킷의 목적지 IP가 192.168.10.101인 것을 확인할 수 있습니다.
- SNAT를 통한 파드의 외부 통신
- 파드가 외부와 통신하기 위해 SNAT(Source NAT)를 사용합니다.
- 이를 통해 파드의 소스 IP가 노드(호스트)의 IP인 192.168.10.101로 변환되어 외부 네트워크와 통신합니다.
3. 다른 노드 간 파드 통신
노드 1과 노드 2에 각각 파드 1개씩 생성
# node2-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
nodeName: k8s-w1
containers:
- name: pod1
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
nodeName: k8s-w2
containers:
- name: pod2
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
# pod 생성
kubectl apply -f node2-pod2.yaml
터널 인터페이스 확인
파드 생성 후 통신 확인
정리
- IP-in-IP 터널링 모드 사용
- 다른 노드에 위치한 파드 간의 통신은 기본적으로 IP-in-IP 터널링(기본값) 모드를 통해 이루어집니다.
- 이는 파드 트래픽을 IP 헤더로 한 번 더 감싸서 전달하는 방식입니다.
- BGP를 통한 라우팅 정보 전파
- 각 노드의 파드 네트워크 대역은 BIRD 데몬에 의해 BGP로 광고되고 전파됩니다.
- Felix 에이전트는 이 정보를 받아 호스트의 라우팅 테이블을 자동으로 추가 및 삭제하여 최신 상태로 유지합니다.
- tunl0 인터페이스를 통한 패킷 전달
- 노드 간의 파드 통신은 tunl0 인터페이스를 통해 수행됩니다.
- 패킷은 tunl0 인터페이스에서 외부 IP 헤더(Outer Header)로 캡슐화되어 상대 노드로 전달됩니다.
- 상대 노드에서는 tunl0 인터페이스가 외부 헤더를 제거하고, 내부의 파드로 패킷을 전달합니다.
CALICO 네트워크 모드
1. IP-IP 모드
Calico의 IP-in-IP 모드는 노드 간 파드 통신을 위해 패킷을 IP-in-IP 터널링 방식으로 전달하는 기본 네트워크 모드입니다. 이는 네트워크 인프라에 대한 추가적인 요구 사항 없이도 파드 간 통신을 가능하게 하며, 설치와 관리가 간편하다는 장점이 있습니다. 그러나 터널링으로 인한 약간의 네트워크 오버헤드와 성능 영향을 고려해야 합니다.
IP-IP 모드 설정 및 확인
# Calico 모드 정보 확인
calicoctl get ippool -o wide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR
default-ipv4-ippool 172.16.0.0/16 true Always Never false all()
IP-IP 모드 파드 간 통신 흐름
정리
- IP-in-IP 터널링을 통한 파드 통신
- tunl0 인터페이스를 사용하여 다른 노드에 있는 파드들과 통신합니다.
- 패킷은 IP 헤더로 감싸져(IP-in-IP 터널링) 상대 노드로 전달됩니다.
- 상대 노드의 tunl0 인터페이스에서 **외부 헤더(Outer Header)**를 제거한 후, 내부 파드로 패킷을 전달합니다.
- BGP를 통한 파드 네트워크 대역 라우팅 정보 업데이트
- 각 노드는 다른 노드의 파드 네트워크 대역 정보를 BGP를 통해 전달받습니다.
- BIRD 데몬이 BGP로 라우팅 정보를 광고하고 수신합니다.
- Felix 에이전트는 수신한 정보를 기반으로 호스트의 라우팅 테이블을 자동으로 업데이트(추가 및 삭제)합니다.
2. Direct 모드
파드가 통신할 때, 터널링이나 캡슐화를 사용하지 않고 패킷을 직접 라우팅 하는 방식입니다.
즉, 파드 통신 패킷이 출발지 노드의 라우팅 정보를 보고 목적지 노드로 원본 패킷을 그대로 전달합니다.
이를 통해서, 패킷이 직접 전달되기 때문에, 네트워크 오버헤드를 줄이고 성능을 향상할 수 있는 장점이 있습니다.
Direct 모드를 사용하면, 터널링 없이 BGP를 활용하여 패킷을 직접 라우팅 하기 때문에 네트워크 성능을 최적화할 수 있지만, 이를 위해서는, 대규모 클러스터나, 다른 장비들이 공유하는 라우팅 테이블에도 영향을 미칠 수 있기 때문에, 네트워크 인프라 지원이 필요합니다.
따라서, 네트워크 담당자나, 엔지니어를 통해 보안 및 관리 측면에서 추가적인 고려가 필요합니다.
Source/Destination Check 기능 비할 성화
Direct 모드 사용을 위해서는 NIC에 Source/Destination Check 기능을 Disable 하여야 사용가능이 가능합니다.
클라우드 벤더사마다 네트워크 보안 옵션이 다를 수 있으며, AWS를 기준으로 설명합니다. https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/VPC_NAT_Instance.html#EIP_Disable_SrcDestCheck
Direct 모드 설정 및 확인
calicoctl 명령어로 ippool 설정을 변경합니다. calico 모드를 기존 IP-IP를 비활성화(Never)로 변경합니다.
# Calico 모드 정보 확인
calicoctl get ippool -o wide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR
default-ipv4-ippool 172.16.0.0/16 true Always Never false all()
# (옵션) 모니터링
watch -d "route -n | egrep '(Destination|UG)'"
# 설정
calicoctl get ippool default-ipv4-ippool -o yaml
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Always/ipipMode: Never/" | calicoctl apply -f -
# 모드 정보 확인 : IPIPMODE 가 Never 로 변경!
calicoctl get ippool -o wide
root@k8s-m:~/yaml# calicoctl get ippool -o wide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR
default-ipv4-ippool 172.16.0.0/16 true Never Never false all()
네트워크 인터페이스 확인
파드 생성(각 노드마다 1개씩)
# node3-pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
nodeName: k8s-w1
containers:
- name: pod1
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
nodeName: k8s-w2
containers:
- name: pod2
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod3
spec:
nodeName: k8s-w0
containers:
- name: pod3
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
kubectl apply -f node3-pod3.yaml
# 파드 IP 정보 확인
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 4h43m 172.16.158.5 k8s-w1 <none> <none>
pod2 1/1 Running 0 4h43m 172.16.184.3 k8s-w2 <none> <none>
pod3 1/1 Running 0 4h43m 172.16.34.4 k8s-w0 <none> <none>
calicoctl get wep
WORKLOAD NODE NETWORKS INTERFACE
pod1 k8s-w1 172.16.158.5/32 calice0906292e2
pod2 k8s-w2 172.16.184.3/32 calibd2348b4f67
pod3 k8s-w0 172.16.34.4/32 cali49778cadcf1
파드간 통신 흐름 확인
파드 1에서 접속하여, 파드 1, 파드 2로 ping 통신을 실행해 보고, 패킷을 캡처하여 확인합니다.
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
# 파드1 -> 파드2 ping 통신(성공)
ping -c 1 172.16.184.3
PING 172.16.184.3 (172.16.184.3) 56(84) bytes of data.
64 bytes from 172.16.184.3: icmp_seq=1 ttl=62 time=0.535 ms
# 파드1 -> 파드3 ping 통신(실패)
ping -c 1 172.16.34.4
PING 172.16.34.4 (172.16.34.4) 56(84) bytes of data.
# 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> icmp -w /tmp/calico-direct.pcap
Pod1, Pod2의 노드는 192.168.10.0/24로 같은 IP 대역이므로, 노드의 네트워크 인터페이스가 같기 때문에 통신이 가능하지만,
Pod3이 동작하는 노드(호스트)의 Router는 노드에 속하지 않기 때문에 podCIDR 정보가 존재하지 않기 때문입니다.
따라서, 통신이 가능하기 위해서는 실제 라우터에 podCIDR(172.16.0.0) 정보를 추가해주어야 합니다.
네트워크 문제를 해결하기 위해 개발자들은 애플리케이션 내부에서 네트워크 통신을 최대한 처리하고, 캡슐화를 통한 라우팅을 활용하는 오버레이 네트워크 기법이 필요합니다.
3. VXLAN 모드
VXLAN은 원본 패킷을 UDP 패킷으로 캡슐화하여 L2 네트워크를 L3 네트워크 위에 가상으로 확장하는 기술로, Calico의 VXLAN 모드는 파드 간 통신을 위해 VXLAN 터널링을 사용하는 네트워크 모드입니다. 이는 네트워크 인프라의 제약을 극복하고, 네트워크 격리와 보안을 강화하는 데 도움이 됩니다. 그러나 추가적인 오버헤드와 성능 영향을 고려해야 하며, MTU 설정 및 네트워크 디버깅에 주의를 기울여야 합니다.
VXLAN 모드 설정
# VXLAN 설정 적용
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/3/calico-vxlan-kans.yaml
# 모드 설정 변경 : VXLAN 모드 사용 시, ipipMode 와 BGP(Bird)는 반드시 Never 비활성화 되어야 합니다
# ipipMode 현재 모드(Always, CrossSubnet)를 확인하고, 해당 모드를 Never 로 변경합니다
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/vxlanMode: Never/vxlanMode: Always/" | calicoctl apply -f -
calicoctl get ippool default-ipv4-ippool -o wide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED DISABLEBGPEXPORT SELECTOR
default-ipv4-ippool 172.16.0.0/16 true Never Always false false all()
# 변경 설정 적용을 위해서 calico 파드 삭제 후 재생성
kubectl delete pod -n kube-system -l k8s-app=calico-node
# 모든 노드에서 enp0s8 down 후 up (라우팅 테이블 갱신을 위함)
route -n && echo && ip link set enp0s8 down && sleep 1 && ip link set enp0s8 up && route -n
VXLAN 모드 확인
동작 확인
direct 모드에서 생성한 파드로 통신 실행 및 패킷 캡처를 확인합니다.
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i ens5 udp port 4789 -w /tmp/calico-vxlan.pcap
정리
- 노드 간 패킷은 VXLAN 터널을 통해 전달합니다.
- BGP 미사용: VXLAN 모드에서는 노드 간의 라우팅 정보를 교환하기 위해 BGP를 사용하지 않습니다.
- Felix를 통한 Pod CIDR 정보 관리: 각 노드의 Felix 에이전트가 etcd 또는 Kubernetes API 서버에서 다른 노드의 Pod CIDR 정보를 가져와 로컬 라우팅 테이블을 업데이트합니다.
4. Pod 패킷 암호화
WireGuard는 IPsec 및 OpenVPN의 대항마로 등장한 open source VPN project로 최신 암호화 기술과 간단한 설계를 바탕으로 높은 성능과 보안성, 이동성 지원(index 기반의 peer hash table 사용으로 이동 중에도 네트워크 연결이 끊기지 않음 ), 사용의 편의성을 제공하는 현대적인 VPN 설루션입니다. 다양한 프로젝트와 플랫폼에서 활용되며, Kubernetes, Cilium, VPN 서비스 등에서 네트워크 트래픽 암호화를 위해 널리 사용되고 있습니다.
WireGuard 설치
작년, Linux 5.6 커널에 WireGuard 1.0.0 기본 패키지로 탑재되었습니다.
# 설치
apt install wireguard -y
# WireGuard 버전 확인
wg version
WireGuard 설정 및 확인
calico에서 사용할 수 있도록 wireguard를 활성화해 줍니다.
calicoctl get felixconfiguration -o yaml
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'
# 확인
calicoctl get felixconfiguration default -o yaml | grep wireguardEnabled
root@k8s-m:~/yaml# calicoctl get felixconfiguration default -o yaml | grep wireguardEnabled
wireguardEnabled: true
calicoctl get node -o yaml | grep wireguardPublicKey
wireguardPublicKey: rkhr9JlHwLJeLmeP04ow4IB2SehZwOqvPkV3TSrhrXg=
wireguardPublicKey: ZUS2hsc1GMXPCtWaCpdiFqRs+qrcaaZEVPxnGXPIqDs=
wireguardPublicKey: BDdVnmSaPGS2tLoCEvb7eFZ6KexSdIEqT+djPxqYT0c=
wireguardPublicKey: 5vYvavrIOWeTfc2VaXHY9YzgIPqnenoML9dJRFEK7So=
이제, WireGuard 설정을 확인합니다.
# wireguard.cali 인터페이스 확인
ip -c -d addr show wireguard.cali
17: wireguard.cali: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8941 qdisc noqueue state UNKNOWN group default qlen 1000
link/none promiscuity 0 minmtu 0 maxmtu 2147483552
wireguard numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 172.16.116.1/32 scope global wireguard.cali
valid_lft forever preferred_lft forever
# Peer 의 정보(고유한 index)
wg show
interface: wireguard.cali
public key: rkhr9JlHwLJeLmeP04ow4IB2SehZwOqvPkV3TSrhrXg=
private key: (hidden)
listening port: 51820
fwmark: 0x100000
peer: 5vYvavrIOWeTfc2VaXHY9YzgIPqnenoML9dJRFEK7So=
endpoint: 192.168.10.102:51820
allowed ips: 172.16.184.0/32, 172.16.184.0/24, 172.16.184.2/32
peer: ZUS2hsc1GMXPCtWaCpdiFqRs+qrcaaZEVPxnGXPIqDs=
endpoint: 192.168.20.100:51820
allowed ips: 172.16.34.0/32, 172.16.34.0/24, 172.16.34.5/32
peer: BDdVnmSaPGS2tLoCEvb7eFZ6KexSdIEqT+djPxqYT0c=
endpoint: 192.168.10.101:51820
allowed ips: 172.16.158.1/32, 172.16.158.0/24, 172.16.158.3/32
wg show all public-key
wireguard.cali rkhr9JlHwLJeLmeP04ow4IB2SehZwOqvPkV3TSrhrXg=
wg show all private-key
wireguard.cali AI/g1cobeXmZ1N3NJJeHuwOsi0+LUZrLBZmazmWMb2k=
# 기타 명령어
wg show all preshared-keys
wg show all dump
wg show all endpoints
wg show all peers
wg show all transfer
wg show all persistent-keepalive
동작 확인
이전 실습과 동일하게 파드로 통신 실행 및 패킷 캡처를 확인합니다.
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
ss -unlp
tcpdump -i ens5 -nn udp port 51820
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i ens5 udp port 51820 -w /tmp/calico-wireguard.pcap
# 현재 모든 워커 노드에 tcpdump 후 ping curl 테스트 시 모든 노드에서 트래픽이 발생한다 >> 이유는 VirtualBox 에 nic2 에 "무작위 모드 : 모두 허용" 상태여서, 모든 노드로 패킷이 전달됨
# VirtualBox 에 nic2 에 "무작위 모드 : 거부"로 설정 후 테스트 하게 되면 정확하게, 출발지와 목적지의 VM에서만 패킷이 확인된다!
모드워커노드) tcpdump -i ens5 -nn udp port 51820
wireguard 비활성화
# wireguardEnabled Off
calicoctl patch felixconfiguration default --patch '{"spec":{"wireguardEnabled": false}}'
'Kubernetes > Network' 카테고리의 다른 글
Ingress와 Kubernetes Gateway Api - KANS 6주차 (0) | 2024.10.13 |
---|---|
kube-proxy IPVS 모드의 동작 원리 이해 - KANS 5주차 2 (1) | 2024.10.06 |
MetalLB를 활용한 LoadBalancer 서비스의 동작 원리 이해 - KANS 5주차 1 (2) | 2024.10.06 |
Pause 컨테이너 & Flannel CNI - KANS 2주차 (2) | 2024.09.08 |
컨테이너 격리 & 네트워크 및 보안 - KANS 1주차 (0) | 2024.08.27 |