가시다님의 KANS [3기] 스터디 내용을 정리한 포스트 입니다.
- AWS 기반으로 Calico 실습을 위한 K8S를 배포합니다.
- 실습 환경: K8S v1.30.X, 노드 OS(Ubuntu 22.04 LTS) , CNI(Calico v3.28.1, IPIP, NAT enable) , IPTABLES proxy mode
- 2개의 네트워크 대역이 존재 : AWS 환경에서 k8s-rtr 은 없고, AWS 내부 라우터가 대신 라우팅 처리합니다.
- 실습 환경 구성
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-3w.yaml
# CloudFormation 스택 배포
aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t2.micro KeyName=2024-03-11_eks SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
# CloudFormation 스택 배포 완료 후 k8s-m EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2
# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do
date
AWS_PAGER="" aws cloudformation list-stacks \
--stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
--query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
--output table
sleep 1
done
# k8s-m EC2 SSH 접속
ssh -i ~/.ssh/2024-03-11_eks.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
CNI가 설치되지 않아 node의 상태가 NotReady로 되어있습니다.

# 모니터링
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
# 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
# CNI 설치 후 파드 상태 확인
kubectl get pod -A -o wide
- CNI 설치 후 calico controller, node 파드가 생성됨
- k8s node의 상태도 Ready로 확인됨
# helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system
# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
# kube-ops-view 접속 URL 확인 (1.5 , 2 배율)
echo -e "KUBE-OPS-VIEW URL = http://$(curl -s ipinfo.io/ip):30000/#scale=1.5"
# (참고) 삭제
helm uninstall -n kube-system kube-ops-view
# 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
Calico CNI (Container Network Interface)는 Kubernetes 클러스터에서 네트워크 연결을 관리하고 보안을 강화하는 데 사용되는 CNI 플러그인 중 하나입니다. 주로 네트워크 정책 및 보안을 제공하는데 강점이 있으며, BGP(Border Gateway Protocol)를 사용해 데이터 패킷을 효율적으로 라우팅합니다.
주요 기능:
1. 네트워크 정책: Pod 간의 트래픽을 세밀하게 제어할 수 있으며, 이를 통해 보안 수준을 높일 수 있습니다.
2. IPIP 및 VXLAN 터널링: 클러스터 내 네트워크를 더욱 유연하게 구성할 수 있습니다.
3. 확장성: 대규모 클러스터에서도 안정적으로 동작하며, 고성능을 제공합니다.
Calico는 순수한 L3 네트워크 솔루션이기 때문에, L2 네트워크를 사용하지 않고 IP 기반으로 통신을 처리합니다. 클러스터 내부 및 외부로의 연결을 효율적으로 관리할 수 있어, 특히 네트워크 보안과 성능을 중요시하는 환경에서 많이 사용됩니다.

# 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

# 칼리코 IPAM 정보 확인
calicoctl ipam show
# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
# host-local IPAM 정보 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo

# CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist | jq

# 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
# calico endpoint (파드)의 정보 확인
calicoctl get workloadEndpoint -A

동일 노드 내의 파드 간 통신은 내부에서 직접 통신됩니다.
- iptables FORWARD Rule 에 정책(허용) 사용
- caliX# 인터페이스에 proxy arp 설정으로 파드에서 바라보는 게이트웨이의 MAC 정보를 파드가 전달 받음
- 동일 노드 내에 파드 간 통신에서는 tunnel 인터페이스는 미 관여

# 파드 생성
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

- k8s-w1 노드에 신규로 생성된 pod 2대가 Running 상태임
- 3개의 calice#~와 라우팅 대역이 테이블에 추가된 것으로 확인 할 수 있다.
# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh
...
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
# ARP 정보는 현재 아무것도 없다
ip -c neigh

[파드2]
# 마스터 노드에서 아래 실행
kubectl exec pod2 -it -- zsh
...
# IP는 32bit 서브넷을 가진다
pod2> ip -c addr
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod2> route -n
# ARP 정보는 현재 아무것도 없다
ip neighbor show

노드에서 실행)
# iptables 필터 테이블에 FORWARD 리스트 중 cali-FORWARD 룰 정보를 필터링해서 watch 로 확인
watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"
# (마스터노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
VETH2=$(calicoctl get workloadEndpoint | grep pod2 | awk '{print $4}')
echo $VETH2
# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=calice0906292e2
VETH2=calibd2348b4f67
# 노드1 calice# 인터페이스의 proxy arp 설정 확인
# cat /proc/sys/net/ipv4/conf/<자신의 pod1에 연결된 calice# 이름>/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH1/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH2/proxy_arp
# 파드1 혹은 파드2에 veth 로 연결된 호스트 네트워크 인터페이스 calice# 중 1개 선택해서 tcpdump
tcpdump -i $VETH1 -nn
# 파드1 Shell 에서 실행 : 정상 통신!
kubectl exec pod1 -it -- zsh
--------------------
ping -c 10 <파드2 IP>
- 파드1에서 게이트웨이의 IP인 169.254.1.1 의 MAC 주소를 알기 위해서 ARP Request 를 보내고 이때 veth 연결된 calice#~ 에 proxy arp 설정이 되어 있고, 자신의 mac 주소(ee:ee:ee:ee:ee:ee)를 알려주고, 이후 정상 통신됨

파드에서 외부(인터넷) 통신 시에는 해당 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됨
- calico 기본 설정은 natOutgoing: true 이다. 즉, iptables 에 MASQUERADE Rule(룰) 에 의해서 외부에 연결됨
- calice# 인터페이스에 proxy arp 설정
- 파드와 외부간 직접 통신에서는 tunnel 인터페이스는 미 관여
1. 파드 배포 전 기본 상태 확인
# 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
$ 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()
# 워커 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
$ 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
# 파드 생성
curl -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod1.yaml
kubectl apply -f node1-pod1.yaml
# 생성된 파드 정보 확인
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 5s 172.16.158.6 k8s-w1 <none> <none>

[터미널1] (파드)
# 파드에서 외부 정상 통신 확인
kubectl exec pod1 -it -- zsh
# 통신 확인
pod1> ping -c 10 8.8.8.8
[터미널2] (W1)
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
[터미널3] (W1)
tcpdump -i calice0906292e2 -nn icmp


다른 노드 환경에서 파드 간 통신 시에는 IPIP 터널(기본값) 모드를 통해서 이루어 집니다
- 각 노드에 파드 네트워크 대역은 Bird 에 의해서 BGP 로 광고 전파/전달 되며, Felix 에 의해서 호스트의 라우팅 테이블에 자동으로 추가 및 삭제 됩니다
- 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신됩니다
- 실제 패킷 확인 시 Outer IP 헤더와 Inner IP 헤더가 보인다!
route | head -2 ; route -n | grep tunl0

노드1과 노드2에 각각 파드 1개씩 생성
# 파드 생성
$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
WORKLOAD NODE NETWORKS INTERFACE
pod1 k8s-w1 172.16.158.7/32 calice0906292e2
pod2 k8s-w2 172.16.184.4/32 calibd2348b4f67
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.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.158.0 0.0.0.0 255.255.255.0 U 0 0 0 *
172.16.158.1 0.0.0.0 255.255.255.255 UH 0 0 0 calie862d12b00b
172.16.158.7 0.0.0.0 255.255.255.255 UH 0 0 0 calice0906292e2
172.16.184.0 192.168.10.102 255.255.255.0 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.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.158.0 192.168.10.101 255.255.255.0 UG 0 0 0 tunl0
172.16.184.0 0.0.0.0 255.255.255.0 U 0 0 0 *
172.16.184.1 0.0.0.0 255.255.255.255 UH 0 0 0 cali05f414d4d59
172.16.184.2 0.0.0.0 255.255.255.255 UH 0 0 0 cali35120ff9265
172.16.184.3 0.0.0.0 255.255.255.255 UH 0 0 0 cali5b1cbf5ab56
172.16.184.4 0.0.0.0 255.255.255.255 UH 0 0 0 calibd2348b4f67
[터미널1] (파드)
## pod 1 ##
$ ping -c 10 {pod2 ip}
[터미널2] (w1)
## node 1 ##
$ watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
[터미널1] (w2)
## node 2 ##
$ tcpdump -i tunl0 -nn

tunl0를 통해 IPIP 터널링을 통해 캡슐화된 패킷이 실제 네트워크로 나가는 것을 알수 있습니다.

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

"2.3 다른 노드에서 파드 <-> 파드 통신" 과 동일한 내용으로 3.1 IPIP 모드 실습은 생략합니다.
- 파드 통신 패킷이 출발지 노드의 라우팅 정보를 보고 목적지 노드로 원본 패킷 그대로 전달합니다
- 오버헤드가 거의 없기 때문에 IPIP에 비하면 빠른 편에 속합니다
- 클라우드 사업자 네트워크의 경우 NIC 에 매칭되지 않는 IP 패킷은 차단되니, NIC에 Source/Destination Check 기능을 Disable 해야 합니다
$ aws ec2 modify-instance-attribute --instance-id <INSTANCE_ID> --source-dest-check "{\"Value\": false}"

[ipip 설정 변경전 - Always]

현재 상태를 보면 ipip 설정이 always로 설정되어 있으며 각 노드의 인터페이스가 'tunl0'으로 설정되어 있습니다.
ipip 설정을 never로 바꾸면 각 노드의 인터페이스가 'ens0'으로 변경된것을 확인할 수 있습니다. 즉 tunl0에서 ens5로 인터페이스가 변경되어 터널 인터페이스로 라우팅을 하지 않는 것을 확인했습니다.
[ipip 설정 변경후 - Never]

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

# 파드 Shell 접속
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
# 파드가 동작하는 노드의 eth0에서 패킷 덤프
tcpdump -i ens5 -nn icmp
# 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i ens5 icmp -w /tmp/calico-direct.pcap
[Pod1 -> Pod2로 ping test]
Pod1 에서 Pod2로 통신 시 TCP dump를 확인해 보면, 통신은 잘 되고 Pod들의 IP가 그대로 노출됨

[Pod1 -> Pod3로 ping test]
Pod1에서 Pod3로 통신을 시도하면, 나가는 패킷은 있지만, 들어오는 패킷은 없이 통신이 안됨

AWS Virtual Router에 Pod 간 라우팅 정보가 없기 때문에 발생합니다. AWS Virtual Router는 기본적으로 할당된 Subnet 간의 라우팅만 관리하기 때문에 대역폭이 다른 두 서브넷 간의 Pod 간 다이렉트 통신이 안된다.
이를 해결하기 위해서는 VPC route table 에 노드에 파드 대역 라우팅 추가로 통신 가능하다.
노드 간 같은 네트워크 대역(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
172.16.34.0 192.168.20.100 255.255.255.0 UG 0 0 0 tunl0 # 노드간 다른 네트워크 대역 - IPIP 모드
172.16.158.0 192.168.10.101 255.255.255.0 UG 0 0 0 ens5 # 노드간 같은 네트워크 대역 - Direct 모드
172.16.184.0 192.168.10.102 255.255.255.0 UG 0 0 0 ens5 # 노드간 같은 네트워크 대역 - Direct 모드
(⎈|HomeLab:default) root@k8s-m:~# k get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 3m36s 172.16.158.10 k8s-w1 <none> <none>
pod2 1/1 Running 0 3m36s 172.16.184.7 k8s-w2 <none> <none>
pod3 1/1 Running 0 3m36s 172.16.34.4 k8s-w0 <none> <none>
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
[Pod1 -> Pod2로 ping test]
Pod1 에서 Pod2로 통신 시 TCP dump를 확인해 보면, 통신은 잘 되고 Pod들의 IP가 그대로 노출됨

[Pod1 -> Pod3로 ping test]
Pod1 에서 Pod3로 통신 시 TCP dump를 확인해 보면, 통신은 잘 되고 Pod들의 IP가 그대로 노출됨
파드 간 통신이 노드와 노드 구간에서는 VXLAN 인캡슐레이션을 통해서 이루어 집니다

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

WireGuard
Pod 간의 통신 패킷을 암호화하기 위해 사용되는 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)가 현격하게 작다.
그말은 코드량이 많지 않으니 그만큼 철저히 검증될 가능성이 높고, 예상치 못한 곳에서의 버그로 인한 취약점이 발생할 가능성이 상대적으로 적다는 얘기가 된다.
- 달리는 기차 위에서 안정적으로 사내망에 접속하고 싶을 때 : Public key를 기반으로 peer를 인식하기 때문에 LTE 기지국이 바뀌더라도 tunnel이 끊기지 않고 안정적으로 유지
WireGuard 설치
# 설치
apt install wireguard -y
# WireGuard 버전 확인
wg version

# 설정
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
# 파드 생성
$kubectl apply -f node3-pod3.yaml
(⎈|HomeLab:default) root@k8s-m:~# k get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 67s 172.16.158.12 k8s-w1 <none> <none>
pod2 1/1 Running 0 67s 172.16.184.9 k8s-w2 <none> <none>
pod3 1/1 Running 0 67s 172.16.34.6 k8s-w0 <none> <none>
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
ss -unlp
tcpdump -i <eth0> -nn udp port 51820
tcpdump -i ens5 -nn udp port 51820
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> udp port 51820 -w /tmp/calico-wireguard.pcap
tcpdump -i ens5 udp port 51820 -w /tmp/calico-wireguard.pcap
# 현재 모든 워커 노드에 tcpdump 후 ping curl 테스트 시 모든 노드에서 트래픽이 발생한다 >> 이유는 VirtualBox 에 nic2 에 "무작위 모드 : 모두 허용" 상태여서, 모든 노드로 패킷이 전달됨
# VirtualBox 에 nic2 에 "무작위 모드 : 거부"로 설정 후 테스트 하게 되면 정확하게, 출발지와 목적지의 VM에서만 패킷이 확인된다!
모드워커노드) tcpdump -i ens5 -nn udp port 51820

TCP dump를 통해 패킷이 암호화 되어 있는 것을 확인 할 수 있다.

프로메테우스와 그라파나를 통해서 calico 운영 상태를 모니터링 할 수 있습니다.
다음 설정을 참고해서 grafana 운영 데쉬보드를 구성해 보겠습니다.
# 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
# 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
#
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
# 파드 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]

# 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 대시보드를 확인해 봅니다.
