5주차 공유 시작하겠습니다.
GitHub - metallb/metallb: A network load-balancer implementation for Kubernetes using standard routing protocols
A network load-balancer implementation for Kubernetes using standard routing protocols - metallb/metallb
github.com
MetalLB - Layer2 모드
- 리더 파드가 선출되고 해당 리더 파드가 생성된 노드로만 트래픽이 인입되어 해당 노드에서 iptables 분산되어 파드로 접속
- 권장 사용 환경 : 테스트 및 소규모의 환경(동일 네트워크 1개 사용)에서 클라이언트 IP 보존이 필요 없을 때
MetalLB - BGP 모드
- speaker 파드가 BGP로 서비스 정보(EXTERNAL-IP)를 전파 후, 외부에서 라우터를 통해 ECMP 라우팅으로 부하 분산 접속
- 권장 사용 환경 : 규모가 있고, 클라이언트 IP보존과 장애 시 빠른 절체가 필요하며, 네트워크 팀 협조가 가능할때
MetalLB
L2 mode (ARP)
BGP mode
- MetalLB 는 온프레미스 환경(IDC)에서 사용할 수 있는 서비스(로드밸런서 타입)입니다 ← 클라우드 환경의 서비스(로드밸런서 타입)와는 동작이 조금 다릅니다!
- 서비스(로드 밸런서)의 'External IP' 전파를 위해서 표준 프로토콜인 ARP(IPv4)/NDP(IPv6), BGP 를 사용합니다
- 일반적인 BGP 데몬과 다르게 BGP 업데이트(외부에서 광고하는 네트워크 대역)을 받지 않습니다.
- 클라우드 플랫폼 호환 : 대부분의 클라우드 플랫폼(예 AWS, Azure, GCP 등)과 호환되지 않습니다 - 링크
Cloud Compatibility :: MetalLB, bare metal load-balancer for Kubernetes
In general, MetalLB is not compatible with cloud providers. MetalLB is for bare-metal clusters, and even cloud providers that offer “dedicated servers” usually don’t support the network protocols that MetalLB requires. This is an incomplete list of c
metallb.universe.tf
- CNI(네트워크 플러그인) 호환 : 일부 CNI와 연동에 이슈가 있습니다 - 링크
Layer 2 모드 (ARP/NDP)
Layer2 모드에서 서비스 접속
리더 파드가 선출되고 해당 리더 파드가 생성된 노드로만 트래픽이 인입되어 해당 노드에서 iptables 분산되어 파드로 접속
- 서비스(로드밸런서) 'External IP' 생성 시 speaker 파드 중 1개가 리더가 되고, 리더 speaker 파드가 존재하는 노드로 서비스 접속 트래픽이 인입되게 됩니다
- 데몬셋으로 배포된 speaker 파드는 호스트 네트워크를 사용합니다 ⇒ "NetworkMode": "host"
- 리더는 ARP(GARP, Gratuitous APR)로 해당 'External IP' 에 대해서 자신이 소유(?)라며 동일 네트워크에 전파를 합니다
- 만약 리더(노드)가 장애 발생 시 자동으로 나머지 speaker 파드 중 1개가 리더가 됩니다.
멤버 리스터 및 장애 발견은 hashicorp 의 memberlist 를 사용 - Gossip based membership and failure detection
BGP 모드 - 링크
-
speaker 파드가 BGP로 서비스 정보(EXTERNAL-IP)를 전파 후, 외부에서 라우터를 통해 ECMP 라우팅으로 부하 분산 접속
- speaker 파드에 BGP 가 동작하여 서비스 정보(EXTERNAL-IP)를 전파한다
- 기본은 IP주소(32bit)를 전파하며, 설정으로 축약된 네트워크 정보를 전파할 수 있다 → bgp-advertisements 에 aggregation-length 설정
- BGP 커뮤니티, localpref 등 BGP 관련 설정을 할 수 있다
- IP 주소 마지막이 0 과 255 를 처리를 못하는 라우터 장비가 있을 경우 avoid-buggy-ips: true 옵션으로 할당되지 않게 할 수 있다
- 외부 클라이언트에서 SVC(서비스, EXTERNAL-IP)로 접속이 가능하며, 라우터에서 ECMP 라우팅을 통해 부하 분산 접속 할 수 있다
- 일반적으로 ECMP 는 5-tuple(프로토콜, 출발지IP, 목적지IP, 출발지Port, 목적지Port) 기준으로 동작합니다.
- 물론 라우터 장비에 따라 다양한 라우팅(분산) 처리가 가능합니다
실습환경
# 파드와 서비스 사용 가능 네트워크 대역
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=10.10.0.0/16",
# kube-proxy 모드 확인 : iptables proxy 모드
kubectl describe configmap -n kube-system kube-proxy | grep mode
mode: "iptables"
# iptables 정보 확인
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker2 iptables -t $i -S ; echo; done
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-worker3 iptables -t $i -S ; echo; done
파드생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: webpod1
labels:
app: webpod
spec:
nodeName: myk8s-worker
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod2
labels:
app: webpod
spec:
nodeName: myk8s-worker2
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
EOF
접속확인
# 파드 정보 확인
kubectl get pod -owide
# 파드 IP주소를 변수에 지정
WPOD1=$(kubectl get pod webpod1 -o jsonpath="{.status.podIP}")
WPOD2=$(kubectl get pod webpod2 -o jsonpath="{.status.podIP}")
echo $WPOD1 $WPOD2
# 접속 확인
docker exec -it myk8s-control-plane ping -i 1 -W 1 -c 1 $WPOD1
docker exec -it myk8s-control-plane ping -i 1 -W 1 -c 1 $WPOD2
docker exec -it myk8s-control-plane curl -s --connect-timeout 1 $WPOD1 | grep Hostname
docker exec -it myk8s-control-plane curl -s --connect-timeout 1 $WPOD2 | grep Hostname
docker exec -it myk8s-control-plane curl -s --connect-timeout 1 $WPOD1 | egrep 'Hostname|RemoteAddr|Host:'
docker exec -it myk8s-control-plane curl -s --connect-timeout 1 $WPOD2 | egrep 'Hostname|RemoteAddr|Host:'
MetalLB - Layer 2 모드
MetalLB 설치 - 링크
Installation :: MetalLB, bare metal load-balancer for Kubernetes
Before starting with installation, make sure you meet all the requirements. In particular, you should pay attention to network addon compatibility. If you’re trying to run MetalLB on a cloud platform, you should also look at the cloud compatibility page
metallb.universe.tf
간단하게 manifests 로 설치 진행!
# Kubernetes manifests 로 설치
#kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/refs/heads/main/config/manifests/metallb-native-prometheus.yaml
# metallb crd 확인
kubectl get crd | grep metallb
bfdprofiles.metallb.io 2024-09-28T15:24:06Z
bgpadvertisements.metallb.io 2024-09-28T15:24:06Z
bgppeers.metallb.io 2024-09-28T15:24:06Z
communities.metallb.io 2024-09-28T15:24:06Z
ipaddresspools.metallb.io 2024-09-28T15:24:06Z
l2advertisements.metallb.io 2024-09-28T15:24:06Z
servicel2statuses.metallb.io 2024-09-28T15:24:06Z
# 생성된 리소스 확인 : metallb-system 네임스페이스 생성, 파드(컨트롤러, 스피커) 생성, RBAC(서비스/파드/컨피그맵 조회 등등 권한들), SA 등
kubectl get-all -n metallb-system # kubectl krew 플러그인 get-all 설치 후 사용 가능
kubectl get all -n metallb-system
# 파드 내에 kube-rbac-proxy 컨테이너는 프로메테우스 익스포터 역할 제공
kubectl get pods -n metallb-system -l app=metallb -o jsonpath="{range .items[*]}{.metadata.name}{':\n'}{range .spec.containers[*]}{' '}{.name}{' -> '}{.image}{'\n'}{end}{end}"
## metallb 컨트롤러는 디플로이먼트로 배포됨
kubectl get ds,deploy -n metallb-system
## 데몬셋으로 배포되는 metallb 스피커 파드의 IP는 네트워크가 host 모드이므로 노드의 IP를 그대로 사용
kubectl get pod -n metallb-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-679855f7d7-2dvbm 2/2 Running 0 9m17s 10.10.2.6 myk8s-worker3 <none> <none>
speaker-jfvh9 2/2 Running 0 9m17s 172.18.0.2 myk8s-worker <none> <none>
speaker-l2tdn 2/2 Running 0 9m17s 172.18.0.5 myk8s-worker3 <none> <none>
speaker-pzs8z 2/2 Running 0 9m17s 172.18.0.3 myk8s-worker2 <none> <none>
speaker-vfsdj 2/2 Running 0 9m17s 172.18.0.4 myk8s-control-plane <none> <none>
# (참고) 상세 정보 확인
kubectl get sa,cm,secret -n metallb-system
kubectl describe role -n metallb-system
kubectl describe deploy controller -n metallb-system
kubectl describe ds speaker -n metallb-system
확인
- 컨피그맵 생성 : 모드 및 서비스 대역 지정
서비스(External-IP) 대역을 노드가 속한 eth0의 대역이 아니여도 상관없다! → 다만 이 경우 GW 역할의 라우터에서 노드들로 라우팅 경로 지정 필요
# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
docker network ls
docker inspect kind
# kind network 중 컨테이너(노드) IP(대역) 확인 : 172.18.0.2~ 부터 할당되며, control-plane 이 꼭 172.18.0.2가 안될 수 도 있음
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
# IPAddressPool 생성 : LoadBalancer External IP로 사용할 IP 대역
kubectl explain ipaddresspools.metallb.io
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ippool
namespace: metallb-system
spec:
addresses:
- 172.18.255.200-172.18.255.250
EOF
kubectl get ipaddresspools -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
my-ippool true false ["172.18.255.200-172.18.255.250"]
# L2Advertisement 생성 : 설정한 IPpool을 기반으로 Layer2 모드로 LoadBalancer IP 사용 허용
kubectl explain l2advertisements.metallb.io
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-advertise
namespace: metallb-system
spec:
ipAddressPools:
- my-ippool
EOF
kubectl get l2advertisements -n metallb-system
NAME IPADDRESSPOOLS IPADDRESSPOOL SELECTORS INTERFACES
my-l2-advertise ["my-ippool"]
확인
허용하기
서비스 생성 및 확인
서비스(LoadBalancer 타입) 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer # 서비스 타입이 LoadBalancer
#externalTrafficPolicy: Local
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
#externalTrafficPolicy: Local
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
#externalTrafficPolicy: Local
EOF
서비스 확인 및 리더 Speaker 파드 확인
# arp scan 해두기
docker exec -it myk8s-control-plane arp-scan --interfac=eth0 --localnet
# LoadBalancer 타입의 서비스 생성 확인 : EXTERNAL-IP가 서비스 마다 할당되며, 실습 환경에 따라 다를 수 있음
## LoadBalancer 타입의 서비스는 NodePort 와 ClusterIP 를 포함함 - 'allocateLoadBalancerNodePorts : true' 기본값
## ExternalIP 로 접속 시 사용하는 포트는 PORT(S) 의 앞에 있는 값을 사용 (아래의 경우는 TCP 80 임)
## 만약 노드의 IP에 NodePort 로 접속 시 사용하는 포트는 PORT(S) 의 뒤에 있는 값을 사용 (아래는 30485 임)
kubectl get service,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.200.1.1 <none> 443/TCP 121m
service/svc1 LoadBalancer 10.200.1.69 172.18.255.200 80:30485/TCP 3m37s
service/svc2 LoadBalancer 10.200.1.218 172.18.255.201 80:31046/TCP 3m37s
service/svc3 LoadBalancer 10.200.1.81 172.18.255.202 80:30459/TCP 3m37s
NAME ENDPOINTS AGE
endpoints/kubernetes 172.18.0.5:6443 31m
endpoints/svc1 10.10.1.6:80,10.10.3.6:80 8m4s
endpoints/svc2 10.10.1.6:80,10.10.3.6:80 8m4s
endpoints/svc3 10.10.1.6:80,10.10.3.6:80 8m4s
# LoadBalancer 타입은 기본적으로 NodePort를 포함 사용. NodePort는 ClusterIP를 포함 사용.
## 클라우드사업자 LB Type이나 온프레미스환경 HW LB Type 경우 LB 사용 시 NodePort 미사용 설정 가능
kubectl describe svc svc1
## 아래 처럼 LB VIP 별로 이던 speaker 배포된 노드가 리더 역할을 하는지 확인 가능
kubectl describe svc | grep Events: -A5
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal IPAllocated 40m metallb-controller Assigned IP ["172.18.255.201"]
Normal nodeAssigned 40m metallb-speaker announcing from node "myk8s-worker" with protocol "layer2"
...
kubectl get svc svc1 -o json | jq
...
"spec": {
"allocateLoadBalancerNodePorts": true,
...
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "172.18.255.202",
"ipMode": "VIP" # https://kubernetes.io/blog/2023/12/18/kubernetes-1-29-feature-loadbalancer-ip-mode-alpha/
} # https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-ip-mode
# metallb CRD인 servicel2status 로 상태 정보 확인
kubectl explain servicel2status
kubectl get servicel2status -n metallb-system
kubectl describe servicel2status -n metallb-system
kubectl get servicel2status -n metallb-system -o json --watch # watch 모드
# 현재 SVC EXTERNAL-IP를 변수에 지정
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC2EXIP=$(kubectl get svc svc2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC3EXIP=$(kubectl get svc svc3 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP $SVC2EXIP $SVC3EXIP
# mypc/mypc2 에서 현재 SVC EXTERNAL-IP를 담당하는 리더 Speaker 파드 찾는법 : arping 툴 사용
docker exec -it mypc arping -I eth0 -f -c 1 $SVC1EXIP
docker exec -it mypc arping -I eth0 -f -c 1 $SVC2EXIP
docker exec -it mypc arping -I eth0 -f -c 1 $SVC3EXIP
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc arping -I eth0 -f -c 1 $i; done
docker exec -it mypc ip -c neigh
docker exec -it mypc ping -c 1 -w 1 -W 1 $SVC1EXIP
docker exec -it mypc ping -c 1 -w 1 -W 1 $SVC2EXIP
docker exec -it mypc ping -c 1 -w 1 -W 1 $SVC3EXIP
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
for i in 172.18.0.2 172.18.0.3 172.18.0.4 172.18.0.5; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
# mypc/mypc2 에서 arp 테이블 정보 확인 >> SVC IP별로 리더 파드(스피커) 역할의 노드를 확인!
docker exec -it mypc ip -c neigh | sort
172.18.0.2 dev eth0 lladdr 02:42:ac:12:00:02 REACHABLE
172.18.0.3 dev eth0 lladdr 02:42:ac:12:00:03 REACHABLE
172.18.0.4 dev eth0 lladdr 02:42:ac:12:00:04 REACHABLE
172.18.0.5 dev eth0 lladdr 02:42:ac:12:00:05 DELAY
172.18.255.200 dev eth0 lladdr 02:42:ac:12:00:04 STALE
172.18.255.201 dev eth0 lladdr 02:42:ac:12:00:04 STALE
172.18.255.202 dev eth0 lladdr 02:42:ac:12:00:02 STALE
kubectl get node -owide # mac 주소에 매칭되는 IP(노드) 찾기
# (옵션) 노드에서 ARP 패킷 캡쳐 확인
docker exec -it myk8s-control-plane tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker2 tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker3 tcpdump -i eth0 -nn arp
# (옵션) metallb-speaker 파드 로그 확인
kubectl logs -n metallb-system -l app=metallb -f
kubectl logs -n metallb-system -l component=speaker --since 1h
kubectl logs -n metallb-system -l component=speaker -f
# (옵션) kubectl krew 플러그인 stern 설치 후 아래 명령 사용 가능
kubectl stern -n metallb-system -l app=metallb
kubectl stern -n metallb-system -l component=speaker --since 1h
kubectl stern -n metallb-system -l component=speaker # 기본 설정이 follow
kubectl stern -n metallb-system speaker # 매칭 사용 가능
생성 확인
arp 확인 하기
svc에 핑을 날리면 응답이 안 온다. -> svc는 icmp를 지원하지 않는다.
서비스 접속 테스트
클라이언트(mypc, mypc2) → 서비스(External-IP) 접속 테스트
# 현재 SVC EXTERNAL-IP를 변수에 지정
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC2EXIP=$(kubectl get svc svc2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC3EXIP=$(kubectl get svc svc3 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP $SVC2EXIP $SVC3EXIP
# mypc/mypc2 에서 접속 테스트
docker exec -it mypc curl -s $SVC1EXIP
docker exec -it mypc curl -s $SVC1EXIP | grep Hostname
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc curl -s $i | grep Hostname ; done
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do echo ">> Access Service External-IP : $i <<" ; docker exec -it mypc curl -s $i | grep Hostname ; echo ; done
## RemoteAddr 주소는 어떻게 나오나요? 왜 그럴까요?
## NodePort 기본 동작과 동일하게 인입한 노드의 인터페이스로 SNAT 되어서 최종 파드로 전달됨
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do echo ">> Access Service External-IP : $i <<" ;docker exec -it mypc curl -s $i | egrep 'Hostname|RemoteAddr|Host:' ; echo ; done
# 부하분산 접속됨
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC1EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC2EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC3EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
# 지속적으로 반복 접속
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC1EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC2EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC3EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
# LoadBalancer Type은 기본값으로 NodePort 포함. NodePort 서비스는 ClusterIP 를 포함
# NodePort:PORT 및 CLUSTER-IP:PORT 로 접속 가능!
kubectl get svc svc1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc1 LoadBalancer 10.200.1.82 172.18.255.202 80:30246/TCP 49m
# 컨트롤노드에서 각각 접속 확인 실행 해보자
docker exec -it myk8s-control-plane curl -s 127.0.0.0:30246 # NodePor Type
docker exec -it myk8s-control-plane curl -s 10.200.1.82 # ClusterIP Tpye
노드IP 감춰보기!
부하분산 확인
iptables 정책 적용 확인
# 현재 SVC EXTERNAL-IP를 변수에 지정
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC2EXIP=$(kubectl get svc svc2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC3EXIP=$(kubectl get svc svc3 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP $SVC2EXIP $SVC3EXIP
# 부하분산 접속됨
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC1EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC2EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC3EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
# 지속적으로 반복 접속
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC1EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC2EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC3EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
# 컨트롤플레인에서 확인 : 너무 복잡해서 리턴 트래픽에 대해서는 상세히 분석 정리하지 않습니다.
docker exec -it myk8s-worker bash
----------------------------------------
# iptables 확인
iptables -t filter -S
iptables -t nat -S
iptables -t nat -S | wc -l
iptables -t mangle -S
# iptables 상세 확인 - 매칭 패킷 카운트, 인터페이스 정보 등 포함
iptables -nvL -t filter
iptables -nvL -t nat
iptables -nvL -t mangle
# rule 갯수 확인
iptables -nvL -t filter | wc -l
iptables -nvL -t nat | wc -l
# 규칙 패킷 바이트 카운트 초기화
iptables -t filter --zero; iptables -t nat --zero; iptables -t mangle --zero
# 정책 확인 : 아래 정책 내용은 핵심적인 룰(rule)만 표시했습니다!
iptables -t nat -nvL
iptables -t nat -S
PREROUTING
# SVC1(External-IP) 접속 시 iptables 에서 DNAT 되어 파드로 전달된다 (SNAT 도 동작)
iptables -t nat -S PREROUTING
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
iptables -t nat -S KUBE-SERVICES
SVC1EXIP=<직접입력>
SVC1EXIP=172.18.255.202
iptables -t nat -S KUBE-SERVICES |grep $SVC1EXIP
-A KUBE-SERVICES -d 172.18.255.202/32 -p tcp -m comment --comment "default/svc1:svc1-webport loadbalancer IP" -m tcp --dport 80 -j KUBE-EXT-DLGPAL4ZCYSJ7UPR
iptables -t nat -S KUBE-EXT-DLGPAL4ZCYSJ7UPR
-A KUBE-EXT-DLGPAL4ZCYSJ7UPR -m comment --comment "masquerade traffic for default/svc1:svc1-webport external destinations" -j KUBE-MARK-MASQ
-A KUBE-EXT-DLGPAL4ZCYSJ7UPR -j KUBE-SVC-DLGPAL4ZCYSJ7UPR
iptables -t nat -S KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
iptables -t nat -S KUBE-SVC-DLGPAL4ZCYSJ7UPR
-A KUBE-SVC-DLGPAL4ZCYSJ7UPR ! -s 10.10.0.0/16 -d 10.200.1.82/32 -p tcp -m comment --comment "default/svc1:svc1-webport cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-DLGPAL4ZCYSJ7UPR -m comment --comment "default/svc1:svc1-webport -> 10.10.1.6:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-USUIKZUHVJLOFB4D
-A KUBE-SVC-DLGPAL4ZCYSJ7UPR -m comment --comment "default/svc1:svc1-webport -> 10.10.3.6:80" -j KUBE-SEP-SXKPWGGXEDMXXU4H
#
iptables -t nat -S KUBE-SEP-USUIKZUHVJLOFB4D
-A KUBE-SEP-USUIKZUHVJLOFB4D -s 10.10.1.6/32 -m comment --comment "default/svc1:svc1-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-USUIKZUHVJLOFB4D -p tcp -m comment --comment "default/svc1:svc1-webport" -m tcp -j DNAT --to-destination 10.10.1.6:80
iptables -t nat -S KUBE-SEP-SXKPWGGXEDMXXU4H
-A KUBE-SEP-SXKPWGGXEDMXXU4H -s 10.10.3.6/32 -m comment --comment "default/svc1:svc1-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-SXKPWGGXEDMXXU4H -p tcp -m comment --comment "default/svc1:svc1-webport" -m tcp -j DNAT --to-destination 10.10.3.6:80
POSTROUTING
iptables -t nat -S POSTROUTING
iptables -t nat -S KUBE-POSTROUTING
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
# SVC2(External-IP) 접속 시 iptables 에서 DNAT 되어 파드로 전달된다 (SNAT 도 동작) : 위와 상동!
# SVC3(External-IP) 접속 시 iptables 에서 DNAT 되어 파드로 전달된다 (SNAT 도 동작) : 위와 상동!
정책 확인
점프한 곳을 따라가보자
Failover 테스트
리더 Speaker 파드가 존재하는 노드를 재부팅! → curl 접속 테스트 시 10~20초 정도의 장애 시간이 발생하였다 ⇒ 이후 자동 원복 되며, 원복 시 5초 정도 장애 시간 발생!
# metallb CRD인 servicel2status 로 상태 정보 확인
kubectl get servicel2status -n metallb-system -o json --watch # watch 모드
# mypc/mypc2 에서 현재 SVC EXTERNAL-IP를 담당하는 리더 Speaker 파드 찾기
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
for i in 172.18.0.2 172.18.0.3 172.18.0.4 172.18.0.5; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
# mypc/mypc2 에서 arp 테이블 정보 확인 >> SVC IP별로 리더 파드(스피커) 역할의 노드를 확인!
docker exec -it mypc ip -c neigh | sort
kubectl get node -owide # mac 주소에 매칭되는 IP(노드) 찾기
# (옵션) 노드에서 ARP 패킷 캡쳐 확인
docker exec -it myk8s-control-plane tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker2 tcpdump -i eth0 -nn arp
docker exec -it myk8s-worker3 tcpdump -i eth0 -nn arp
# (옵션) metallb-speaker 파드 로그 확인
kubectl logs -n metallb-system -l app=metallb -f
kubectl logs -n metallb-system -l component=speaker --since 1h
kubectl logs -n metallb-system -l component=speaker -f
# (옵션) kubectl krew 플러그인 stern 설치 후 아래 명령 사용 가능
kubectl stern -n metallb-system -l app=metallb
kubectl stern -n metallb-system -l component=speaker --since 1h
kubectl stern -n metallb-system -l component=speaker # 기본 설정이 follow
kubectl stern -n metallb-system speaker # 매칭 사용 가능
# 사전 준비
## 지속적으로 반복 접속
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC1EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
## 상태 모니터링
watch -d kubectl get pod,svc,ep
## 실시간 로그 확인
kubectl logs -n metallb-system -l app=metallb -f
혹은
kubectl stern -n metallb-system -l app=metallb
# 장애 재연
## 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)를 중지
docker stop <svc1 번 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)> --signal 15
docker stop myk8s-worker --signal 15
docker ps -a
docker ps -a | grep worker$
## 지속적으로 반복 접속 상태 모니터링
### curl 연속 접속 시도 >> 대략 10초 이내에 정상 접근 되었지만, 20초까지는 불안정하게 접속이 되었다
### 실제로는 다른 노드의 speaker 파드가 리더가 되고, 이후 다시 노드(컨테이너)가 정상화되면, 다시 리더 speaker 가 됨
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC1EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
Hostname: webpod1
RemoteAddr: 172.18.0.2:25432
2024-09-29 06:31:07
2024-09-29 06:31:09
Hostname: webpod2
RemoteAddr: 172.18.0.2:26011
2024-09-29 06:31:10
2024-09-29 06:31:12
2024-09-29 06:31:14
...
# 변경된 리더 Speaker 파드 확인
# mypc/mypc2 에서 현재 SVC EXTERNAL-IP를 담당하는 리더 Speaker 파드 찾기
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
# mypc/mypc2 에서 arp 테이블 정보 확인 >> SVC IP별로 리더 파드(스피커) 역할의 노드를 확인!
docker exec -it mypc ip -c neigh | sort
kubectl get node -owide # mac 주소에 매칭되는 IP(노드) 찾기
# 장애 원복(노드 정상화)
## 노드(실제 컨테이너) 정상화
docker start <svc1 번 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)>
docker start myk8s-worker
# 변경된 리더 Speaker 파드 확인
# mypc/mypc2 에서 현재 SVC EXTERNAL-IP를 담당하는 리더 Speaker 파드 찾기
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
# mypc/mypc2 에서 arp 테이블 정보 확인 >> SVC IP별로 리더 파드(스피커) 역할의 노드를 확인!
docker exec -it mypc ip -c neigh | sort
kubectl get node -owide # mac 주소에 매칭되는 IP(노드) 찾기
worker를 꺼보자
그리고 다시 키면 리더를 다시 갖고 간다. (04번이 worker1번이다.)
MetalLB - BGP 모드(매우 간략히)
ExternalIP 서비스
- ExternalIP 서비스는 특정 노드IP(포트)로 인입한 트래픽을 컨테이너로 보내(ClusterIP 를 사용), 외부에서 접속할 수 있게 합니다
- ExternalIP 는 노드 1개 혹은 일부 노드들을 지정할 수 있다
- ExternalTrafficPolicy 정책을 사용할 수 없다 → 즉 외부에서 ExternalIP 로 접근 시 무조건 Node의 IP로 DNAT 되어서 Client IP 수집할 수 없다
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-echo
spec:
replicas: 2
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
terminationGracePeriodSeconds: 0
containers:
- name: ndks-websrv
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-externalip
spec:
type: ClusterIP
externalIPs:
- 192.168.10.101
- 192.168.10.102
ports:
- name: svc-webport
port: 9000
targetPort: 8080
selector:
app: deploy-websrv
EOF
# 확인 : ExternalIP 도 결국 ClusterIP를 사용(포함)
kubectl get svc svc-externalip
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-externalip ClusterIP 10.96.101.49 192.168.10.101,192.168.10.102 9000/TCP 46s
kubectl describe svc svc-externalip
Name: svc-externalip
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=deploy-websrv
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.101.49
IPs: 10.96.101.49
External IPs: 192.168.10.101,192.168.10.102
Port: svc-webport 9000/TCP
TargetPort: 8080/TCP
Endpoints: 172.16.158.18:8080,172.16.184.17:8080
Session Affinity: None
Events: <none>
# ExternalTrafficPolicy 설정이 없음
kubectl get svc svc-externalip -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2022-02-24T07:32:47Z"
name: svc-externalip
namespace: default
resourceVersion: "1454"
uid: a27a8ca6-44ef-4363-ba3f-9894b4af5a54
spec:
clusterIP: 10.110.23.53
clusterIPs:
- 10.110.23.53
externalIPs:
- 192.168.10.101
- 192.168.10.102
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: svc-webport
port: 9000
protocol: TCP
targetPort: 8080
selector:
app: deploy-websrv
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
'study > KANS 3기' 카테고리의 다른 글
KANS 3기 Gateway API (1) | 2024.10.12 |
---|---|
KANS 3기 6주차 첫번째 (0) | 2024.10.12 |
KANS 3기 5주차 두번째 (0) | 2024.10.04 |
KANS 3기 4주차 (0) | 2024.09.28 |
KANS 3기 3주차 두번째 (0) | 2024.09.18 |