KANS 스터디 3주차 - Calico CNI 2편

유형욱·2022년 2월 5일
0

KANS 스터디

목록 보기
5/9

KANS 스터디 3주차 - Calico CNI 2편

개요

지난 Calico CNI 1편에 이어서 작성된 내용이다. 앞서 작성한 1편에서는 Calico CNI의 기본 구조와 통신에 대한 이해를 중점적으로 다루었다.

2편에서는 Calico CNI에서 제공하는 다양한 네트워크 모드에 대해서 분석해보려 한다.


2. Calico 네트워크 모드

💡 요약

  • Calico 는 다양한 네트워크 통신 방법(모드)를 제공한다.
  • Calico 나 Cilium 에서 파드 혹은 네임스페이스의 레벨에서 IN/OUT 트래픽에 대한 통제가 가능하다.(Network Policy)

0) Calico Mode 요약

Clico에서는 다음과 같은 모드를 제공한다.

  • IPIP(기본)
  • Direct(권장)
  • BGP
  • VXLAN
  • Pod 패킷 암호화

📢 토론 & 발표 : Calico 를 사용하신다면, 어떤 모드기능을 사용하시는지 사례를 알려주세요!

1) IPIP

1) 통신 흐름

💡 요약 : 파드 간 통신이 노드와 노드 구간에서는 IPIP 인캡슐레이션을 통해서 이루어 진다. 1편 참고

  • 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신
  • 다른 노드의 파드 대역은 BGP로 전달 받아 호스트 라우팅 테이블에 업데이트됨(Bird->Felix->Confd)

2) Direct

성능이 뛰어남(약 20%정도)

1) 통신 흐름

💡 요약 : 파드 통신 패킷이 출발지 노드의 라우팅 정보를 보고 목적지 노드로 원본 패킷 그대로 전달한다.

사전 요구사항 및 참고사항

  • 클라우드 사업자 네트워크의 경우 NIC 에 매칭되지 않는 IP 패킷은 차단되니, NIC에 Source/Destination Check 기능을 Disable 해야 한다. - 링크
# AWS CLI 로 특정 인스턴스의 Source/Destination Check 기능을 Disable 하기
aws ec2 modify-instance-attribute --instance-id <INSTANCE_ID> --source-dest-check "{\"Value\": false}"

참고 : 리눅스의 ESB 모드

  • Virtualbox 설정 : 노드(VM)에 네트워크 NIC 2 번에 무작위 모드모두 허용 설정이 vagrant file 에 의해 되어 있음

2) Direct 모드 설정

Direct 모드 설정 및 확인

  • 모드 정보 확인 : IPIPMODE: Always 이므로 Never로 변경!
(calico-k8s:default) root@k8s-m:~# calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   DISABLEBGPEXPORT   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      false              all()

# (옵션) 모니터링
watch -d "route -n | egrep '(Destination|UG)'"

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.20.100  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
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 tunl0
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

# 설정
(calico-k8s:default) root@k8s-m:~# calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Always/ipipMode: Never/" | calicoctl apply -f -
Successfully applied 1 'IPPool' resource(s)
  • Iface가 tunl0에서 enp0s8로 바뀌는 모습

  • 변경 확인
# 모드 정보 확인 : IPIPMODE 가 Never 로 변경!
calicoctl get ippool -o wide
root@k8s-m:~/yaml# calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Never      Never       false      all()

(calico-k8s:default) root@k8s-m:~# route -n | egrep '(Destination|UG)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8
172.16.158.0    192.168.10.101  255.255.255.0   UG    0      0        0 enp0s8
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

# calico config 설정 변경 확인
(calico-k8s:default) root@k8s-m:~# calicoctl get ippool default-ipv4-ippool -o yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  creationTimestamp: "2022-02-05T02:27:50Z"
  name: default-ipv4-ippool
  resourceVersion: "43961"
  uid: b529fcb1-7e4c-4a16-b7c9-de6e37c7eea7
spec:
  allowedUses:
  - Workload
  - Tunnel
  blockSize: 24
  cidr: 172.16.0.0/16
  ipipMode: Never
  natOutgoing: true
  nodeSelector: all()
  vxlanMode: Never

결론적으로 터널 인터페이스 IP를 사용하지 않고 물리 인터페이스를 사용해서 통신. 즉, Pod IP를 사용해서 통신이 되는 것을 알 수 있다.

3) 동작 확인

파드생성

# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/5/node3-pod3.yaml
kubectl apply -f node3-pod3.yaml
pod/pod1 created
pod/pod2 created
pod/pod3 created

# 파드 IP 정보 확인
(calico-k8s:default) root@k8s-m:~# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          2m34s   172.16.158.5   k8s-w1   <none>           <none>
pod2   1/1     Running   0          2m34s   172.16.184.2   k8s-w2   <none>           <none>
pod3   1/1     Running   0          2m34s   172.16.34.1    k8s-w0   <none>           <none>

(calico-k8s:default) root@k8s-m:~# calicoctl get workloadEndpoint
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.5/32   calice0906292e2
pod2       k8s-w2   172.16.184.2/32   calibd2348b4f67
pod3       k8s-w0   172.16.34.1/32    cali49778cadcf1

파드간 ping 통신 실행 및 패킷 캡쳐 확인

  • pod1 shell에서 pod1~3 으로 ping 통신
 pod1  ~  ping -c1 172.16.158.5
PING 172.16.158.5 (172.16.158.5) 56(84) bytes of data.
64 bytes from 172.16.158.5: icmp_seq=1 ttl=64 time=0.032 ms

--- 172.16.158.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.032/0.032/0.032/0.000 ms

 pod1  ~  ping -c1 172.16.184.2
PING 172.16.184.2 (172.16.184.2) 56(84) bytes of data.
64 bytes from 172.16.184.2: icmp_seq=1 ttl=62 time=1.49 ms

--- 172.16.184.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.485/1.485/1.485/0.000 ms

 pod1  ~  ping -c1 172.16.34.1
PING 172.16.34.1 (172.16.34.1) 56(84) bytes of data.
^C
--- 172.16.34.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

❓ 워커노드1(파드) → 워커노드2(파드) or 워커노드0(파드) 와 통신 확인해보자! 되는 경우 어떻게 되는 걸까? 안되는 경우는 왜일까? ⇒ Overlay 네트워크 기법이 필요한 이유!
(힌트) k8s-w0 에서 route -n 확인 및 k8s-rtr 에서 tcpdump -i any icmp -nn 로 정보 확인해보자!

Pod1에서 Pod2, Pod3으로 통신할 경우 Pod2는 정상적으로 request/reply가 찍히지만, Pod3의 경우에는 request만 찍히는 모습

통신이 안된 이유를 분석해보자

k8s-w1의 라우팅 테이블을 확인해본 결과 pod3의 대역인 172.16.34.0으로 통신을 할 때 192.168.10.254 라고하는 Gateway를 통해서 나간다.

그럼 192.168.10.254대역은 무엇일까? 바로 k8s-rtr 이다.

  • k8s-rtr에 접속 후 ip 정보를 확인
root@k8s-rtr:~# ip -br -c -4 addr
lo               UNKNOWN        127.0.0.1/8
enp0s3           UP             10.0.2.15/24
enp0s8           UP             192.168.10.254/24
enp0s9           UP             192.168.20.254/24
loop1            UNKNOWN        10.1.1.254/24
loop2            UNKNOWN        10.1.2.254/24


k8s-w0에 배포된 Pod로 Ping을 한 결과 k8s-rtr에서 request가 들어오는 것을 알 수 있다.

  • 라우팅 테이블 정보 확인
root@k8s-rtr:~# ip -c route
default via 10.0.2.2 dev enp0s3 proto dhcp src 10.0.2.15 metric 100
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
10.0.2.2 dev enp0s3 proto dhcp scope link src 10.0.2.15 metric 100
10.1.1.0/24 dev loop1 proto kernel scope link src 10.1.1.254
10.1.2.0/24 dev loop2 proto kernel scope link src 10.1.2.254
192.168.10.0/24 dev enp0s8 proto kernel scope link src 192.168.10.254
192.168.20.0/24 dev enp0s9 proto kernel scope link src 192.168.20.254

w0, w1, w2 간에 통신할 때 rtr을 통하도록 되어있는데 Direct 모드로 되어있다 보니 캡슐레이션 되지 않고 172.x 대역으로 통신하다 보니 정상적으로 라우팅 해주지 못하는 것으로 확인된다.
이전 Overay Network 환경에서 가능했던 이유는 Linux Router(rtr)을 통하더라도 캡슐화되어 Worker Node 대역으로 들어오기 때문에 정상적으로 라우팅 된 것을 알 수 있다.

4) CrossSubnet 모드

❗ 동작 : 노드 간 같은 네트워크 대역(Direct 모드로 동작) , 노드 간 다른 네트워크 대역(IPIP 모드로 동작)

IPIPMode 변경

# CrossSubnet 모드 설정
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Never/ipipMode: CrossSubnet/" | calicoctl apply -f -

# 변경사항 Watch
watch -d "route -n | egrep '(Destination|UG)'"
  • 변경 전/후 비교

(변경 전)
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8
172.16.158.0    192.168.10.101  255.255.255.0   UG    0      0        0 enp0s8
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

(변경 후)
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.20.100  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 enp0s8
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

k8s-m와 네트워크 대역이 다른 172.16.34.0 대역이 할당된 노드만 tunl0로 변경된 것을 확인할 수 있다.

물론 k8s-w0은 다른 노드들과 대역이 다르기 때문에 다음과 같이 라우팅 테이블이 모두 tunl0이다.

root@k8s-w0:~# ip -c route | grep bird
blackhole 172.16.34.0/24 proto bird
172.16.116.0/24 via 192.168.10.10 dev tunl0 proto bird onlink
172.16.158.0/24 via 192.168.10.101 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink

결론 : 결론적으로 CrossSubnet 모드가 가장 효율적(?)이라고 볼 수 있다.

3) BGP (생략) -> 추후 재작성 예정

요약 : K8S 클러스터 내부 네트워크와 IDC 내부망 네트워크간 직접 라우팅이 가능합니다!

K8S 클러스터 네트워크 대역과 IDC 내부망 네트워크 대역간 직접 통신 ↔ BGP 로 네트워크 대역을 전파

참고 : IDC망을 관리하는 네트워크 팀과 협조하여 K8S 클러스터와의 통신 간 효율적인 네트워크 환경을 구성하는 것을 권장

1) 네트워크 기본 정보

IDC(내부망)과 K8S 클러스터 사용 네트워크 대역

  • IDC 호스트 : 아래 빨간색은 회사 내부 네트워크 대역

2) IDC 호스트

3) K8S Calico 확인 및 파드/서비스 생성

4) K8S Calico BGP 구성 및 파드와 통신 설정

5) IDC 내부망 ↔ K8S 파트 네트워크 통신

6) 별도 IP Pool 생성 제공 ← IP 대역 관리를 편하게 할 수 있다!

4) VXLAN(생략)

  • Calico에서는 VXLAN 모드 사용시 BGP 사용 불가!
  • BGP를 사용하기 위해서는 IPIP모드 또는 Direct 모드 사용
    • Network 장비와 연동을 위해서 BGP 사용하는 경우

5) Pod 패킷 암호화

WireGuard 기능등을 사용하여 쉽고 간편하게 암호화 통신 제공가능

(1) 통신 흐름

요약 : Calico 의 다양한 네크워크 모드 환경 위에서 WireGuard 터널을 자동 생성 및 파드 트래픽을 암호화하여 노드간 전달합니다

📌 참고 : WireGuard 소개 글

(2) WireGuard 설정 - 링크

노드에 WireGuard 설치 - 링크 ← 이미 설치되어 있음

# 설치
apt install wireguard -y

# WireGuard 버전 확인
wg version
root@k8s-m:~/yaml# wg version
wireguard-tools v1.0.20200513 - https://git.zx2c4.com/wireguard-tools/

WireGuard 설정 및 확인

  • WireGuard 설정 및 확인
# 설정
(calico-k8s:default) root@k8s-m:~# calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'
Successfully patched 1 'FelixConfiguration' resource

# 확인
(calico-k8s:default) root@k8s-m:~# calicoctl get felixconfiguration default -o yaml | grep wireguardEnabled
  wireguardEnabled: true

# wireguard.cali 인터페이스 확인
(calico-k8s:default) root@k8s-m:~# calicoctl get node -o yaml | grep wireguardPublicKey
    wireguardPublicKey: Zpr9B1xt/vNfqVDd7+khL/Sgp/UgPax9cRLCviIw23Q=
    wireguardPublicKey: JRfFa5cs0u1OmPEzTqZlqgZyNpVeS1r3g3jOG7JAkT4=
    wireguardPublicKey: TIRRGkoyyhtAOWl7sDGVhCBTRW8tDGoTEELuDgsRCFk=
    wireguardPublicKey: CazuOk0lhm8DB4CaRKwPNeyVQvs6hSb6jtB9z0jhERA=
    
(calico-k8s:default) root@k8s-m:~# ifconfig wireguard.cali
wireguard.cali: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1440
        inet 172.16.116.5  netmask 255.255.255.255  destination 172.16.116.5
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        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
  • wireguard.cali 설정 확인 : 통신 포트, Peer/Endpoint 정보, 패킷 암호화를 위한 공개키/사설키 정보
(calico-k8s:default) root@k8s-m:~# wg showconf wireguard.cali
[Interface]
ListenPort = 51820
FwMark = 0x100000
PrivateKey = kLTb72iPcf2oCEV+Unj8hfV/cwi718O/ErNPvbW2g1Q=

[Peer]
PublicKey = TIRRGkoyyhtAOWl7sDGVhCBTRW8tDGoTEELuDgsRCFk=
AllowedIPs = 172.16.158.6/32, 172.16.158.7/32, 172.16.158.0/24
Endpoint = 192.168.10.101:51820

[Peer]
PublicKey = CazuOk0lhm8DB4CaRKwPNeyVQvs6hSb6jtB9z0jhERA=
AllowedIPs = 172.16.184.0/24, 172.16.184.3/32, 172.16.184.4/32
Endpoint = 192.168.10.102:51820

[Peer]
PublicKey = JRfFa5cs0u1OmPEzTqZlqgZyNpVeS1r3g3jOG7JAkT4=
AllowedIPs = 172.16.34.0/24, 172.16.34.2/32, 172.16.34.3/32
Endpoint = 192.168.20.100:51820

# Peer 의 정보(고유한 index)
(calico-k8s:default) root@k8s-m:~# wg show
interface: wireguard.cali
  public key: Zpr9B1xt/vNfqVDd7+khL/Sgp/UgPax9cRLCviIw23Q=
  private key: (hidden)
  listening port: 51820
  fwmark: 0x100000

peer: TIRRGkoyyhtAOWl7sDGVhCBTRW8tDGoTEELuDgsRCFk=
  endpoint: 192.168.10.101:51820
  allowed ips: 172.16.158.6/32, 172.16.158.7/32, 172.16.158.0/24

peer: CazuOk0lhm8DB4CaRKwPNeyVQvs6hSb6jtB9z0jhERA=
  endpoint: 192.168.10.102:51820
  allowed ips: 172.16.184.0/24, 172.16.184.3/32, 172.16.184.4/32

peer: JRfFa5cs0u1OmPEzTqZlqgZyNpVeS1r3g3jOG7JAkT4=
  endpoint: 192.168.20.100:51820
  allowed ips: 172.16.34.0/24, 172.16.34.2/32, 172.16.34.3/32
  

(3) 동작 확인

파드생성 및 확인

(calico-k8s:default) root@k8s-m:~# kubectl apply -f node3-pod3.yaml
pod/pod1 created
pod/pod2 created
pod/pod3 created

(calico-k8s:default) root@k8s-m:~# calicoctl get workloadEndpoint
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.8/32   calice0906292e2
pod2       k8s-w2   172.16.184.5/32   calibd2348b4f67
pod3       k8s-w0   172.16.34.4/32    cali49778cadcf1

파드간 ping 통신 실행 및 패킷 캡쳐 확인

# pod1에서 pod2로 ping
 pod1  ~  ping 172.16.184.5
PING 172.16.184.5 (172.16.184.5) 56(84) bytes of data.
64 bytes from 172.16.184.5: icmp_seq=1 ttl=62 time=3.22 ms
64 bytes from 172.16.184.5: icmp_seq=2 ttl=62 time=1.52 ms
64 bytes from 172.16.184.5: icmp_seq=3 ttl=62 time=2.39 ms
^C
--- 172.16.184.5 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 1.521/2.377/3.221/0.694 ms

# w1에서 tcpdump
root@k8s-w1:~# tcpdump -i enp0s8 -nn udp port 51820
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
12:36:51.476347 IP 192.168.10.101.51820 > 192.168.10.102.51820: UDP, length 128
12:36:51.478762 IP 192.168.10.102.51820 > 192.168.10.101.51820: UDP, length 128

  • WireShark를 통한 패킷분석
root@k8s-w1:~# tcpdump -i enp0s8 udp port 51820 -w /tmp/calico-wireguard.pcap
tcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
^C12 packets captured
12 packets received by filter
0 packets dropped by kernel

패킷캡쳐한 정보를 직접 WireShark에서 분석해보니 Protocol: WireGuard으로 되어 있어있으며, 실제 Inner 헤더에 들어가 있어야할 정보가 Encrypted Packet으로 처리된 것을 확인할 수 있다.

6) Calico 네트워크 접근 통제

네트워크 접근 통제(Network Policy) 기능은 Cilium 스터디 시간에 추가 작성 예정.

네트워크 정책 소개

네트워크 정책(Network Policy)은 쿠버네티스 클러스터 내부에서 파드 간에 통신할 경우 트래픽 룰을 규정하는 것이다.
네트워크 정책을 사용하지 않을 경우 클러스터 내부의 모든 파드는 서로 통신이 가능하다.
그러나 네트워크 정책을 사용할 수 있다면, 네임스페이스별로 트래픽을 전송하지 못하게 하거나
기본적으로 모든 파드 간 통신을 차단하고 특정 파드 간 통신만 허용하는 화이트리스트 방식을 사용할 수 있다.
또한 CNI(Calico, Cilium 등)에서 네트워크 정책을 지원해야 한다.

네트워크 정책 실습

(심화 정보) Calico 자체 제공 추가 기능 Netwok Policy


이렇게 3주차 스터디 내용에 대한 복습이 끝이났다.
본 스터디를 신청할 때 가장 궁금했고 필요했던 내용이었는데... 생각보다 훨씬 어렵고 공부해야 할 내용이 많았다.
핑계지만 투입된 프로젝트 마무리 기간 + 설 연휴가 겹쳐서 복습을 하지못했는데... 다가오는 4주차 스터디이후에 꼭 다시한번 3주차 내용을 복습해야 할 것 같다.

그래도 지난 1달 사이에 많은 것을 느끼고 배울 수 있는 시간이었던 것 같다.
마지막 8주차까지 런🔥

profile
DevOps를 꿈꾸는 엔지니어 입니다.

1개의 댓글

comment-user-thumbnail
2022년 2월 5일

스터디 내용을 꾸준히 정리하고 계시네요.
멋집니다!
남은 5주도 잘 부탁드립니다 :)

답글 달기