쿠버네티스 네트워킹 요구 사항
쿠버네티스 네트워크 모델은 아래 4가지 요구사항이 있습니다.
1. 모든 pod는 서로 통신할 수 있어야하고, 모든 pod는 nat없이 서로 직접 통신이 가능해야합니다.
2. 모든 node는 nat없이 pod와 통신할 수 있어야 합니다.
3. pod는 자신이 인식하는 자기의 ip와 다른 pod가 인식하는 자신의 ip가 동일해야 합니다.
또, 쿠버네티스는 아래 4가지의 문제를 해결해야합니다.
1. 컨테이너 간 루프백 통신
2. 파드 간 통신
3. 파드에서 서비스를 통한 통신
4. 외부에서 서비스를 통한 통신
Flannel CNI
Flannel은 단일 바이너리 에이전트 데몬이 각 노드에서 동작하며 주로 소~중 규모의 클러스터나 간단한 네트워킹이 필요할 때 사용합니다.
Flannel은 UDP를 이용한 패킷 캡슐화를 사용하여 노드 간 통신을 처리합니다.
또 다양한 백엔드 VXLAN, host-gw 등을 지원하지만, 위에 언급한 내용과 같이 대규모 클러스터에서 사용 시 성능 이슈가 발생할 수 있습니다.
[실습]
Step 1) kind & Flannel 배포
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
# flannel 설치를 위해 defaultCNI를 Disable
# 이럴 경우 Static pod만 생성됨
networking:
disableDefaultCNI: true
클러스터를 생성하고 각 node를 조회하면 not ready 상태로 확인되는데, 이유는 CNI가 설치되지 않아, 노드 간의 통신이 불가능하기 때문입니다.
실습에 필요한 컨테이너 내부 패키지 생성
$ 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'
Step 2) Flannel 설치
$ kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
Flannel 생성 확인
kubectl describe cm -n kube-flannel kube-flannel-cfg
cni-conf.json:
----
{
"name": "cbr0", ## bridge interface 정보
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json:
----
{
"Network": "10.244.0.0/16", ## pod들에 할당할 대역
"EnableNFTables": false,
"Backend": { ## 네트워크 타입
"Type": "vxlan"
}
}
Flannel이 정상적으로 설치되었지만 아래와 같이 아직 pod 생성에 실패하고 있는 것들이 있습니다.
로그를 보면 bridge 플러그인이 없다고 표시되고 있습니다.
Step 3) Bridge Plugin 추가하기
https://github.com/containernetworking/plugins
경로에서 다운로드 받은 bridge 플러그인을 각 노드에 복사하고 권한을 부여합니다.
## bridge 파일 복사
$ docker cp bridge myk8s-control-plane:/opt/cni/bin/bridge
$ docker cp bridge myk8s-worker:/opt/cni/bin/bridge
$ docker cp bridge myk8s-worker2:/opt/cni/bin/bridge
## bridge 파일 권한 설정
$ docker exec -it myk8s-control-plane chmod 755 /opt/cni/bin/bridge
$ docker exec -it myk8s-worker chmod 755 /opt/cni/bin/bridge
$ docker exec -it myk8s-worker2 chmod 755 /opt/cni/bin/bridge
명령어를 통해 bridge 파일이 잘 올라 갔는지 확인합니다.
이후에 생성이 실패했던 Pod들의 상태를 보면 running 상태로 잘 생성되었습니다.
Step 4) Flannel 정보 확인
# flannel 정보 확인 : 대역, MTU
$ 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
# 각 노드에 할당된 dedicate subnet
$ kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
# 노드 정보 중 flannel 관련 정보 확인: VSLAN 모드 정보와, VTEP 정보 (노드 IP, VtepMac)을 확인
$ kubectl describe node | grep -A3 Annotations
Step 5) 노드 내부에서 네임스페이스 비교
아래 Flannel, kube-proxy는 아래 그림에도 나와있듯 네트워크를 호스트와 공유합니다.
Flannel의 네트워크 기본 정보 확인을 해보면 vxlan type을 사용하고 있는 것도 표시되는 것을 확인할 수 있습니다.
route 정보 확인
# 다른 노드의 파드 대역(podCIDR)의 라우팅 정보가 업데이트되어 있음을 확인
$ ip -c route
default via 172.19.0.1 dev eth0
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
172.19.0.0/16 dev eth0 proto kernel scope link src 172.19.0.2
flannel.1 인터페이스를 통한 다른 노드의 arp 테이블 확인
$ ip -c neigh show dev flannel.1
10.244.0.0 lladdr 06:c0:1f:94:a6:a8 PERMANENT
10.244.2.0 lladdr 06:ee:9c:2b:3e:3d PERMANENT
[실습]
각 worker node에 파드 생성
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
파드가 생성되면 네트워크 인터페이스는 파드와 연결을 위해 cni0 이라는 이름의 브릿지에 연결됩니다.
custom bridge 정보를 조회할 수 있으며, cbr0 하위에 10.244.2.2는 파드의 ip를 의미합니다.
아래 명령어를 통해 veth의 정보를 조금 더 확인해 볼 수 있습니다.
출력된 정보에서 master cni0는 브릿지가 해당 인터페이스에 연결되어 있다를 의미합니다.
$ ip -c route
default via 10.244.1.1 dev eth0
10.244.0.0/16 via 10.244.1.1 dev eth0
10.244.1.0/24 dev eth0 proto kernel scope link src 10.244.1.5
# gateway는 node에서 확인하면 아래와 같이 확인된다.
# worker node 진입 후 실행
$ ip -c route
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
노드 간 통신에 대한 흐름은 아래와 같습니다.
먼저 Flannel이 설치되면서 아래와 같이 방화벽 정책이 허용되어 있고, 라우팅 역시 잘 적용되어 있습니다.
pod에서 2번 노드의 pod에 ping을 보내고, 외부로 ping을 보내보겠습니다.
- 노드 간 통신
pod-1에서 pod-2로 ping 테스트 시 정상적으로 통신되는 것을 확인할 수 있습니다.
node 간 통신은 아래와 같은 flow로 수행됩니다.
1. 파드에서 ping을 요청하면 cni0 브릿지로 패킷을 전송합니다.
2. cni0은 라우팅 정책을 통해 flannel.1 에게 패킷을 전달합니다.
3. flannel.1은 VXLAN 캡슐화 후 eth1에게 패킷을 전달합니다.
4. node 1의 eth1에서 node2의 eth1로 VXLAN 패킷을 전송합니다.
5. node2는 eth1이 받은 패킷을 flannel.1로 전달해 디캡슐화를 합니다.
6. flannel.1을 cni0으로 패킷을 전달합니다.
7. cni0은 파트2에 최종적으로 패킷을 전달합니다.
- 외부 통신
pod-1에서 외부 인터넷으로 통신 시 veth를 통해 cni0으로 패킷이 전달되고, eth1에 전달해 외부로 패킷이 전달 됩니다.
Flannel은 노드 간 통신에만 사용되기 때문에 외부 통신 시에는 사용되지 않습니다.
패킷 덤프 시에도 cni0에는 통신이 확인되지만 flannel에서는 확인되지 않는 것을 확인할 수 있습니다.
'Cloud > Kubernetes' 카테고리의 다른 글
[KANS] LoadBalancer (2) | 2024.10.05 |
---|---|
[KANS] Service : ClusterIP, NodePort (1) | 2024.09.28 |
[KANS] Calico 네트워크 모드 (1) | 2024.09.15 |
[KANS] Calico CNI 기본 통신 (0) | 2024.09.14 |
[KANS] Kind를 이용한 Pause container (0) | 2024.09.06 |