[KANS3] Calico CNI & Mode

xgro·2024년 9월 18일
0

KANS3

목록 보기
3/9
post-thumbnail

📌 Notice

Kubernetes Advanced Networking Study (=KANS)
k8s 네트워크 장애 시, 네트워크 상세 동작 원리를 기반으로 원인을 찾고 해결하는 내용을 정리한 블로그입니다.

CloudNetaStudy 그룹에서 스터디를 진행하고 있습니다.

Gasida님께 다시한번 🙇 감사드립니다.

EKS 관련 이전 스터디 내용은 아래 링크를 통해 확인할 수 있습니다.

이번 주에는 Kubernetes 네트워킹의 핵심인 Calico를 깊이 있게 탐구했습니다.

스터디를 거듭하면서, 하나씩 지식을 쌓으면서 새로운것을 배워나가는것이 힘들긴 하지만 굉장한 보람을 느끼게 해주는것 같습니다.



📌 Summary

  • Calico는 Kubernetes 클러스터를 위한 강력한 네트워크 솔루션으로, 다양한 네트워크 모드와 기능을 제공합니다.

  • 주요 모드로는 Direct, IPIP, VXLAN, 그리고 WireGuard를 이용한 암호화 모드가 있습니다.

  • Calico는 BGP를 통한 효율적인 라우팅, 네트워크 정책을 통한 세밀한 접근 제어를 지원합니다.

  • Prometheus와 Grafana를 활용하여 Calico 컴포넌트의 메트릭을 모니터링하고 시각화할 수 있습니다.



📌 Study

👉 Step 00. 사전 준비

Calico 설치

# 모니터링
watch -d 'kubectl get pod -A -owide'

# calico cni install
## kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml - 서브넷 24bit 추가
# 기본 yaml 에 4946줄 이동 후 아래 내용 추가 해둠
vi calico.yaml
...
            # Block size to use for the IPv4 POOL created at startup. Block size for IPv4 should be in the range 20-32. default 24
            - name: CALICO_IPV4POOL_BLOCK_SIZE
              value: "24"
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/kans3/calico-kans.yaml

#
tree /opt/cni/bin/
ls -l /opt/cni/bin/
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

Calicoctl 설치

# calicoctl install
curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin
calicoctl version

metric-server 설치

# metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system

kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
kubectl get apiservices |egrep '(AVAILABLE|metrics)'

# 확인
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'

# (참고) 삭제
helm uninstall -n kube-system metrics-server



👉 Step 01. Calico 기본 통신 이해

이번 단계에서는 Calico가 Kubernetes 클러스터 내에서 어떻게 기본적인 네트워크 통신을 처리하는지 알아보겠습니다.

이를 위해 Calico CNI 플러그인의 주요 구성 요소와 동작 방식을 살펴보겠습니다.


✅ 01. Calico CNI 알아보기

Calico는 Kubernetes 플랫폼에서 널리 사용되는 Container Network Interface(CNI) 플러그인입니다. Calico는 워크로드 간의 네트워크 통신과 네트워크 보안 기능을 제공합니다.

Calico의 주요 구성 요소

  • calicoctl: Calico 오브젝트를 관리하는 명령줄 도구

  • calico-node: 각 노드에서 데몬셋으로 실행되는 핵심 컴포넌트

  • Felix: 호스트 엔드포인트 관리 및 라우팅 테이블 업데이트 담당

  • BIRD: BGP 라우팅 데몬으로, 파드 네트워크 대역을 광고

  • confd: Calico 설정 변경 관리 도구

구성 요소 확인

# 버전 확인 - 링크
## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version

# calico 관련 정보 확인
kubectl get daemonset -n kube-system
kubectl get pod -n kube-system -l k8s-app=calico-node -owide

kubectl get deploy -n kube-system calico-kube-controllers
kubectl get pod -n kube-system -l k8s-app=calico-kube-controllers -owide

Calico IPAM(IP Address Management)의 상태 확인, 노드별 IP 블록 할당 정보, 그리고 strictAffinity 설정에 따른 IP 할당 정책을 확인

# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인 - 링크
calicoctl ipam show

# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration
## strictAffinity True 설정 시 : 각 노드는 할당된 IP 블록에서만 IP 주소를 사용, 특정 pod들에 해당해둔 pod ip/mac을 고정할경우, 해당 대역들을 무작위로 배치하는 IPAM 대역에서 제외 -> 이로써 중복으로 같은 IP 할당 방지
## strictAffinity False 설정 시 : 노드가 부족한 경우 다른 노드에서 IP를 공유 가능
## When StrictAffinity is true, borrowing IP addresses is not allowed - https://docs.tigera.io/calico/latest/reference/resources/ipamconfig
## 관련 코드
### https://github.com/projectcalico/libcalico-go/blob/v3.9.0-0.dev/lib/ipam/ipam.go
### https://github.com/projectcalico/libcalico-go/blob/v3.9.0-0.dev/lib/ipam/ipam_types.go#L78

Kubernetes 노드별 podCIDR 할당 정보 확인, CNI 플러그인 구성 확인, 그리고 Calico의 데이터 저장소 및 IPAM 설정을 확인

# host-local IPAM 정보 확인 : k8s-m 노드의 podCIDR 은 host-local 대신 칼리코 IPAM 를 사용함
## 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
kubectl get node k8s-m -o json | jq '.spec.podCIDR'

# CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist | jq
...
			"datastore_type": "kubernetes", # 칼리코 데이터저장소는 쿠버네티스 API 를 사용
      "ipam": { 
          "type": "calico-ipam" # IPAM 은 칼리코 자체 IPAM 을 사용
      },
...

Calico의 노드 상태, IP 풀 정보, 그리고 Kubernetes 클러스터의 네트워크 대역 정보를 확인합니다.

# calicoctl node 정보 확인 : Bird 데몬(BGP)을 통한 BGP 네이버 연결 정보(bgp peer 는 노드의 IP로 연결) - 링크
calicoctl node status
calicoctl node checksystem

# ippool 정보 확인 : 클러스터가 사용하는 IP 대역 정보와 칼리코 모드 정보 확인
calicoctl get ippool -o wide

# 파드와 서비스 사용 네트워크 대역 정보 확인 
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
        "--service-cluster-ip-range=10.200.1.0/24",
        "--cluster-cidr=172.16.0.0/16",

kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet
 podSubnet: 172.16.0.0/16
 serviceSubnet: 10.96.0.0/12


✅ 02. 파드(Pod) ↔ 파드간 통신

Calico에서 동일 노드 내 파드 간 통신은 다음과 같이 이루어집니다.


출처 - CloudNet@

네트워크 구성

  • 각 파드는 고유한 네트워크 네임스페이스를 가집니다.
  • 파드의 eth0 인터페이스는 호스트의 veth (Virtual Ethernet) 인터페이스와 페어로 연결됩니다.
  • veth 인터페이스는 호스트의 root 네트워크 네임스페이스에 존재합니다.
  • Calico는 각 파드에 /32 CIDR (단일 IP 주소)를 할당합니다.

통신 과정

  1. 출발지 파드에서 패킷 전송:
    파드는 자신의 eth0 인터페이스를 통해 패킷을 전송합니다.
    패킷은 페어를 이루는 veth 인터페이스로 전달됩니다.
  1. 호스트에서의 패킷 처리:
    호스트의 커널은 수신한 패킷을 처리합니다.
    Calico가 설정한 라우팅 규칙과 iptables 규칙에 따라 패킷을 전달합니다.
  1. 목적지 파드로 패킷 전달:
    패킷은 목적지 파드와 연결된 veth 인터페이스로 전달됩니다.
    최종적으로 목적지 파드의 eth0 인터페이스로 패킷이 전달됩니다.

파드 생성 전 노드(k8s-w1) Shell 에서 기본 정보 확인

# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
    ipip any remote any local any ttl inherit nopmtudisc numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.158.0/32 scope global tunl0
       valid_lft forever preferred_lft forever

# 네트워크 네임스페이스 확인
lsns -t net

# 네트워크 라우팅 경로 정보 확인
# 이중 bird 는 bird 데몬이 BGP 라우팅 프로토콜에 의해 파드 네트워크 대역을 전달받거나 전달하는 경로 → 각각 노드의 파드 대역입니다
Quiz. blackhole 라우팅은 왜 있을까요? 
ip -c route | grep bird
blackhole 172.16.158.0/24 proto bird
172.16.34.0/24 via 192.168.20.100 dev tunl0 proto bird onlink
172.16.116.0/24 via 192.168.10.10 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink

# 아래 tunl0 Iface 에 목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서 각 노드에 전달됩니다 → 각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.34.0     192.168.20.100  255.255.255.0   UG    0      0        0 tunl0
172.16.116.0    192.168.10.10   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
172.16.158.0    0.0.0.0         255.255.255.0   U     0      0        0 *
...

# (옵션) iptables rule 갯수 확인 >> iptables rule 이 모든 노드에서 똑같나요? 다른가요?
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l

파드 배포 후 상태 확인

파드를 배포하며, workloadEndpoint가 어떻게 관리되는지 확인합니다.

# [터미널1] k8s-m 모니터링
watch -d calicoctl get workloadEndpoint

# [터미널2] k8s-m 모니터링
# 파드 생성 : 노드의 이름이 다를 경우 아래 yaml 파일 다운로드 후 수정해서 사용하시면 됩니다
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml

# 생성된 파드 정보 확인
kubectl get pod -o wide
root@k8s-m:~# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          34s   172.16.228.78   k8s-w1   <none>           <none>
pod2   1/1     Running   0          34s   172.16.228.79   k8s-w1   <none>           <none>

# calicoctl 이용한 endpoint 확인 : veth 정보도 출력!
calicoctl get workloadendpoints
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.2/32   calice0906292e2
pod2       k8s-w1   172.16.158.1/32   calibd2348b4f67

파드 내부에 접속하여 ip를 확인합니다.

# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh
...
# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
...
4: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether ee:2f:83:31:9b:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.158.2/32 brd 172.16.158.2 scope global eth0
       valid_lft forever preferred_lft forever

# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

# ARP 정보는 현재 아무것도 없다
ip -c neigh

✅ 03. 파드 → 외부(인터넷) 통신


출처 - CloudNet@

사전 준비

실습을 진행하기 전에 calico 설정 정보 확인 & 노드에 iptables 정보를 확인합니다.

# 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      all()

# 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
iptables -n -t nat --list cali-nat-outgoing
root@k8s-w1:~# iptables -n -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
target      prot opt source               destination
MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

ipset list
ipset list cali40masq-ipam-pools

출발지의 IP와 노드의 외부 IP로 NAT되어 외부 환경과 통신하는것을 확인합니다.

# 파드에서 외부 정상 통신 확인
kubectl exec pod1 -it -- zsh
----------------------------

# 혹은 통신 확인 
pod1> ping -c 10 8.8.8.8

# The right way to check the weather - 링크
curl wttr.in/seoul
curl 'wttr.in/seoul?format=3'
curl 'wttr.in/busan?format=3'
curl -s 'wttr.in/{London,Busan}'
curl v3.wttr.in/Seoul.sxl
curl wttr.in/Moon
curl wttr.in/:help

# 패킷 덤프 내용 확인
tcpdump -i <각자 실습 환경에 따라 다름> -nn icmp
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on calice0906292e2, link-type EN10MB (Ethernet), capture size 262144 bytes
09:27:29.236878 IP 172.16.228.82 > 8.8.8.8: ICMP echo request, id 56376, seq 1, length 64
09:27:29.311810 IP 8.8.8.8 > 172.16.228.82: ICMP echo reply, id 56376, seq 1, length 64


# [실습환경 A Type] 아래 192.168.10.101는 노드의 외부(인터넷) 연결된 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i ens5 -nn icmp
15:37:56.579286 IP 192.168.10.101 > 8.8.8.8: ICMP echo request, id 57122, seq 1, length 64
15:37:56.610585 IP 8.8.8.8 > 192.168.10.101: ICMP echo reply, id 57122, seq 1, length 64

# [실습환경 B Type] 아래 10.0.2.15는 VM의 1번 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i enp0s3 -nn icmp
06:05:12.356260 IP 10.0.2.15 > 8.8.8.8: ICMP echo request, id 15671, seq 5, length 64
06:05:12.402586 IP 8.8.8.8 > 10.0.2.15: ICMP echo reply, id 15671, seq 5, length 64


# nat MASQUERADE rule 카운트(pkts)가 증가!
## 출발지 매칭은 cali40masq-ipam-pools 을 사용
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
root@k8s-w1:~# iptables -n -v -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
 pkts bytes target     prot opt in     out     source               destination
    3   252 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

# IPSET 으로 의 cali40masq-ipam-pools IP 대역 정보 확인 : 172.16.0.0/16 대역임을 확인
ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x97754149
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16

✅ 04. 다른 노드에서 파드 ↔ 파드간 통신


출처 - CloudNet@

각 노드에 파드 네트워크 대역은 Bird에 의해서 BGP 로 광고 전파/전달 되며, Felix 에 의해서 호스트의 라우팅 테이블에 자동으로 추가 및 삭제 됩니다.

다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신됩니다.

파드 배포 전 기본 상태 확인

# 아래 명령어로 확인 시 나머지 노드들의 파드 대역을 자신의 호스트 라우팅 테이블에 가지고 있고, 해당 경로는 tunl0 인터페이스로 보내게 된다
route | head -2 ; route -n | grep tunl0

# 마스터노드, 노드1~
root@k8s-m:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0

# 노드1
root@k8s-w1:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.29.0     192.168.100.10  255.255.255.192 UG    0      0        0 tunl0
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
...

노드의 tunl0 정보 확인

# 터널 인터페이스에 IP가 할당되어 있고, MTU는 1480(IP헤더 20바이트 추가를 고려)을 사용하며, 현재 TX/RX 카운트는 0 이다
# Calico 사용 시 파드의 인터페이스도 기본 MTU 1480 을 사용한다
ifconfig tunl0

# 노드1
root@k8s-w1:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        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

# 노드2
root@k8s-w2:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.46.10  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        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

파드간 통신을 처리하는 라우팅 정보 확인 : 각각 상대방 노드의 IP를 게이트웨이로 전달하게 됩니다!

# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml

# calicoctl 이용한 endpoint 확인
calicoctl get workloadendpoints


route -n | head -2 ; route -n | grep 172.16.

# 노드1
root@k8s-w1:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
...

# 노드2
root@k8s-w2:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0
...

지금까지 tunl0 인터페이스가 사용되지 않았는데, 다른 노드의 파드간 통신시에 사용되는지 확인합니다.

# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 실행
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
root@k8s-w1:~# watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 0  bytes 0 (0.0 B)
        TX packets 0  bytes 0 (0.0 B)

# (옵션) 패킷 덤프 : calice#
tcpdump -i -nn <calice#>

# 패킷 덤프 : tunl0
tcpdump -i tunl0 -nn
혹은 패킷을 파일로 추출시
tcpdump -i tunl0 -nn -w /tmp/calico-tunl0.pcap

# 패킷 덤프 : IP 헤더에 상위 프로토콜을 IPIP(4)인 패킷만 필터
#tcpdump -i <각자 자신의 노드의 eth0> -nn proto 4
tcpdump -i ens5 -nn proto 4    # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4  # [실습환경 B Type]
혹은 패킷을 파일로 추출시
tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./

# 마스터 노드에서 pod1 Shell 접속
kubectl exec pod1 -it -- zsh

# pod1 에서 pod2 로 핑 통신 : 정상 통신!
ping -c 10 <pod2 IP>
pod1> ping -c 10 172.16.46.12
PING 172.16.46.12 (172.16.46.12) 56(84) bytes of data.
64 bytes from 172.16.46.12: icmp_seq=1 ttl=62 time=0.781 ms
64 bytes from 172.16.46.12: icmp_seq=2 ttl=62 time=0.803 ms
...

# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 확인 : TX/RX 패킷 카운트가 각각 10개로 증가했다
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)

# 패킷 덤프 : tunl0 - 터널 인터페이스에 파드간 IP 패킷 정보 확인!
tcpdump -i tunl0 -nn
root@k8s-w1:~# tcpdump -i tunl0 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tunl0, link-type RAW (Raw IP), capture size 262144 bytes
23:36:36.621795 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64
23:36:36.622470 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64
23:36:37.623274 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64
23:36:37.623977 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64
...

# 패킷 덤프 : eth0(enp#~) - IP Outer 헤더 안쪽에 IP 헤더 1개가 더 있음을 알 수 있다!
tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
root@k8s-w2:~# tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
root@k8s-w2:~# tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
23:36:36.625169 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:36.625413 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:37.626715 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64 (ipip-proto-4)
23:36:37.626866 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64 (ipip-proto-4)
...

tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]
ls -l /tmp


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./



👉 Step 02. Calico 네트워크 모드

Calico는 다양한 네트워크 모드를 제공하여 다양한 환경과 요구사항에 맞춰 쿠버네티스 클러스터의 네트워킹을 구성할 수 있습니다.

주요 모드는 다음과 같습니다

  1. Direct 모드

    • 가장 기본적이고 성능이 뛰어난 모드입니다.
    • 파드 간 통신 시 원본 패킷을 그대로 전달합니다.
    • BGP를 사용하여 라우팅 정보를 교환합니다.
    • 노드 간 직접 통신이 가능한 환경에서 최적의 성능을 제공합니다.
  2. IPIP 모드

    • IP-in-IP 캡슐화를 사용하는 오버레이 네트워크 모드입니다.
    • 원본 IP 패킷을 다른 IP 패킷으로 캡슐화합니다.
    • 노드 간 직접 라우팅이 불가능한 환경에서 유용합니다.
    • 'tunl0' 인터페이스를 사용하여 캡슐화된 패킷을 처리합니다.
  3. VXLAN 모드

    • VXLAN(Virtual Extensible LAN) 기술을 사용하는 오버레이 네트워크 모드입니다.
    • Layer 2 프레임을 UDP 패킷으로 캡슐화합니다.
    • 클라우드 환경이나 대규모 네트워크에서 유용합니다.
    • 'vxlan.calico' 인터페이스를 통해 VXLAN 트래픽을 처리합니다.
  4. 파드 패킷 암호화 (Wireguard)

    • 파드 간 통신을 암호화하여 보안을 강화하는 모드입니다.
    • Wireguard 프로토콜을 사용하여 엔드-투-엔드 암호화를 제공합니다.
    • 다른 모드(Direct, IPIP, VXLAN)와 함께 사용할 수 있습니다.
    • 'wireguard.cali' 인터페이스를 통해 암호화된 트래픽을 처리합니다.

각 모드의 특징

  • Direct 모드는 최고의 성능을 제공하지만 네트워크 구성에 제약이 있을 수 있습니다.

  • IPIP와 VXLAN 모드는 유연성이 높지만 약간의 성능 오버헤드가 있습니다.

  • 패킷 암호화는 보안을 강화하지만 추가적인 처리 비용이 발생합니다.

Calico의 다양한 네트워크 모드를 통해 사용자는 자신의 환경과 요구사항에 가장 적합한 네트워킹 솔루션을 구성할 수 있습니다.

✅ 01. IPIP 모드

파드 간 통신이 노드와 노드 구간에서는 IPIP 인캡슐레이션을 통해서 이루어 집니다.

다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신

다른 노드의 파드 대역은 BGP로 전달 받아 호스트 라우팅 테이블에 업데이트됩니다.

Azure 네트워크에서는 IPIP 통신이 불가능하여 IPIP 모드 대신 VXLAN 모드 사용해야 합니다.

멀티 캐스트, 브로드캐스트, IP-IP 캡슐화 패킷 및 GRE(일반 라우팅 캡슐화) 패킷은 Azure VNet 내에서 차단됩니다.


✅ 02. Direct 모드

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


출처 - CloudNet@

클라우드 사업자 네트워크의 경우 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}"

Direct 모드 설정을 진행합니다.

# Calico 모드 정보 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      all()

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

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

# 모드 정보 확인 : 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()

# BGP 로 전달 받은 파드 네트워크 대역이 호스트 라우팅 테이블에 적용되었는지 확인 : Iface 가 tunl0 에서 ens5 혹은 enp0s8 로 변경!
route -n | egrep '(Destination|UG)'
root@k8s-w1:~# route -n | egrep '(Destination|UG)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.29.0     192.168.100.10  255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8

파드를 생성합니다.

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

# 파드 IP 정보 확인
kubectl get pod -o wide
calicoctl get wep

파드의 IP가 직접 사용되는것을 확인합니다.

# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i <eth0> -nn icmp
tcpdump -i ens5 -nn icmp   # [실습환경 A Type]
tcpdump -i enp0s8 -nn icmp # [실습환경 B Type]

# 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> icmp -w /tmp/calico-direct.pcap
tcpdump -i ens5 icmp -w /tmp/calico-direct.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 icmp -w /tmp/calico-direct.pcap # [실습환경 B Type]

워커 노드 0으로 ping 실행시 IP대역이 다르므로, 통신을 받지 못하는것을 확인

# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i <eth0> -nn icmp
tcpdump -i ens5 -nn icmp   # [실습환경 A Type]
tcpdump -i enp0s8 -nn icmp # [실습환경 B Type]

# 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> icmp -w /tmp/calico-direct.pcap
tcpdump -i ens5 icmp -w /tmp/calico-direct.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 icmp -w /tmp/calico-direct.pcap # [실습환경 B Type]

worker node 0은 서브넷이 다름!


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

# CrossSubnet 모드 설정
calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode":"CrossSubnet"}}'

# 모드 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE      VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   CrossSubnet   Never       false      all()

# 파드 생성
kubectl apply -f node3-pod3.yaml
calicoctl get wep

# 호스트 라우팅 정보 확인
route -n | grep UG
root@ip-172-20-63-146:~# route -n | grep UG
100.105.79.128  172.20.61.184   255.255.255.192 UG    0      0        0 ens5  # 노드간 같은 네트워크 대역 - Direct 모드
100.125.78.64   172.20.59.153   255.255.255.192 UG    0      0        0 ens5  # 노드간 같은 네트워크 대역 - Direct 모드
100.127.64.128  172.20.64.181   255.255.255.192 UG    0      0        0 tunl0 # 노드간 다른 네트워크 대역 - IPIP 모드

# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

내부에서는 Direct, 외부로는 tunl0이 설정되는것을 확인할 수 있습니다.


✅ 03. BGP 연동

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


출처 - CloudNet@

IDC망을 관리하는 네트워크 팀과 협조하여 K8S 클러스터와의 통신 간 효율적인 네트워크 환경을 구성하시길 권장합니다.


✅ 04. VXLAN 모드

파드 간 통신이 노드와 노드 구간에서는 VXLAN 인캡슐레이션을 통해서 이루어 집니다.


출처 - CloudNet@

다른 노드 간의 파드 통신은 vxlan 인터페이스를 통해 L2 프레임이 UDP - VXLAN에 감싸져서 상대측 노드로 도달 후 vxlan 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신

BGP 미사용, VXLAN L3 라우팅을 통해서 동작

Azure 네트워크에서도 사용 가능 🙆🏻 (UDP를 사용하므로)


✅ 05. Pod 패킷 암호화

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

WireGuard 소개

WireGuard는 구닥다리 IPsec 및 OpenVPN의 대항마로 등장한 open source VPN project 이며 작년, Linux 5.6 커널에 WireGuard 1.0.0 기본 패키지로 탑재되었다.

정말 간결한 코드 구조와 빠른 성능 (모든 것이 kernel에서 동작하고, 주요 암호 알고리즘에 대해서 병렬처리하므로써 빠른 속도를 자랑함)

WireGuard는 보시다시피 경쟁자들인 IPsec, SoftEther VPN, OpenVPN 등과 비교할 때, 코드 size(Line of Codes)가 현격하게 작다.

코드량이 많지 않으니 그만큼 철저히 검증될 가능성이 높고, 예상치 못한 곳에서의 버그로 인한 취약점이 발생할 가능성이 상대적으로 적다는 얘기가 된다.

WireGuard 설정 및 확인합니다.

# 설정
calicoctl get felixconfiguration -o yaml
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'

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

calicoctl get node -o yaml | grep wireguardPublicKey
calicoctl get node <노드 Name> -o yaml | grep wireguardPublicKey
root@k8s-m:~/yaml# calicoctl get node k8s-w1 -o yaml | grep wireguardPublicKey
  wireguardPublicKey: BToK9bLEhMaPUJsuKy3KdrxVOpklyo0qlGRdMN6lHWc=

# wireguard.cali 인터페이스 확인
ip -c -d addr show wireguard.cali
root@k8s-w1:~# ip -c -d addr show wireguard.cali
12: wireguard.cali: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none  promiscuity 0 minmtu 0 maxmtu 2147483552
    wireguard numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.228.74/32 scope global wireguard.cali
       valid_lft forever preferred_lft forever

ifconfig wireguard.cali
root@k8s-w1:~# ifconfig wireguard.cali
wireguard.cali: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1440
        inet 172.16.228.69  netmask 255.255.255.255  destination 172.16.228.69
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)

사설키 등 와이어 가드 정보를 확인합니다.

# wireguard.cali 설정 확인 : 통신 포트, Peer/Endpoint 정보, 패킷 암호화를 위한 공개키/사설키 정보
wg showconf wireguard.cali
root@k8s-w1:~# wg showconf wireguard.cali
[Interface]
ListenPort = 51820
FwMark = 0x100000
PrivateKey = AIgTihI2p4icwVMR4sIvuVaSqwKlkxMImQp4A/Gm+Gg=

[Peer]
PublicKey = BToK9bLEhMaPUJsuKy3KdrxVOpklyo0qlGRdMN6lHWc=
AllowedIPs = 172.16.228.64/26, 172.16.228.69/32, 172.16.228.67/32
Endpoint = 192.168.100.101:51820

[Peer]
PublicKey = 9TCD8hG6SLutZSOZSzQeqj6O0icJAxA3RPIipcBKBxs=
AllowedIPs = 172.16.197.0/26, 172.16.197.3/32, 172.16.197.5/32
Endpoint = 192.168.100.103:51820

[Peer]
PublicKey = Ercb/0pNZ+I1ELOkiXlWbZA9J0Fjt7XqsstDH4GhNmI=
AllowedIPs = 172.16.46.3/32, 172.16.46.0/26, 172.16.46.5/32
Endpoint = 192.168.100.102:51820

# Peer 의 정보(고유한 index)
wg show
interface: wireguard.cali
  public key: 8TNaYyzzc1N4SHfqE+Y5b4rMBKX/lqPe0tWO/h8sOB4=
  private key: (hidden)
  listening port: 51820
  fwmark: 0x100000

peer: 6WtZqEKSmoSKiFp20fhk/jyTcrTqf9qshyZI1HvE9Qk=
  endpoint: 192.168.10.102:51820
  allowed ips: 172.16.184.0/32, 172.16.184.0/24, 172.16.184.1/32

peer: +fOEOJgFxueIbrp709iB4F4gFRb2ny4lWKbxCNNfczM=
  endpoint: 192.168.20.100:51820
  allowed ips: 172.16.34.0/32, 172.16.34.0/24, 172.16.34.1/32

peer: d2LgXvRo4DwsyhiLXUn9TEt6D3l4pFIVlCD7KESR/m0=
  endpoint: 192.168.10.101:51820
  allowed ips: 172.16.158.0/32, 172.16.158.0/24, 172.16.158.1/32

# 키 정보 확인
wg show all public-key
wireguard.cali	8TNaYyzzc1N4SHfqE+Y5b4rMBKX/lqPe0tWO/h8sOB4=

wg show all private-key
wireguard.cali	kJbrfATGFP2v4sl+Wqg1Gv8zwFpIXshYFFD3udMDd3k=

wg show all preshared-keys
wireguard.cali	6WtZqEKSmoSKiFp20fhk/jyTcrTqf9qshyZI1HvE9Qk=	(none)
wireguard.cali	+fOEOJgFxueIbrp709iB4F4gFRb2ny4lWKbxCNNfczM=	(none)
wireguard.cali	d2LgXvRo4DwsyhiLXUn9TEt6D3l4pFIVlCD7KESR/m0=	(none)

# 그외 키타 정보
wg show all dump
wg show all endpoints
wg show all peers
wg show all transfer
wg show all persistent-keepalive


✅ 06. Calico 네트워크 접근 통제

네트워크 정책(Network Policy)

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


출처 - CloudNet@

네트워크 정책 구성

네트워크 정책은 인그레스(Ingress 수신)과 이그레스(Egrss 송신)로 구성되어 있다.
인그레스는 인바운드 방향의 트래픽 룰을 설정하고, 이그레스는 아웃바운드 방향의 트래픽 룰을 설정한다.
설정 범위를 podSelector 로 지정한다. 네트워크 정책은 네임스페이스별로 생성해야 한다.

네트워크 정책 예시
sample-networkpolicy.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: sample-networkpolicy
  namespace: default # 보안 정책을 생성할 네임스페이스 지정
spec:
  podSelector:
    # 설정할 대상 파드를 여기에 기입
    # 레이블 셀렉터이므로 복수의 파드를 대상으로 할 수 있음.
  policyTypes:
  - Ingress # 인그레이스 룰을 생성하는 경우 명시
  - Egress # 이그레스 룰을 생성할 경우 명시
  ingress:
  - from:
      # 인그레스 룰을 여기에 기입(이그레스 룰과 형식은 동일)
    ports:
      # 이 인그레스 룰로 허가할 수신 포트 번호와 프로토콜 기입
  egress:
  - to:
      # 이그레스 룰을 여기에 기입(인그레스 룰과 형식은 동일)
    ports:
      # 이 이그레스 룰로 허가할 송신 포트 번호와 프로토콜 기입



👉 Step 03. 모니터링

✅ 01. Monitor Calico component metrics - Docs

Use Prometheus configured for Calico components to get valuable metrics about the health of Calico.

For Calico, the “jobs” that Prometheus can harvest metrics from are the Felix and Typha

You can configure Felix, Typha, and/or kube-controllers to provide metrics to Prometheus.

Configure Calico to enable metrics reporting

# Felix configuration
calicoctl get felixconfiguration -o yaml
calicoctl patch felixconfiguration default  --patch '{"spec":{"prometheusMetricsEnabled": true}}'

# Creating a service to expose Felix metrics : Felix by default uses port 9091 TCP to publish its metrics.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: felix-metrics-svc
  namespace: kube-system
spec:
  clusterIP: None
  selector:
    k8s-app: calico-node
  ports:
  - port: 9091
    targetPort: 9091
EOF
kubectl get svc,ep -n kube-system felix-metrics-svc

# kube-controllers configuration : Prometheus metrics are enabled by default on TCP port 9094 for calico-kube-controllers
## Creating a service to expose kube-controllers metrics
calicoctl get kubecontrollersconfiguration default -o yaml
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: kube-controllers-metrics-svc
  namespace: kube-system
spec:
  clusterIP: None
  selector:
    k8s-app: calico-kube-controllers
  ports:
  - port: 9094
    targetPort: 9094
EOF
kubectl get svc,ep -n kube-system kube-controllers-metrics-svc

Cluster preparation

# Namespace creation
kubectl create -f -<<EOF
apiVersion: v1
kind: Namespace
metadata:
  name: calico-monitoring
  labels:
    app:  ns-calico-monitoring
    role: monitoring
EOF
kubectl get ns

# Service account creation
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: calico-prometheus-user
rules:
- apiGroups: [""]
  resources:
  - endpoints
  - services
  - pods
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: calico-prometheus-user
  namespace: calico-monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: calico-prometheus-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: calico-prometheus-user
subjects:
- kind: ServiceAccount
  name: calico-prometheus-user
  namespace: calico-monitoring
EOF
kubectl get sa -n calico-monitoring

Install prometheus

#
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: calico-monitoring
data:
  prometheus.yml: |-
    global:
      scrape_interval:   15s
      external_labels:
        monitor: 'tutorial-monitor'
    scrape_configs:
    - job_name: 'prometheus'
      scrape_interval: 5s
      static_configs:
      - targets: ['localhost:9090']
    - job_name: 'felix_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: felix-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'felix_windows_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: felix-windows-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'typha_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: typha-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'kube_controllers_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: kube-controllers-metrics-svc
        replacement: $1
        action: keep
EOF
kubectl get cm -n calico-monitoring prometheus-config

# Create Prometheus pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: prometheus-pod
  namespace: calico-monitoring
  labels:
    app: prometheus-pod
    role: monitoring
spec:
  nodeSelector:
    kubernetes.io/os: linux
  serviceAccountName: calico-prometheus-user
  containers:
  - name: prometheus-pod
    image: prom/prometheus
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    volumeMounts:
    - name: config-volume
      mountPath: /etc/prometheus/prometheus.yml
      subPath: prometheus.yml
    ports:
    - containerPort: 9090
  volumes:
  - name: config-volume
    configMap:
      name: prometheus-config
EOF
kubectl get pods prometheus-pod -n calico-monitoring -owide

View metrics

# 파드 IP 확인
kubectl get pods prometheus-pod -n calico-monitoring -owide

# 파드 IP metrics 엔드포인트 curl 접속 확인
curl <파드 IP>:9090/metrics
curl 172.16.34.7:9090/metrics

#
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: prometheus-dashboard-svc
  namespace: calico-monitoring
spec:
  type: NodePort
  selector:
    app: prometheus-pod
    role: monitoring
  ports:
    - protocol: TCP
      port: 9090
      targetPort: 9090
      nodePort: 30001 
EOF
kubectl get svc,ep -n calico-monitoring

# 프로메테우스 접속 주소
echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):30001"  # [실습환경 A Type]
echo -e "Prometheus URL = http://192.168.10.10:30001"            # [실습환경 B Type]

위의 모든 구성이 완료되면, 프로메테우스에서 구성을 확인할 수 있습니다.


✅ 02. Visualizing metrics via Grafana

Use Grafana dashboard to view Calico component metrics.

# Provisioning datasource
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-config
  namespace: calico-monitoring
data:
  prometheus.yaml: |-
    {
        "apiVersion": 1,
        "datasources": [
            {
               "access":"proxy",
                "editable": true,
                "name": "calico-demo-prometheus",
                "orgId": 1,
                "type": "prometheus",
                "url": "http://prometheus-dashboard-svc.calico-monitoring.svc:9090",
                "version": 1
            }
        ]
    }
EOF
kubectl get cm -n calico-monitoring

# Provisioning Calico dashboards : Here you will create a configmap with Felix and Typha dashboards.
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/grafana-dashboards.yaml

# Creating Grafana pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: grafana-pod
  namespace: calico-monitoring
  labels:
    app:  grafana-pod
    role: monitoring
spec:
  nodeSelector:
    kubernetes.io/os: linux
  containers:
  - name: grafana-pod
    image: grafana/grafana:latest
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    volumeMounts:
    - name: grafana-config-volume
      mountPath: /etc/grafana/provisioning/datasources
    - name: grafana-dashboards-volume
      mountPath: /etc/grafana/provisioning/dashboards
    - name: grafana-storage-volume
      mountPath: /var/lib/grafana
    ports:
    - containerPort: 3000
  volumes:
  - name: grafana-storage-volume
    emptyDir: {}
  - name: grafana-config-volume
    configMap:
      name: grafana-config
  - name: grafana-dashboards-volume
    configMap:
      name: grafana-dashboards-config
EOF

#
kubectl get pod -n calico-monitoring

#
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: calico-monitoring
spec:
  type: NodePort
  selector:
    app:  grafana-pod
    role: monitoring
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
      nodePort: 30002 
EOF
kubectl get svc,ep -n calico-monitoring

# 그라파나 접속 주소 : 초기 계정 ( admin , admin )
echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):30002"  # [실습환경 A Type]
echo -e "Grafana URL = http://192.168.10.10:30002"            # [실습환경 B Type]

위의 구성을 완료하면, 그라파나에서 다음과 같이 felix의 각종 지표를 대시보드로 확인할 수 있습니다.



📌 Conclusion

이번 Calico 네트워크 스터디를 통해 쿠버네티스의 고급 네트워킹 솔루션에 대해 심도 있게 이해할 수 있었습니다. 특히 Calico의 다양한 네트워크 모드(Direct, IPIP, VXLAN)와 그 작동 원리를 탐구하며, 각 모드가 파드 간 통신을 어떻게 효율적으로 지원하는지 학습했습니다.

BGP 기반의 Calico 라우팅 기술을 통해 클러스터 내부 및 외부와의 네트워크 연결이 어떻게 최적화되는지 파악하였으며, WireGuard를 활용한 암호화 기능으로 보안이 강화된 통신이 가능함을 확인했습니다.

네트워크 정책(Network Policy)을 활용하여 Calico가 세밀한 트래픽 제어와 보안을 제공하는 방법을 이해하였으며, 인그레스와 이그레스 규칙을 통해 파드 간 통신을 효과적으로 관리하는 방법을 배웠습니다.

또한 Prometheus와 Grafana를 활용한 모니터링 설정을 실습하면서 Calico 컴포넌트의 성능 지표를 실시간으로 수집하고 시각화하는 방법을 익혔습니다. 이는 네트워크 문제 진단과 성능 최적화에 큰 도움이 될 것으로 기대됩니다.

Calico는 복잡하면서도 유연한 네트워킹 솔루션으로서, 쿠버네티스 환경에서 안정적이고 효율적인 네트워크 인프라를 제공합니다. 이번 스터디를 통해 Calico의 기본 원리와 실제 구성 방법을 숙지하게 되었으며, 향후 대규모 쿠버네티스 클러스터의 네트워크 설계 및 문제 해결 시 이 지식을 적극 활용할 수 있을 것입니다. 또한 클라우드 네이티브 환경에서의 네트워크 보안과 성능 최적화에 대한 깊은 인사이트를 얻을 수 있었습니다.



🔗 Reference

profile
안녕하세요! DevOps 엔지니어 이재찬입니다. 블로그에 대한 피드백은 언제나 환영합니다! 기술, 개발, 운영에 관한 다양한 주제로 함께 나누며, 더 나은 협업과 효율적인 개발 환경을 만드는 과정에 대해 인사이트를 나누고 싶습니다. 함께 여행하는 기분으로, 즐겁게 읽어주시면 감사하겠습니다! 🚀

0개의 댓글

관련 채용 정보