5주차 공유 시작하겠습니다.
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 등)과 호환되지 않습니다 - 링크
- 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 설치 - 링크
간단하게 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 |