반응형

 

OCP Cluster를 구축한 직후에  ovs-vsctl 명령으로 Open vSwitch의 구성을 확인해봤다.

 

특징을 살펴보면, 

  • br-int Bridge에 각 Kube Node로 가는 물리 Port(50.50.51.2x)는 geneve 터널링 방식을 사용.
    (아래 ovs-vsctl 명령 결과에서 geneve로 검색하면 됨)

 

[root@worker1 ~]# ovs-vsctl show
9cad81c8-d7be-4b18-90e8-03aadd86a8cb
Bridge br-ex
Port enp131s0f0
Interface enp131s0f0
type: system
Port patch-br-ex_worker1.twcm.cloud-to-br-int
Interface patch-br-ex_worker1.twcm.cloud-to-br-int
type: patch
options: {peer=patch-br-int-to-br-ex_worker1.twcm.cloud}
Port br-ex
Interface br-ex
type: internal
Bridge br-int
fail_mode: secure
datapath_type: system
Port ovn-5e8778-0
Interface ovn-5e8778-0
type: geneve
options: {csum="true", key=flow, remote_ip="50.50.51.22"}
Port "83cf7b04f35b763"
Interface "83cf7b04f35b763"
Port br-int
Interface br-int
type: internal
Port d10af31c1d33dac
Interface d10af31c1d33dac
Port "460c3ee2f55e7c9"
Interface "460c3ee2f55e7c9"
Port ovn-f95ec3-0
Interface ovn-f95ec3-0
type: geneve
options: {csum="true", key=flow, remote_ip="50.50.51.21"}
Port patch-br-int-to-br-ex_worker1.twcm.cloud
Interface patch-br-int-to-br-ex_worker1.twcm.cloud
type: patch
options: {peer=patch-br-ex_worker1.twcm.cloud-to-br-int}
Port ovn-84a2f7-0
Interface ovn-84a2f7-0
type: geneve
options: {csum="true", key=flow, remote_ip="50.50.51.23"}
Port "5fe45b331cff8df"
Interface "5fe45b331cff8df"
Port ovn-k8s-mp0
Interface ovn-k8s-mp0
type: internal
Port "59a37a9be52afdb"
Interface "59a37a9be52afdb"
ovs_version: "2.15.4"

 

 

실제 Pod가 사용하는 Overlay Network Portovn-k8s-mp0는 아래 보이는 것처럼 MTU가 1400 bytes이다.

geneve 터널링을 위해 100 byte를 사용해야 하므로, MTU1400 bytes가 된 것이다.

 

[root@worker1 ~]# ifconfig ovn-k8s-mp0
ovn-k8s-mp0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1400
inet 10.131.0.2 netmask 255.255.254.0 broadcast 10.131.1.255
inet6 fe80::ec45:a3ff:fe53:665 prefixlen 64 scopeid 0x20<link>
ether ee:45:a3:53:06:65 txqueuelen 1000 (Ethernet)
RX packets 248078 bytes 20827992 (19.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 264379 bytes 23121981 (22.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

 

 

Pod가 IP Networking은 일반적인 L2 Switching 통해서 처리되므로, 아래 routing table을 참조하여 이웃하는 Worker, Master Node로 전달된다. 

10.128.0.0  10.131.0.0 network가 Pod가 사용하는 Network이다.

 

[root@worker1 ~]# netstat -nr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 50.50.51.10 0.0.0.0 UG 0 0 0 br-ex
10.128.0.0 10.131.0.1 255.252.0.0 UG 0 0 0 ovn-k8s-mp0
10.131.0.0 0.0.0.0 255.255.254.0 U 0 0 0 ovn-k8s-mp0
50.50.51.0 0.0.0.0 255.255.255.0 U 0 0 0 br-ex
169.254.169.0 50.50.51.10 255.255.255.252 UG 0 0 0 br-ex
169.254.169.3 10.131.0.1 255.255.255.255 UGH 0 0 0 ovn-k8s-mp0
172.30.0.0 50.50.51.10 255.255.0.0 UG 0 0 0 br-ex

 

 

Netfilter를 살펴보면,

Pod와 Service 리소스를 생성하면서 Netfilter의 Chain Rule 변화 여부를 모니터링 해봤는데, 변화가 없다.

 

[root@worker1 ~]# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
OVN-KUBE-ETP all -- anywhere anywhere
OVN-KUBE-EXTERNALIP all -- anywhere anywhere
OVN-KUBE-NODEPORT all -- anywhere anywhere
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
OVN-KUBE-SNAT-MGMTPORT all -- anywhere anywhere
KUBE-POSTROUTING all -- anywhere anywhere /* kubernetes postrouting rules */
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
OVN-KUBE-EXTERNALIP all -- anywhere anywhere
OVN-KUBE-NODEPORT all -- anywhere anywhere
Chain KUBE-MARK-MASQ (0 references)
target prot opt source destination
MARK all -- anywhere anywhere MARK or 0x4000
Chain KUBE-MARK-DROP (0 references)
target prot opt source destination
MARK all -- anywhere anywhere MARK or 0x8000
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
RETURN all -- anywhere anywhere mark match ! 0x4000/0x4000
MARK all -- anywhere anywhere MARK xor 0x4000
MASQUERADE all -- anywhere anywhere /* kubernetes service traffic requiring SNAT */ random-fully
Chain KUBE-KUBELET-CANARY (0 references)
target prot opt source destination
Chain OVN-KUBE-SNAT-MGMTPORT (1 references)
target prot opt source destination
SNAT all -- anywhere anywhere /* OVN SNAT to Management Port */ to:10.131.0.2
Chain OVN-KUBE-NODEPORT (2 references)
target prot opt source destination
Chain OVN-KUBE-EXTERNALIP (2 references)
target prot opt source destination
Chain OVN-KUBE-ETP (1 references)
target prot opt source destination
[root@worker1 ~]#

 

그리고 위 상황에서 Service  리소스를 한개를 ClusterIP type에서 NodePort type으로 변경했더니,

아래와 같이 OVN-KUBE-NODEPORT에 Chain Rule 한개 추가되었다.

 

[root@worker1 ~]# iptables -L -t nat
... 중간 생략 ...
## 이 내용이 추가되었음.
Chain OVN-KUBE-NODEPORT (2 references)
target prot opt source destination
DNAT tcp -- anywhere anywhere ADDRTYPE match dst-type LOCAL tcp dpt:31785 to:172.30.44.86:80
## 참고로 위 Chain Rule은 iptables 명령으로 추가한다면, 아래와 같이 실행하면 된다.
## (설명) destination port가 31785이면, 172.30.44.86:80 주소로 DNAT 처리하는 Rule을
## OVN-KUBE-NODEPORT 체인에 추가하라는 명령.
# -A OVN-KUBE-NODEPORT -p tcp -m addrtype --dst-type LOCAL -m tcp --dport 31785 -j DNAT --to-destination 172.30.44.86:80
... 중간 생략 ...

 

 

이하, 작성 중...  (빨리 작성해야 하는데...)
반응형

Kuberentes를 사용하면서 이해하기 어려운 부분이 CNI(Cluter Network Interface)이다.

실제로 각 CNI 마다 동작하는 방식과 구현이 다른데 CNI라고 퉁쳐서 표현하다보니 Kubernetes를 사용하는 운영자 입장에서는 CNI가 어떻게 IP Traffic(트래픽)을 처리하는지 알기 어렵다.

여러 종류의 CNI 중에서 내가 사용하고 있는 특정 CNI에 대해서 깊이 있게 설명하는 자료도 별로 없으니 스터디하기가 더욱 어렵다.

 

나의 일터에서는 주로 Openshift SDN을 사용하기 때문에 오늘은 시간을 내서 Openshift SDN을 깊게 파헤쳐보려 한다.

 

우선 아래 그림에 있는 빨간색 숫자를 따라 가면서 보자.

이 빨간 색 숫자의 순서대로 요청 패킷이 처리된다.

 

Openshift SDN 동작 방식

 

 

그림에 순서에 맞게 설명을 적었기 때문에 전반적인 설명은 필요 없을 것 같다.

단, 복잡한 처리가 있는 부분을 좀더 살펴 본다면,

 

(2) Netfilter를 이용하여 DNAT, SNAT 처리

Kubernetes Pod 정보를 조회해보면 Cluster IP를 볼 수 있다. 바로 그 Pod의 Cluster IP로 DNAT 처리되는 것이다.

즉, 10.10.12.208:8080 목적지 주소를 보고, 이 주소 값과 Mapping되는 Kubernetes Pod IP를 찾고 Netfilter를 이용하여 DNAT 처리한다.

Worker 1 Node에서 iptables 명령을 이용하여 Netfilter의 Chain Rule을 조회하면 실제로 아래와 같이 정보를 볼 수 있다.

이 Chain Rule대로 관련 Packet 조건을 찾고, 그 조건에 맞는 Netfilter Chain을 따라가면서 DNAT처리하는 것이다.

 

$ iptables -t nat -L KUBE-SERVICES -nv | grep 10.10.12.208
1760 106K KUBE-FW-BAR7JSQD2GL6A2KT tcp -- * * 0.0.0.0/0 10.10.12.208 /* almighty/almighty5:web loadbalancer IP */ tcp dpt:8080
… 중간 생략 …
$ iptables -t nat -L KUBE-SEP-ZBWV76PMI2XAVMCD -nv
1949 117K DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* almighty/almighty5:web */ tcp to:10.128.2.151:8080
—> kube-proxy가 실제 iptables 룰 생성시 사용한 명령은 아래와 같음.
KUBE-SEP-ZBWV76PMI2XAVMCD -p tcp -m comment --comment "almighty/almighty5:web" -m tcp -j DNAT --to-destination 10.128.2.151:8080

 

만약 Destination Pod가 다른 Worker Node에 있다면, 다른 Worker Node로 IP Packet을 보내기 위해 tun0 인터페이스를 이용하여야 한다. 이때 tun0의 IP Address로 SNAT 하도록 하는 Chain Rule 적용을 받는다.

 

Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
OPENSHIFT-MASQUERADE all -- anywhere anywhere /* rules for masquerading OpenShift traffic */
KUBE-POSTROUTING all -- anywhere anywhere /* kubernetes postrouting rules */
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
RETURN all -- anywhere anywhere mark match ! 0x1/0x1
MARK all -- anywhere anywhere MARK xor 0x1
MASQUERADE all -- anywhere anywhere /* kubernetes service traffic requiring SNAT */ random-fully

 

 

(3) DST IP로 갈 수 있는 Routing Table을 조회하여 tun0로 Packet을 보냄

아마 대부분 Kubernetes 사용자가 헷갈려하는 부분일 것이다.

만약 Destination Pod가 같은 Worker Node에 있다면, 단순히 Netfilter가 처리한 대로 Packet Forward만 하면 된다.

그런데, Destination Pod가 다른 Worker Node에 있다면 관련있는 물리 Network Port를 찾아서 보내야 한다.

위 그림에서 Worker 1과 Worker 2의 tun0 간에는 ARP Packet이 브로드캐스팅되므로, 이미 Worker 1에서는 Destination Pod IP에 해당하는 MAC Address를 알고 있다. 

(참고: arp 명령으로 Worker 1 노드의 ARP Table 조회 가능)

따라서 일반적인 L2 Forward 처리와 동일하게 처리된다.

 

 

(4) ~ (5) Encapsulate, Decapsulate

일반적인 VxLAN 터널링을 사용하여 Worker Node간에 Pod의 메시지를 전송해준다.

VxLAN 터널링 때문에 tun0의 MTU는 1450 byte가 된다. (왜냐하면, 터널링 헤더 메시지 때문에 50 Byte만큼 tun0 포트의 MTU가 작아지게 된다)

 

 

나머지 절차는 모두 L2 Switching이기 때문에 딱히 설명할 것이 없다.

 

응답 메시지는 따로 설명하지 않았다.

왜냐하면,

L2 Switching 구간은 단순히 Source Address와 Destination Address를 반대로 뒤집으면 되고,

Netfilter가 SNAT 처리한 부분의 Connection Track 정보를 찾아서 원래의 주소값으로 치환해주면 되기 때문이다.

 

 

블로그 작성자: sejong.jeonjo@gmail.com

 

반응형

이것저것 문서를 다 찾아보았지만 아래 Red Hat 설명이 제일 깔끔하고, 정확하다.

(Red Hat 검증팀이 직접 테스트해보면서, 그 절차를 문서화한 것이니까 당연히 정확할 듯)

 

 

16.9. DPDK 및 RDMA 사용 OpenShift Container Platform 4.10 | Red Hat Customer Portal

Access Red Hat’s knowledge, guidance, and support through your subscription.

access.redhat.com

 

 

Kubernetes Cluster에서 DKDP 테스트할 때, 필요한 예제 App과 Library !!!

 

 

GitHub - openshift/app-netutil: app-netutil is a library that provides API methods for applications to get pod network informati

app-netutil is a library that provides API methods for applications to get pod network information. - GitHub - openshift/app-netutil: app-netutil is a library that provides API methods for applicat...

github.com

 

 

GitHub - openshift/app-netutil: app-netutil is a library that provides API methods for applications to get pod network informati

app-netutil is a library that provides API methods for applications to get pod network information. - GitHub - openshift/app-netutil: app-netutil is a library that provides API methods for applicat...

github.com

 

 


 

아래 문서들은 호기심 해소를 위해 보면 좋은 자료들.

아직 내용을 다 읽어보진 않았지만, 설명 그림(illustration)이 괜찮게 보여서 인용해본다.

 

 

 

GitHub - intel/userspace-cni-network-plugin

Contribute to intel/userspace-cni-network-plugin development by creating an account on GitHub.

github.com

 

위 문서에 있는 이미지들...

 

 

 

 

 

MISC

Red Hat이 공식 Container Image Registry를 통해서 아래와 같이 제공하는 container image도 있기는 한데, 라이센스 문제나 비용 문제 때문에 쓸수 없으니까, 그냥 내용 참고만 하고 나중에 돈이 충분하게 생기면 한번 image pulling해서 써봐야겠다.

 

https://catalog.redhat.com/software/containers/openshift4/dpdk-base-rhel8/5e32be6cdd19c77896004a41?container-tabs=dockerfile&gti-tabs=registry-tokens 

 

Red Hat Ecosystem Catalog

Your role Select your roleArchitectDeveloperDevOps EngineerProduct ManagerSystems AdministratorOther

catalog.redhat.com

 

+ Recent posts