1. CNI 가 무엇인지 알아봅니다.
2. Flannel CNI의 특징에 대해서 알아봅니다.
- 그중에서도 오버레이 네트워크 / VXLAN / VLAN
Container network interface
먼저 쿠버네티스 네트워킹 모델을 이해하려 합니다.
기본적으로 쿠버네티스는 다음과 같은 원칙을 지켜서 아키텍처가 만들어졌습니다.
아래는 가시다님의 책 원고에 내용을 발췌 한 것 입니다.
도커 네트워크를 통해 컨테이너의 네트워크에 대해서 앞시간에 아주 자세히 다뤘기 때문에, 쿠버네티스 네트워크 모델의 내용을 이해할수 있어야 합니다.
도커 네트워크에서 다뤄지는 영역에서 더 추가된 부분은 다음과 같이 그림으로 나타내어진 동작 방식에서 유추해 볼수 있습니다.
1. 루프백 통신
2. 같은 노드 내에서 파드간 통신
3. 클러스터 내에서 서비스를 통한 통신
4. 클러스터 외부에서 서비스를 통한 통신
Flannel
- Flannel runs a small, single binary agent called flanneld
on each host, and is responsible for allocating a subnet lease to each host out of a larger, preconfigured address space. - 모든 노드에 flanneld 데몬형태로 동작
- 네트워킹 환경 지원 (Backends) : VXLAN, host-gw, UDP, 그외에는 아직 실험적임
- VXLAN (권장) : Port(UDP 8472), DirecRouting 지원(같은 서브넷 노드와는 host-gw 처럼 동작)
- 단, 네트워크 엔지니어분들이 알고 있는 ‘L2 확장’ 이 아니라, 각 노드마다 별도의 서브넷이 있고, 해당 서브넷 대역끼리 NAT 없이 라우팅 처리됨
- host-gw : 호스트 L2 모드? 일반적으로 퍼블릭 클라우드 환경에서는 동작하지 않는다
- UDP (비권장) : VXLAN 지원하지 않는 오래된 커널 사용 시, Port(UDP 8285)
- VXLAN (권장) : Port(UDP 8472), DirecRouting 지원(같은 서브넷 노드와는 host-gw 처럼 동작)
- cni0 - 노드마다 cni0 생성 : bridge 역할
- flannel.1 - 노드마다 flannel.1 생성 : VXLAN VTEP 역할 , 뒷에 숫자는 VXLAN id 1 에 1을 의미
VXLAN VTEP 역할?
- VXLAN network model
- VLAN
스위치들 사이에 터널이 수립되고 데이터를 전송하는 VM은 원본 데이터를 스위치로 전송을 하고 스위치는 수립된 터널을 통해 데이터를 받는 VM이 있는 스위치까지 VXLAN 터널을 통해 패킷을 전송 합니다. 패킷을 수신한 스위치는 캡슐화된 VXLAN 패킷을 해제하여 원본 데이터를 획득하고 헤더를 참조하여 목적지 서버까지 전달 하게 됩니다.
VXLAN 네트워크에서 VTEP과 VNI라는 새로운 요소가 등장하며, 이는 전통적인 데이터 센터 네트워크에서는 존재하지 않는 개념 입니다. VXLAN 네트워크 모델을 이해하기 위해 VTEP과 VNI 이해가 필요 합니다.
- VTEP : VTEP은 VXLAN 터널을 구성하는 종단 장비 입니다.
- VTEP은 VXLAN segment와 tenant 장비를 맵핑하고 원본 이더넷 프레임을 캡슐화 또는 캡슐화 해제를 수행 합니다. VTEP은 Local LAN Segement와 통신하기 위한 인터페이스와 IP Network 통신을 위한 인터페이스를 가지고 있습니다.
- IP 인터페이스는 고유한 IP 주소를 가지고 있으며 IP 전송 네트워크에서 VTEP을 식별하기 위한 용도로 사용되며 Infrastructure VLAN 이라는 명칭을 사용 합니다. VTEP 장비는 해당 IP 주소를 사용하여 이더넷 프레임을 캡슐화하여 IP 인터페이스를 통해 VXLAN 패킷을 전송합니다.
- VNI: 식별자 ( VLAN ID와 유사)
- 그림 1-6 VXLAN Network Model에서 볼 수 있듯이 VNI는 식별자이며 VLAN ID와 유사합니다. VNI는 tenant를 식별하며, 다른 VNI를 할당 받은 VM들은 Layer 2 환경에서 서로 통신 하지 못합니다. VXLAN 헤더에서 VNI는 24 bit이며 최대 1600만개의 VNI를 생성할 수 있습니다. (유효한 VNI 값의 범위 - 4096 to 16,777,215)
VLAN
1계층 장비인 허브와 2계층 장비인 스위치의 차이점은 VLAN의 유무라고 알고 있다.
LAN: 일반적으로 하나의 사무실 또는 주택 내의 네트워크를 LAN 이라고 부른다.
동일한 ip 대역과 동일한 subnet mask를 사용한다.
arp request가 닿는 모든 범위
Broadcast : 허브나 스위치는 브로드 캐스팅을 통해 ARP request를 송신지 포트를 제외한 모든 포트에 신호로 내보내기 때문에 트래픽이 늘어날수 밖에 없고 이는 성능의 저하로 연결됩니다. ARP는 브로드 캐스팅 방식의 대표적인 프로토콜이죠.
네트워크 장비에 연결된 컴퓨터가 늘어날수록 브로드캐스트의 숫자도 늘어납니다.
이때 필요한것이 VLAN 입니다.
이름 그대로 한대의 스위치로 가상의 영역을 분리하는 기술입니다. 스위치에 연결된 호스트 중에서는 서로 메시지를 주고 받을
일이 적거나 브로드캐스트 메시지를 받을 필요가 없어서 굳이 같은 네트워크(LAN)에 속할 필요가 없는 호스트가 있을수
있습니다.
그렇다고 이들을 분리하고자 매번 새로운 스위치 장비를 구입하는것은 정말 낭비이지요. 이때 구성하는것이 VLAN 입니다.
POD 통신 과정
✅ cni0 의 역할
- 같은 노드 내에서의 파드 간 통신
- 새로운 파드가 생성되면, Kubernetes 네트워크 플러그인은 파드에 가상 이더넷 인터페이스(veth 쌍)를 붙입니다. 이 veth 쌍의 반대쪽 끝은 cni0 브리지에 연결됩니다.
- 이 브리지를 통해 같은 노드에 있는 모든 파드가 서로 Layer 2 (이더넷) 네트워크 상에서 통신할 수 있습니다.
- 호스트 네트워크와의 연결
- cni0는 호스트의 네트워크 인터페이스(예: eth0)와 연결됩니다. 이를 통해 파드들은 외부 네트워크(클러스터 외부) 또는 클러스터 내의 다른 노드와 통신할 수 있습니다.
- 네트워크 플러그인은 보통 CNI 설정에 정의된 IP 범위를 사용해 각 파드에 적절한 IP 주소를 할당합니다.
- 트래픽 라우팅
- cni0 브리지는 같은 노드 내에서 파드 간 트래픽을 처리하며, 다른 노드나 외부 네트워크로 향하는 트래픽도 올바른 인터페이스로 포워딩되도록 합니다. 이는 IP 라우트를 통해 이루어지거나, Flannel, Calico와 같은 다른 CNI 플러그인을 통해 관리될 수 있습니다.
✅ flannel.1 의 역할
- 다른 노드에 위치한 파드 간 통신
- VXLAN VTEP 역할 , 뒷에 숫자는 VXLAN id 1 에 1을 의미
- flannel.1은 패킷의 인캡슐레이션과 디캡슐레이션을 담당함
- 오버레이 네트워크는 물리적인 네트워크 위에 추가로 구성된 가상 네트워크 - 이 가상 네트워크는 물리적 네트워크 구조에 구애받지 않고 서로 다른 노드에 있는 Pod들이 마치 같은 네트워크에 있는것 처럼 통신 할수 있게 해줍니다.
실습
- kind와 Flannel 배포
#
cat <<EOF> kind-cni.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
mynode: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
controllerManager:
extraArgs:
bind-address: 0.0.0.0
etcd:
local:
extraArgs:
listen-metrics-urls: http://0.0.0.0:2381
scheduler:
extraArgs:
bind-address: 0.0.0.0
- |
kind: KubeProxyConfiguration
metricsBindAddress: 0.0.0.0
- role: worker
labels:
mynode: worker
- role: worker
labels:
mynode: worker2
networking:
disableDefaultCNI: true
EOF
kind create cluster --config kind-cni.yaml --name myk8s --image kindest/node:v1.30.4
# 배포 확인
kind get clusters
kind get nodes --name myk8s
kubectl cluster-info
# 네트워크 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
# 노드 확인 : CRI
kubectl get nodes -o wide
# 노드 라벨 확인
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
...
"mynode": "control-plane",
...
kubectl get nodes myk8s-worker -o jsonpath={.metadata.labels} | jq
kubectl get nodes myk8s-worker2 -o jsonpath={.metadata.labels} | jq
# 컨테이너 확인 : 컨테이너 갯수, 컨테이너 이름 확인
docker ps
docker port myk8s-control-plane
docker port myk8s-worker
docker port myk8s-worker2
# 컨테이너 내부 정보 확인
docker exec -it myk8s-control-plane ip -br -c -4 addr
docker exec -it myk8s-worker ip -br -c -4 addr
docker exec -it myk8s-worker2 ip -br -c -4 addr
#
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping htop git nano -y'
docker exec -it myk8s-worker sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
docker exec -it myk8s-worker2 sh -c 'apt update && apt install tree jq psmisc lsof wget bridge-utils tcpdump iputils-ping -y'
flannel을 설치하려면 bridge 실행 파일을 생성하여 각 인스턴스마다 배포해야 합니다. 다음과 같이 bridge 실행파일 생성 후 로컬에 복사합니다.
docker exec -it myk8s-control-plane bash
---------------------------------------
apt install golang -y
git clone https://github.com/containernetworking/plugins
cd plugins
chmod +x build_linux.sh
#
./build_linux.sh
Building plugins
bandwidth
firewall
portmap
sbr
tuning
vrf
bridge
host-device
ipvlan
loopback
macvlan
ptp
vlan
dhcp
host-local
static
# 파일 권한 확인 755
ls -l bin
-rwxr-xr-x 1 root root 4559683 Sep 3 04:54 bridge
...
exit
---------------------------------------
# 자신의 PC에 복사 : -a 권한 보존하여 복사(755)
docker cp -a myk8s-control-plane:/plugins/bin/bridge .
ls -l bridge
flannel을 설치 합니다.
watch -d kubectl get pod -A -owide
#
kubectl describe pod -n kube-system -l k8s-app=kube-dns | grep Events: -A 6
# Flannel cni 설치
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
# namespace 에 pod-security.kubernetes.io/enforce=privileged Label 확인
kubectl get ns --show-labels
kubectl get ds,pod,cm -n kube-flannel
kubectl describe cm -n kube-flannel kube-flannel-cfg
kubectl describe ds -n kube-flannel kube-flannel-ds
kubectl exec -it ds/kube-flannel-ds -n kube-flannel -c kube-flannel -- ls -l /etc/kube-flannel
# failed to find plugin "bridge" in path [/opt/cni/bin]
kubectl get pod -A -owide
kubectl describe pod -n kube-system -l k8s-app=kube-dns
Warning FailedCreatePodSandBox 35s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "786e9caec9c312a0b8af70e14865535575601d024ec02dbb581a1f5ac0b8bb06": plugin type="flannel" failed (add): loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory
Warning FailedCreatePodSandBox 23s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6ccd4607d32cbb95be9ff40b97a436a07e5902e6c24d1e12aa68fefc2f8b548a": plugin type="flannel" failed (add): failed to delegate add: failed to find plugin "bridge" in path [/opt/cni/bin]
#
kubectl get pod -A -owide
flannel을 설치 후 확인
for i in myk8s-control-plane myk8s-worker myk8s-worker2; do echo ">> node $i <<"; docker exec -it $i cat /run/flannel/subnet.env ; echo; done
>> node myk8s-control-plane <<
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=65485
FLANNEL_IPMASQ=true
>> node myk8s-worker <<
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.1/24
FLANNEL_MTU=65485
FLANNEL_IPMASQ=true
>> node myk8s-worker2 <<
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.2.1/24
FLANNEL_MTU=65485
FLANNEL_IPMASQ=true
# kubectl describe node | grep -A3 Annotations
Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"8a:00:fe:cd:cf:93"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 172.18.0.4
--
Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"16:05:d3:c8:e3:34"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 172.18.0.3
--
Annotations: flannel.alpha.coreos.com/backend-data: {"VNI":1,"VtepMAC":"4e:cd:b4:47:48:56"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 172.18.0.2
- 노드 내부에서 Flannel 프로세스 확인
- Network 네임스페이스를 호스트와 공유하기 때문에 Flannel / Kube-proxy 컨테이너는 Host IP를 가진다.
docker exec -it myk8s-worker bash
lsns -p $(pgrep flanneld)
lsns -p $(pgrep kube-proxy)
root@myk8s-worker:/# lsns -p $(pgrep flanneld)
NS TYPE NPROCS PID USER COMMAND
4026531834 time 21 1 root /sbin/init
4026531837 user 21 1 root /sbin/init
4026532634 uts 15 1 root /sbin/init
4026532643 net 15 1 root /sbin/init
4026532992 ipc 2 2259 65535 /pause
4026533000 mnt 1 2450 root /opt/bin/flanneld --ip-masq --kube-subnet-mgr
4026533001 pid 1 2450 root /opt/bin/flanneld --ip-masq --kube-subnet-mgr
4026533002 cgroup 1 2450 root /opt/bin/flanneld --ip-masq --kube-subnet-mgr
root@myk8s-worker:/# lsns -p $(pgrep kube-proxy)
NS TYPE NPROCS PID USER COMMAND
4026531834 time 21 1 root /sbin/init
4026531837 user 21 1 root /sbin/init
4026532634 uts 15 1 root /sbin/init
4026532643 net 15 1 root /sbin/init
4026532816 cgroup 17 1 root /sbin/init
4026532982 ipc 2 286 65535 /pause
4026532986 mnt 1 319 root /usr/local/bin/kube-proxy --config=/var/lib/kube-pr
4026532987 pid 1 319 root /usr/local/bin/kube-proxy --config=/var/lib/kube-pr
root@myk8s-worker:/#
- 파드간 통신 테스트를 위해 2개의 파드를 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: myk8s-worker
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: pod
spec:
nodeSelector:
kubernetes.io/hostname: myk8s-worker2
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
kubectl exec -it pod-1 -- zsh
# 호스트 GW IP 호출 (동일 노드 간 내부 통신)
ping 10.244.1.1
# Pod2 IP 호출 (다른 노드의 파드 간 통신)
ping 10.244.2.2
# 외부 인터넷 IP 호출
ping 8.8.8.8
# 외부 인터넷 도메인 ghcnf
curl -s wttr.in/Seoul
네트워크 패킷 분석
1) tcpdump -i cni0 -nn icmp
- ping 10.244.1.2 (같은 노드에 있는 pod에 통신할때)
- ping 10.244.2.2 -> 10.244.1.5
2) tcpdump -i flannel1 -nn icmp
3) tcpdump -i eth0 -nn icmp
4) wireshark
다음과 같이 네트워크 트래픽에 대한 확인을 마쳤습니다.
'컨테이너 > 쿠버네티스 네트워크' 카테고리의 다른 글
[KANS] 쿠버네티스 네트워크 (8) Calico Mode / eBPF (1) | 2024.09.17 |
---|---|
[KANS] 쿠버네티스 네트워크 (7) Calico CNI (0) | 2024.09.14 |
[KANS] 쿠버네티스 네트워크 (5) PAUSE 컨테이너 (3) | 2024.09.03 |
[KANS] 쿠버네티스 네트워크 (4) 쿠버네티스 인증 deep div (0) | 2024.09.02 |
[KANS] 쿠버네티스 네트워크 (3) 컨테이너 네트워크 & IPTABLES (0) | 2024.08.29 |