기본적으로 어떻게 하나의 node에서 kubernetes component들이 네트워킹을 이루고, 연결이 되었는 지를 알게되었었다.
그런데, 서로 다른 node에 있는 pod들끼리는 어떻게 네트워크를 이루고 소통이 가능한 것일까??
---Node1--- ---Node2--- ---Node3---
|pod1 | |pod4 | |pod7 |
| pod2 | | pod5 | | pod9 |
|pod3 | | pod6 | | pod8 |
----------- ----------- -----------
어떻게 서로 다른 node에 배포된 pod들끼리 하나의 network인 것처럼 네트워크 동작을 하는 것인가??
pod의 기본적인 network model은 다음과 같다.
1. 모든 pod들은 각자의 IP주소를 갖는다.
2. 모든 pod들은 같은 노드에 있는 모든 다른 pod와 소통할 수 있어야 한다.
3. 모든 pod들은 NAT없이도 다른 node에 있는 pod와 소통할 수 있어야 한다.
즉, pod가 배포되면 kubernetes는 IP를 부여하고 같은 노드, 서로 다른 노드에 있는 pod들끼리 소통할 수 있도록 연결성을 부여해주어야 한다는 것이다.
이러한 pod들 간의 네트워킹 문제를 해결하는 여러 solution들이 있다. 이전에 살펴보았던 CNI역시도 이 문제를 해결하는 기술이기 때문에 ciium, calico, flannel 등이 있다.
그러나, 우리는 하나하나 네트워크를 구성해나가도록 하여, 어떻게 pod들끼리 소통이 가능하게 되고, 연결되었는 지 알아보도록 하자.
다음과 같이 3개의 node가 있다고 하자. 이는 master node이던 worker node이던 상관없다.
----Node1---- ----Node2---- ----Node3----
| pod1 | | pod3 | | pod4 |
| pod2 | | | | |
| | | | | |
------------- ------------- -------------
192.168.1.11 192.168.1.12 192.168.1.13
| | |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
이렇게 구성된 cluster에서 각 노드마다의 pod들을 하나의 network로 연결해주는 virtual switch인 bridge를 만들어주는 것이다.
각 노드마다 해당 명령어로 bridge를 만들어주는 것이다.
ip link add v-net-0 type bridge
ip link set dev v-net-0 up
다음과 같은 것이다.
----Node1---- ----Node2---- ----Node3----
| pod1 | | pod3 | | pod4 |
| | pod2| | | | | | |
| | | | | | | | | |
| bridge | | bridge | | bridge |
| v-net-0 | | v-net-0 | | v-net-0 |
------------- ------------- -------------
192.168.1.11 192.168.1.12 192.168.1.13
| | |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
다음으로 bridge interface에 IP를 부여하여 pod가 해당 IP대역을 할당받을 수 있도록 만들도록 하자.
ip addr add 10.255.1.1/24 dev v-net-0
ip addr add 10.255.2.1/24 dev v-net-0
ip addr add 10.255.3.1/24 dev v-net-0
아래 그림과 같이 정리된다.
-------Node1------- -------Node2-------
| pod1 | | pod3 |
| | pod2 | | | |
| | | | | | |
| ---------- | | ---------- |
| | bridge| | | | bridge| |
| | v-net-0| | | | v-net-0| |
| ---------- | | ---------- |
|(10.244/1.0/24) | |(10.244/2.0/24) |
| 10.244.1.1 | | 10.244.2.1 |
------------------- -------------------
192.168.1.11 192.168.1.12
| |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
이제 각 node마다 bridge도 만들어지고 IP도 설정되었으니, pod와 bridge를 연결시켜주면 된다. 즉, pod와 bridge에 veth pair(virtual cable)을 연결하고, pod에 bridge network대역의 IP를 할당해주어 interface를 설정해주는 것이다.
다음 동작을 하게 되는 것이다.
# create veth pair
ip link add ...
# attach veth pair
ip link set ...
ip link set ...
# assign IP Address
ip -n <namespace> addr add ...
ip -n <namespace> route add ...
# bring up interface
ip -n <namespace> link set ...
이렇게 각 pod마다 veth를 설정하고 bridge에 연결하고 bridge IP대역으로 IP를 pod에 할당하여 서로를 연결하는 것이다.
-------Node1------- -------Node2-------
| pod1 | | pod3 |
|10.244.1.2 | | 10.244.2.2 |
| | | | | |
| | pod2 | | | |
| | 10.244.1.3 | | | |
| | | | | | |
| ---------- | | ---------- |
| | bridge| | | | bridge| |
| | v-net-0| | | | v-net-0| |
| ---------- | | ---------- |
|(10.244/1.0/24) | |(10.244/2.0/24) |
| 10.244.1.1 | | 10.244.2.1 |
------------------- -------------------
192.168.1.11 192.168.1.12
| |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
위와 같이 설정하여 각 node안에서의 pod들은 하나의 network에 연결되어 서로 통신이 가능하다는 것을 알게되었다.
이제는 서로 다른 node들끼리의 pod들이 어떻게 network로 연결되는 지 알아보도록 하자.
만약, 10.244.1.2
가 10.244.2.2
에 ping을 보낸다면, 두 노드는 서로 다른 network이기 때문에 Network is unreachable
로 나온다.
따라서, route table을 만들어주어야 한다. 즉, node1에서 node2의 10.244.2.2
를 만나기 위해서는 192.168.1.12
를 지나야한다는 것을 설정해주어야 한다.
ip route add 10.244.1.2 via 192.168.1.11
각 pod와 각 node에 대한 맵핑으로 route table들을 하나씩 만드는 방법 밖에 없다.
따라서, 이러한 작업들을 간단히 하기 위해서, kubernetes는 각 node의 bridge network를 이어주는 하나의 network를 만들어준다. 즉, node들의 bridge를 연결해주는 하나의 bridge를 더두는 것이다. 이는 모든 host들을 default gateway로서 사용하여 network를 이어주는 것이다.
Network | Gateway |
---|---|
10.244.1.0/24 | 192.168.1.11 |
10.244.2.0/24 | 192.168.1.12 |
10.244.3.0/24 | 192.168.1.13 |
이제 이들을 연결해주는 하나의 network를 만드는 것이다.
-------Node1------- -------Node2-------
| pod1 | | pod3 |
|10.244.1.2 | | 10.244.2.2 |
| | | | | |
| | pod2 | | | |
| | 10.244.1.3 | | | |
| | | | | | |
| ---------- | | ---------- |
| | bridge| | | | bridge| |
| | v-net-0| | | | v-net-0| |
| ---------- | | ---------- |
|(10.244/1.0/24) | |(10.244/2.0/24) |
| 10.244.1.1 | | 10.244.2.1 |
------------------- -------------------
192.168.1.11 192.168.1.12
| |
| |
--------10.244.0.0/16----------
| |
-------------------------------
| |
| |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
10.244.0.0/16
대역을 가지고 위에서 정의한 route table을 가지고 있으니 10.244.1.2
에서 10.244.2.2
로 ping을 보내면 192.168.1.12
을 packet이 거쳐 10.244.2.
bridge network를 타고가 pod3인 10.244.2.2
에 당도하는 것이다.
반대로 응답도 10.244.0.0/16
으로 packet이 전달되어 route table에 의해 192.168.1.11
gateway를 지나 10.244.1.1
bridge network를 지나 pod1인 10.244.1.2
에 당도한다.
그런데, 누군가 이러한 위의 작업들에 대한 script를 만들어주고 구동해야한다. 이러한 복잡한 작업들을 누가 해주는 것인가?? 그것이 바로 CNI가 해주는 것이다.
CNI는 container가 만들어지면 일련의 script를 구동하여 bridge에 container를 연결하고 veth를 만들고, 다른 node와의 성공적인 통신을 위해 route table을 설정해준다. 더 정확히는 CNI plugin의 방법으로 route table을 효율적이고 쉬운 방법으로 설정해준다.
실제로 container가 만들어지면 각 노드의 Container Rumtime은 CNI 구동을 위한 config 설정을 위해 /etc/cni/net.d/net-script.conflist
를 확인하여 config를 설정하고, /opt/cni/bin/net-script.sh
를 실행하여 add
명령으로 container에 network namespace를 부여한다.
./net-script.sh add <container> <namespace>
그 다음이 script가 위의 과정들을 처리해주는 것이다.
이전에도 말했듯이 CNI는 kubernetes에서 container runtime이 CNI 스펙에 따라 container network를 구축하도록 하는 일종의 규칙이라고 했다. 그리고, 이 규칙에 따라 구현한 구현체가 바로 CNI plugin이고, containerd와 같은 container runtime은 이러한 CNI plugin을 사용하여 kubernetes에서 container 생성 시 network namespace를 부여하고 network망을 구축하는 것이다.
container runtime으로는 containerd, cri-o 등이 있는데, 이들은 container생성 시에 CNI plugin을 호출하여 container network namespace, network망을 구축해주는 것이다.
여기서 CNI plugin으로는 cilium, flannel, calico 등이 있으며, 이들은 /opt/cni/bin
에 위치해있다. 또한, 이들의 configuration들은 /etc/cni/net.d
에 있으며 cni plugin들 각각 bridge.conflist
, flannel.conflist
와 같은 형태로 configuration을 가지고 있는 것이다.
ls /etc/cni/net.d
10-bridge.conf ...
cni의 10-bridge.conf
는 bridge에 대한 설정이 들어있는 파일인데, 다음과 같이 생겼다.
cat /etc/cni/net.d/10-bridge.conf
{
"cniVersion": "0.2.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"isMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.22.0.0/16",
"routes": [
{"dst": "0.0.0.0/0" }
]
}
}
이는 CNI standard configuration 형식을 가졌으면 이름은 mynet
이고 type은 bridge
이다.
이제 실제로 CNI plugin을 사용해보도록 하자. 우리는 weave를 사용해보도록 하자.
https://www.weave.works/blog/weave-cloud-end-of-service
weavenet을 kubernetes에 설치해보록 하자.
kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml
관련 문서는 다음과 같다. https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#-installation
이전에 node간의 통신을 위해서 bridge를 하나 더 만들어서 route table을 관리한다고 했다.
-------Node1------- -------Node2-------
| pod1 | | pod3 |
|10.244.1.2 | | 10.244.2.2 |
| | | | | |
| | pod2 | | | |
| | 10.244.1.3 | | | |
| | | | | | |
| ---------- | | ---------- |
| | bridge| | | | bridge| |
| | v-net-0| | | | v-net-0| |
| ---------- | | ---------- |
|(10.244/1.0/24) | |(10.244/2.0/24) |
| 10.244.1.1 | | 10.244.2.1 |
------------------- -------------------
192.168.1.11 192.168.1.12
| |
| |
--------10.244.0.0/16----------
| |
-------------------------------
| |
| |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
Network | Gateway |
---|---|
10.244.1.0/24 | 192.168.1.11 |
10.244.2.0/24 | 192.168.1.12 |
10.244.3.0/24 | 192.168.1.13 |
그러나 node수가 수백 수천대가 되어가고 pod수가 각 node마다 수 백개 씩 있다면 route table을 매번 유지하는 것은 어렵다.
여기서 CNI plugin은 새로운 방법을 제시해주는데, 각 node마다 CNI agenet를 두는 것이다. weave의 경우 weave agent를 각 node마다 하나씩 배포해준다. 그리고 각 node의 weave agent는 서로 연결되어 작은 network를 갖는다. 즉, peer-to-peer에 대한 topology를 갖게 되는 것이다.
-------Node1------- -------Node2-------
| pod1 | | pod3 |
|10.244.1.2 | | 10.244.2.2 |
| | | | | |
| | pod2 | | | |
| | 10.244.1.3 | | | |
| | | | | | |
| ---------- | | ---------- |
| | bridge| | | | bridge| |
| | v-net-0| | | | v-net-0| |
| ---------- | | ---------- |
|(10.244/1.0/24) | |(10.244/2.0/24) |
| 10.244.1.1 | | 10.244.2.1 |
| | | |
| weave agent-------------weave agent |
------------------- -------------------
192.168.1.11 192.168.1.12
| |
| |
-----------------LAN---------------
| 192.168.1.0 |
-----------------------------------
각 weave agent
는 노드마다 설치되고 서로가 연결되어 peer-to-peer 네트워크를 이루는 것이다.
만약 10.244.1.2
IP를 가진 pod1에서 Node2에 있는 10.244.2.2
IP를 가진 pod3로 packet을 보낸다고 할 때, node1에 있는 weave agent는 해당 packet을 가로채, destination을 보고 현재 node가 아닌 다른 node로 향하는 packet임을 필터링한다. 그리고 나서 packet의 source와 desination을 바꾸어주는데, 이 기준은 weave agent들끼리 연결된 peer-to-peer network 기준으로 바꿔주는 것이다.
즉, weave-agent는 packet을 encapsulating하여 다른 node에 있는 weave-agent로 보내주는 것이다. node2에 있는 weave-agent는 해당 packet을 받아 역직렬화하여 원래의 packet을 가져와 내부의 bridge interface로 전달하고 bridge에서 pod3으로 전달되는 것이다.
ps -aux | grep kubelet | grep container-runtime-endpoint
oot 4331 0.0 0.0 4283916 97936 ? Ssl 16:35 0:06 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --pod-infra-container-image=registry.k8s.io/pause:3.9
unix:///var/run/containerd/containerd.sock
이다.
ls /opt/cni/bin/
bandwidth dummy host-device loopback ptp tap vrf
bridge firewall host-local macvlan sbr tuning
dhcp flannel ipvlan portmap static vlan
/opt/cni/bin/
이다.