Calico 네트워크 모드
- Calico 는 다양한 네트워크 통신 방법(모드)를 제공합니다.
- Calico 나 Cilium 에서 파드 혹은 네임스페이스의 레벨에서 IN/OUT 트래픽에 대한 통제가 가능합니다.
Calico 라우팅 모드
Direct 모드
파드 통신 패킷이 출발지 노드의 라우팅 정보를 보고 목적지 노드로 원본 패킷 그대로 전달합니다
클라우드 사업자 네트워크의 경우 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 모드 설정
-Direct 모드 설정 및 확인
# 모드 정보 확인 : IPIPMODE 가 Never 로 변경!
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 에서 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 enp0s8
172.16.46.0 192.168.100.102 255.255.255.192 UG 0 0 0 enp0s8
172.16.197.0 192.168.100.103 255.255.255.192 UG 0 0 0 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
파드간 ping 통신 실행 및 패킷 캡쳐 확인
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>
# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i <eth0> -nn icmp
tcpdump -i ens5 -nn icmp
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> icmp -w /tmp/calico-direct.pcap
tcpdump -i ens5 icmp -w /tmp/calico-direct.pcap
!!워커노드1(파드) → 워커노드2(파드) or 워커노드0(파드) 와 통신 확인해보자! 되는 경우 어떻게 되는 걸까? 안되는 경우는 왜일까? ⇒ Overlay 네트워크 기법이 필요한 이유!
파드 삭제
kubectl delete -f node3-pod3.yaml
CrossSubnet 모드
노드 간 같은 네트워크 대역(Direct 모드로 동작) , 노드 간 다른 네트워크 대역(IPIP 모드로 동작)
# CrossSubnet 모드 설정
calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode":"CrossSubnet"}}'
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Never/ipipMode: CrossSubnet/" | calicoctl apply -f -
# 모드 확인
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>
파드 삭제
kubectl delete -f node3-pod3.yaml
Pod 패킷 암호화 (네트워크 레벨)
- WireGuard는 구닥다리 IPsec 및 OpenVPN의 대항마로 등장한 open source VPN project 이며 작년, Linux 5.6 커널에 WireGuard 1.0.0 기본 패키지로 탑재되었다.
- 정말 간결한 코드 구조와 빠른 성능 (모든 것이 kernel에서 동작하고, 주요 암호 알고리즘에 대해서 병렬처리하므로써 빠른 속도를 자랑함)
노드에 WireGuard 설치 - 링크 ← 이미 설치되어 있음
# 설치
apt install wireguard -y
# WireGuard 버전 확인
wg version
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
키 정보 확인
파드 생성
kubectl apply -f node3-pod3.yaml
파드간 ping 통신 실행 및 패킷 캡쳐 확인
# 파드 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
부가 기능
파드에 고정 IP를 직접 지정해서 배포 - Docs
# 배포
cat <<EOF| kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: static-ip-pod
annotations:
"cni.projectcalico.org/ipAddrs": "[\"172.16.100.100\"]"
spec:
containers:
- name: static-ip-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 확인 : annotations 에 지정한 IP를 파드에 직접 할당 확인!
kubectl get pod static-ip-pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
static-ip-pod 1/1 Running 0 110s 172.16.100.100 k8s-w1 <none> <none>
# 삭제
kubectl delete pod static-ip-pod
Floating IP 를 파드에 할당 : Add a floating IP to a pod - Docs
모니터링
Monitor Calico component metrics - Docs
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"
Visualizing metrics via Grafana - Docs
# 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"
실습환경 삭제
# CloudFormation 스택 삭제
aws cloudformation delete-stack --stack-name mylab
# [모니터링] 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
'study > KANS 3기' 카테고리의 다른 글
KANS 3기 5주차 두번째 (0) | 2024.10.04 |
---|---|
KANS 3기 4주차 (0) | 2024.09.28 |
KANS 3기 3주차 첫번째 (0) | 2024.09.18 |
KANS 3기 3주차 실습환경 구축 (0) | 2024.09.08 |
KANS 3기 2주차 두번째 (0) | 2024.09.07 |