본문 바로가기

study/KANS 3기

KANS 3기 5주차 첫번째

5주차 공유 시작하겠습니다.

 

MetalLB - 공홈 , Github

 

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