들어가며
이번 포스팅은 CloudNet@ 커뮤니티에서 주최하는 KANS 스터디 3주 차 주제인 "Calico CNI"에 대해서 정리한 내용입니다.
(스터디 내용이 많아 "k8s Calico CNI(1)"와 "k8s Calico CNI mode & 운영(2)"으로 포스팅을 나누어 작성합니다.)
Calico란?
Calico는 Kubernetes 워크로드와 Kubernetes가 아닌 오픈스택 또는 레거시 워크로드에서 원활하고 안전하게 통신할 수 있도록 하는 네트워킹 및 보안 솔루션입니다.
Calico 실습환경 구축
AWS에서 10.x 대역과 20.x대역에 대한 라우터를 내부적으로 처리하는 구성입니다.
(서울리전에 테스트 중인 시스템이 있어, ap-northeast-3 [오사카]로 변경하여 진행했습니다.)
aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides KeyName=jhpark SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-3
ssh -i <pem> ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-3)
kubectl config rename-context "kubernetes-admin@kubernetes" "HomeLab"
# 네임스페이스 변경
kubens default
# 기본정보 확인
kubectl cluster-info
kubectl get node -owide
kubectl get service,ep
kubectl get pod -A -owide
#z
tree /opt/cni/bin/
ls -l /opt/cni/bin/
# 네트워크 정보 확인
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l
default kube-api-server의 정보가 192.168.10.10:6443으로 확인된 것을 확인할 수 있고,
CNI가 설치되어 있지 않아 인스턴스들의 상태가 NotReady 상태로 되어 있는 것을 확인할 수 있습니다.
opt 경로의 기본 디렉터리를 확인해 보면 Calico가 아직 설치되어 있지 않은 것을 확인할 수 있습니다.
iptable의 필터와, nat 개수가 각각 50, 48개를 확인합니다.
다음으로 Calico를 설치합니다.
# 모니터링
watch -d 'kubectl get pod -A -owide'
# calico cni install
## kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml - 서브넷 24bit 추가
#
tree /opt/cni/bin/
ls -l /opt/cni/bin/
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l
# calicoctl install
curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin
calicoctl version
# CNI 설치 후 파드 상태 확인
kubectl get pod -A -o wide
Calico를 설치하기만 했는데도 iptable의 필터와, nat 개수가 108, 126개로 증가했습니다.
# kube-ops-view 설치
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system
# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : [실습환경 A Type]
echo -e "KUBE-OPS-VIEW URL = http://$(curl -s ipinfo.io/ip):30000/#scale=1.5"
# metrics-server 설치
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
kubectl get apiservices |egrep '(AVAILABLE|metrics)'
# 확인
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'
Calico의 기본 통신 이해
Calico는 Kubernetes에서 Node 간 통신을 담당하는 CNI Plug-in 역할을 수행합니다.
각 주요 컴포넌트에 대한 설명은 다음과 같습니다.
calicoctl
calicoctl은 Calico Object를 CRUD 할 수 있는 툴입니다.
Calico Datastore(칼리코 데이터 저장소)
Calico 동작을 위해 데이터를 저장하는 곳입니다. kubectl을 통해 Pod를 생성하거나, calicoctl을 통해 Calico의 설정을 변경하게 되면, 이 저장소에 데이터가 반영됩니다. 데이터의 저장소는 Kubernetes API 저장소와 ETCD를 사용할 수 있습니다. (상대적으로 관리 복잡도가 낮은 Kubernetes API 저장소를 주로 사용한다고 합니다.)
confd
Calico Datastore와 상호작용하여 변경된 설정을 감지하고 BIRD에 적용합니다.
BIRD
BIRD는 오픈소스 라우팅 데몬 프로그램으로, 자신이 해당하는 노드의 Pod 네트워크 대역을 BGPBorder Gateway Protocol)을 통해 다른 노드로 광고합니다. (이를 아래서 설명할 felix가 수집하여 Host에 정보를 업데이트합니다.) 이를 통해 각 노드는 다른 모든 노드의 파드 대역(pod CIDR)과 통신할 수 있습니다.
또한 만약 노드가 종료되면, 삭제를 요청하는 정보를 전달하는 등 항상 동적으로 최신 상태를 유지합니다.
felix
각 노드에서 동작하며 BIRD로 학습한 다른 노드의 파드 네트워크 대역을 Host의 라우팅 테이블에 최종적으로 업데이트하는 역할을 하며, IPtable 규칙 및 설정을 관리합니다.
다시 말해 Calico CNI 기반 Pod 간 통신은
Pod 생성 시 calico datastore를 참고하여,
CNI Plug-in과 Calico의 자체 CNI IPAM 플러그인을 통해 Pod의 네트워크를 설정하게 되며,
해당 Pod가 다른 노드에 위치한 Pod와 통신할 때는,
BIRD로 학습한 네트워크 정보를 바탕으로, Felix에 의해 업데이트된 route table, IP table을 통해
통신을 하게 됩니다 (보.. 복잡하군요 ^^.. ;)
ip -c route를 통해 마스터의 route를 확인해 보면 다음과 같이 자동으로 Calico의 네트워크 정보
가 추가된 것을 확인할 수 있습니다.
이는 172.16.158.0/24 대역의 Pod와 통신하려면, 192.168.10.101(k8s-w1)로 찾아가고, 172.16.184.0/24 대역의 Pod와 통신하려면 192.168.10.102(k8s-w2)로 찾아가라는 의미입니다.
Calico 구성요소 확인
명령어를 통해 위에서 살펴본 Calico 구성요소들을 살펴보았습니다.
# 버전 확인 - 링크
## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version
# calico 관련 정보 확인
kubectl get daemonset -n kube-system
kubectl get pod -n kube-system -l k8s-app=calico-node -owide
kubectl get deploy -n kube-system calico-kube-controllers
kubectl get pod -n kube-system -l k8s-app=calico-kube-controllers -owide
# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인 - 링크
calicoctl ipam show
# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration
# host-local IPAM 정보 확인 : k8s-m 노드의 podCIDR 은 host-local 대신 칼리코 IPAM 를 사용함
## 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
kubectl get node k8s-m -o json | jq '.spec.podCIDR'
# CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist | jq
...
"datastore_type": "kubernetes", # 칼리코 데이터저장소는 쿠버네티스 API 를 사용
"ipam": {
"type": "calico-ipam" # IPAM 은 칼리코 자체 IPAM 을 사용
},
...
# calicoctl node 정보 확인 : Bird 데몬(BGP)을 통한 BGP 네이버 연결 정보(bgp peer 는 노드의 IP로 연결) - 링크
calicoctl node status
calicoctl node checksystem
# ippool 정보 확인 : 클러스터가 사용하는 IP 대역 정보와 칼리코 모드 정보 확인
calicoctl get ippool -o wide
# 파드와 서비스 사용 네트워크 대역 정보 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
"--service-cluster-ip-range=10.200.1.0/24",
"--cluster-cidr=172.16.0.0/16",
kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet
podSubnet: 172.16.0.0/16
serviceSubnet: 10.96.0.0/12
# calico endpoint (파드)의 정보 확인 : WORKLOAD 는 파드 이름이며, 어떤 노드에 배포되었고 IP 와 cali 인터페이스와 연결됨을 확인
calicoctl get workloadEndpoint
calicoctl get workloadEndpoint -A
calicoctl get workloadEndpoint -o wide -A
# 노드에서 컨테이너(프로세스) 확인 : pstree
ps axf
kubernetes에서 관리하는 podCIDR가 존재하지만, 실제로 사용하지는 않는다고 합니다.
실행 중인 프로세스들을 확인해 보면 pause 컨테이너 밑에 calico 구성요소들이 동작하는 것을 확인할 수 있습니다.
동일 노드에서 Calico CNI 기반 Pod to Pod 통신
동일 노드에서 파드간 통신은 calico-node라는 가상 라우터 Pod를 통해 수행됩니다. 외부로 나가지 않기 때문에 tunnel 인터페이스는 관여하지 않습니다.
실습 - 기본 상태 확인
기본상태는 다음과 같습니다.
# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
# 네트워크 네임스페이스 확인
lsns -t net
# 네트워크 라우팅 경로 정보 확인
ip -c route | grep bird
# 아래 tunl0 Iface 에 목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서 각 노드에 전달됩니다 → 각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
...
# (옵션) iptables rule 갯수 확인 >> iptables rule 이 모든 노드에서 똑같나요? 다른가요?
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l
172.16.116.0/32 터널 인터페이스를 확인할 수 있고, Pod가 없기 때문에 네트워크 인터페이스 없는 것을 확인했습니다.
BIRD 관련 라우팅 정보를 확인해 보면 다른 노드들의 Pod 대역을 확인할 수 있습니다.
(캡처에 보이는 balckhole 라우팅 : 라우팅 루프를 해결하기 위함)
또한 상대 워커노드로 갈 때는 tunl0 인터페이스를 사용하는 것도 확인할 수 있습니다.
IP table의 filter, nat 개수가 각각 39, 15개로 확인됩니다.
실습 - 파드 배포 후 상태 확인
# 배포 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
# [터미널1] k8s-m 모니터링
watch -d calicoctl get workloadEndpoint
# [터미널2] k8s-m 모니터링
# 파드 생성 : 노드의 이름이 다를 경우 아래 yaml 파일 다운로드 후 수정해서 사용하시면 됩니다
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml
# 생성된 파드 정보 확인
kubectl get pod -o wide
root@k8s-m:~# kubectl get pod -o wide
# calicoctl 이용한 endpoint 확인 : veth 정보도 출력!
calicoctl get workloadendpoints
w1 노드에 pod1, pod2에 대한 라우팅 정보가 추가되었습니다.
IP table의 filter, nat 개수가 각각 121, 15개로 확인됩니다. (Pod 개수가 증가할수록 비효율적으로 올라가네요)
Pod shell에 접속하여 네트워크 구성을 살펴봅니다.
# 마스터 노드에서 아래 실행
# Pod 1
kubectl exec pod1 -it -- zsh
...
# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
...
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether 7e:7b:ac:ea:7e:63 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.158.2/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::7c7b:acff:feea:7e63/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
# ARP 정보는 현재 아무것도 없다
ip -c neigh
---
# Pod 2
kubectl exec pod2 -it -- zsh
...
# 아래 처럼 if12 는 호스트 네트워크 인터페이스 12번인 calice0906292e2와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod2> ip -c addr
...
3: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether b2:a8:83:bf:82:4f brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.158.3/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::b0a8:83ff:febf:824f/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod2> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
# ARP 정보는 현재 아무것도 없다
ip neighbor show
호스트에 GW IP(169.254.1.1)를 추가할 필요 없이 Proxy ARP에 의해 기본으로 설정된 169.254.1.1에 대해서 응답할 수 있습니다.
파드간 통신 실행 이해를 위한 준비
proxy arp: 네트워크에서 IP 패킷이 올바른 대상에 도달할 수 있도록 도와주는 기술입니다. ARP(Address Resolution Protocol)는 IP 주소를 물리적인 MAC 주소로 변환하는 프로토콜인데, Proxy ARP는 네트워크 간 라우팅을 하지 않고도 이 과정에서 중재 역할을 수행합니다.
Proxy ARP의 동작 원리는 다음과 같습니다.
프락시 ARP는 한 네트워크 장치(일반적으로 라우터)가 다른 네트워크의 장치를 대신하여 ARP 요청에 응답하는 방식으로 동작합니다. 예를 들어, 한 호스트가 동일한 서브넷에 있지 않은 호스트에게 IP 패킷을 보내려 할 때, ARP 요청을 보냅니다. 이때 프락시 ARP를 설정한 장치는 그 요청에 자신이 응답하고, 패킷을 실제 대상에게 전달해 줍니다. 이를 통해 두 호스트는 서로 다른 서브넷에 있더라도 마치 같은 서브넷에 있는 것처럼 통신할 수 있습니다.
# iptables 필터 테이블에 FORWARD 리스트 중 cali-FORWARD 룰 정보를 필터링해서 watch 로 확인
watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"
# (마스터노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
VETH2=$(calicoctl get workloadEndpoint | grep pod2 | awk '{print $4}')
echo $VETH2
# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=calice0906292e2
VETH2=calibd2348b4f67
# 노드1 calice# 인터페이스의 proxy arp 설정 확인
# cat /proc/sys/net/ipv4/conf/<자신의 pod1에 연결된 calice# 이름>/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH1/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH2/proxy_arp
# 파드1 혹은 파드2에 veth 로 연결된 호스트 네트워크 인터페이스 calice# 중 1개 선택해서 tcpdump
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
# 파드1 Shell 에서 실행 : 정상 통신!
kubectl exec pod1 -it -- zsh
--------------------
ping -c 10 <파드2 IP>
# 게이트웨이 169.254.1.1 의 MAC 주소를 ARP 에 의해서 학습되었다
ip -c -s neigh
# 노드에서 확인
# iptables 에 기본 FORWARD 는 DROP 이지만, 아래 cali-FORWARD Rule에 의해 허용되며, pkts 카운트가 증가한다
iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'
watch -d "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"
# 파드1에서 게이트웨이의 IP인 169.254.1.1 의 MAC 주소를 알기 위해서 ARP Request 를 보낸다
# 이때 veth 연결된 calice#~ 에 proxy arp 설정이 되어 있고,
# 자신의 mac 주소(ee:ee:ee:ee:ee:ee)를 알려주고, 이후 정상 통신됨
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn
# 호스트에서 calice0906292e2 의 MAC 주소 다시 확인
root@k8s-w1:~# ip -c -d link
모든 cali* 인터페이스가 동일한 맥주소인 ee:ee:ee:ee:ee:ee를 사용해도 괜찮은 이유는 point-to-point routed interfaces를 사용하기 때문에 호스트의 데이터링크 레이어(L2 Layer)에 도달하지 않기 때문입니다.
따라서 모든 cali# 가 동일한 맥 주소를 사용해도 문제가 없습니다.
Calico CNI 기반 워커노드의 Pod to Internet(외부) 통신
파드에서 외부(인터넷) 통신 시에는 해당 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됩니다. 실습은 8.8.8.8(구글)로 통신하는 예시입니다.
먼저 외부 통신 관련 calico의 설정정보를 확인해 봅니다.
# 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
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()
# 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
iptables -n -t nat --list cali-nat-outgoing
root@k8s-w1:~# iptables -n -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully
ipset list
ipset list cali40masq-ipam-pools
출발지가 Pod ippool에 있지만, 목적지가 포함되지 않으면 outgoing MASQUERADE 한다는 의미입니다.
다음으로 Pod를 생성한 후 외부 통신을 실행해 봅니다.
# 파드 생성
curl -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod1.yaml
kubectl apply -f node1-pod1.yaml
# 생성된 파드 정보 확인
kubectl get pod -o wide
# 노드에서 실행
# iptables NAT MASQUERADE 모니터링 : pkts 증가 확인
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
# (컨트롤플레인 노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=<각자 실습 환경에 따라 다름>
VETH1=calice0906292e2
# 패킷 덤프 실행 >> 어떤 인터페이스에서 패킷 캡쳐 확인이 되나요?
tcpdump -i any -nn icmp
tcpdump -i $VETH1 -nn icmp
tcpdump -i tunl0 -nn icmp
tcpdump -i ens5 -nn icmp # [실습환경 A Type]
# nat MASQUERADE rule 카운트(pkts)가 증가!
## 출발지 매칭은 cali40masq-ipam-pools 을 사용
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
root@k8s-w1:~# iptables -n -v -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
pkts bytes target prot opt in out source destination
3 252 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully
# IPSET 으로 의 cali40masq-ipam-pools IP 대역 정보 확인 : 172.16.0.0/16 대역임을 확인
ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x97754149
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16
veth 인터페이스(calice#~)에서 잡았을 때,
tunnel 인터페이스에서 잡았을 때,
빠져나가는 인터페이스 (ens5)를 확인
icmp를 통해 pod에서 통신할 때 192.168.10.101로 돌아옵니다.
외부 통신에서는 터널을 통과하지 않는다는 것을 확인했습니다.
서로 다른 노드의 Calico CNI 기반 Pod to Pod 통신
IPIP(Internet Protocol in Internet Protocol): IPIP 터널링은 네트워크에서 IP 패킷을 다른 IP 패킷 안에 캡슐화(Encapsulation) 하여 전송하는 기술입니다. 이 기술을 통해 두 개의 서로 다른 네트워크를 중간 네트워크를 통해 연결하거나, VPN과 같은 가상 네트워크를 구성할 수 있습니다.
다른 노드 간의 파드간 통신 시에는 IPIP터널(기본값) 모드를 통해서 이루어집니다.
각 노드에 파드 네트워크 대역은 BIRD를 통해 BGP로 전파되며, felix에 의해 호스트 라우팅 테이블에 자동으로 업데이트됩니다.
다른 노드 간의 Pod 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져 상대 노드로 전달되고, 상대 노드의 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부 Pod와 통신하는 구조입니다.
파드 배포전 기본 상태 확인
노드간 BGP로 Pod 대역 정보가 전파되는 것을 확인해 봅니다.
# 아래 명령어로 확인 시 나머지 노드들의 파드 대역을 자신의 호스트 라우팅 테이블에 가지고 있고, 해당 경로는 tunl0 인터페이스로 보내게 된다
route | head -2 ; route -n | grep tunl0
# 마스터노드, 노드1~
root@k8s-m:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.16.34.0 192.168.20.100 255.255.255.0 UG 0 0 0 tunl0
172.16.116.0 192.168.10.10 255.255.255.0 UG 0 0 0 tunl0
172.16.184.0 192.168.10.102 255.255.255.0 UG 0 0 0 tunl0
# 노드1
root@k8s-w1:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.16.34.0 192.168.20.100 255.255.255.0 UG 0 0 0 tunl0
172.16.116.0 192.168.10.10 255.255.255.0 UG 0 0 0 tunl0
172.16.158.0 192.168.10.101 255.255.255.0 UG 0 0 0 tunl0
...
# 터널 인터페이스에 IP가 할당되어 있고, MTU는 1480(IP헤더 20바이트 추가를 고려)을 사용하며, 현재 TX/RX 카운트는 0 이다
# Calico 사용 시 파드의 인터페이스도 기본 MTU 1480 을 사용한다
ifconfig tunl0
# 노드1
root@k8s-w1:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP> mtu 8981
inet 172.16.158.0 netmask 255.255.255.255
tunnel txqueuelen 1000 (IPIP Tunnel)
RX packets 66006 bytes 6140377 (6.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 74957 bytes 66884209 (66.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# 노드2
root@k8s-w2:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP> mtu 8981
inet 172.16.184.0 netmask 255.255.255.255
tunnel txqueuelen 1000 (IPIP Tunnel)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
실습을 위한 Pod를 배포
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
# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml
# calicoctl 이용한 endpoint 확인
calicoctl get workloadendpoints
파드를 생성하게 되면 다음과 유사한 상태가 됩니다.
네트워크 정보를 확인해 봅니다.
route -n | head -2 ; route -n | grep 172.16.
ip -c route
172.16.158.x 대역의 pod 들의 세부 라우팅 정보가 표시되는 것을 확인했습니다.
파드간 통신 실행 및 확인
tunl0과 eth0에 tcpdump를 걸고, Pod1에서 Pod2로 ping을 보낼 때의 패킷의 흐름을 확인합니다.
tunl0 - 터널 인터페이스에 파드간 IP 패킷 정보를 확인했습니다.
eth0(enp#~) - IP Outer 헤더 안쪽에 IP 헤더 1개가 더 있는 것을 확인했습니다. (IPIP : 4번)
'클라우드 컴퓨팅 & NoSQL > [KANS] 쿠버네티스 네트워크 심화 스터디' 카테고리의 다른 글
[4주차 - Service : ClusterIP, NodePort] KANS 스터디 (24.09.22) (2) | 2024.09.26 |
---|---|
[3주차(2/2) - k8s Calico CNI mode & 운영] KANS 스터디 (24.09.08) (0) | 2024.09.18 |
[2주차(2/2) - Flannel CNI] KANS 스터디 (24.09.01) (4) | 2024.09.05 |
[2주차(1/2) - K8S Pause container] KANS 스터디 (24.09.01) (6) | 2024.09.04 |
[1주차(2/2) - 컨테이너 네트워크 & IPTables] KANS 스터디 (24.08.25) (6) | 2024.09.01 |