5주차 - LoadBalancer(MetalLB)
Last updated
Last updated
0. 목적성
온프레미스에서 사용할 수 있는 LoadBalancer인 MetalLB 의 네트워크 흐름을 이해합니다.
온프레미스에서는 로드밸런서 파드를 생성해서 직접 부하분산을 진행합니다.
aws 나 클라우드 환경에서는 managed service인 로드밸런서를 사용해서 부하분산을 하게 됩니다.
NodePort 방식
외부 로드 밸런서에서 1번 부하분산되고 그 이후 추가로 서비스에서 부하분산 과정이 수행되어서 성능이 저하됩니다.
Pod 방식
외부 로드밸런서가 목적지 pod로 직접 부하분산을 하게되어 1번의 부하분산과정으로 수행됩니다.
온프레미스 환경에서 LoadBalancer 서비스를 구현하게 됩니다.
쿠버네티스 데몬셋으로 스피커 파드 생성으로 ExternalIP를 전파합니다.
이 덕분에, 외부에서 쿠버네티스 서비스로 접근이 가능해집니다.
ARP 테이블 모드와 BGP 모드가 있습니다.
SVC1 IP 로 접속하게 되면, Node에 배포된 speaker 파드를 통해 iptables로 각 파드로 DNAT이 됩니다.
리더 파드(호스트네트워크사용)를 선출해서 리더 파드가 있는 노드로만 트래픽 인입이 됩니다.
하지만, 다른 파드가 리더로 선출되는데 10초가 소요됩니다 하지만, 그 사이에 장애가 발생할 수 있습니다.
// 1. KIND를위한EC2 생성
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-5w.yaml
aws cloudformation deploy --template-file kans-5w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
// 2. K8S 클러스터 생성 및 기본 툴들 설치
cat <<EOT> kind-svc-2w.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
"InPlacePodVerticalScaling": true #실행 중인 파드의 리소스 요청 및 제한을 변경할 수 있게 합니다.
"MultiCIDRServiceAllocator": true #서비스에 대해 여러 CIDR 블록을 사용할 수 있게 합니다.
nodes:
- role: control-plane
labels:
mynode: control-plane
topology.kubernetes.io/zone: ap-northeast-2a
extraPortMappings: #컨테이너 포트를 호스트 포트에 매핑하여 클러스터 외부에서 서비스에 접근할 수 있도록 합니다.
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
- containerPort: 30003
hostPort: 30003
- containerPort: 30004
hostPort: 30004
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs: #API 서버에 추가 인수를 제공
runtime-config: api/all=true #모든 API 버전을 활성화
controllerManager:
extraArgs:
bind-address: 0.0.0.0
etcd:
local:
extraArgs:
listen-metrics-urls: http://0.0.0.0:2381
scheduler:
extraArgs:
bind-address: 0.0.0.0
- |
kind: KubeProxyConfiguration
metricsBindAddress: 0.0.0.0
- role: worker
labels:
mynode: worker1
topology.kubernetes.io/zone: ap-northeast-2a
- role: worker
labels:
mynode: worker2
topology.kubernetes.io/zone: ap-northeast-2b
- role: worker
labels:
mynode: worker3
topology.kubernetes.io/zone: ap-northeast-2c
networking:
podSubnet: 10.10.0.0/16 #파드 IP를 위한 CIDR 범위를 정의합니다. 파드는 이 범위에서 IP를 할당받습니다.
serviceSubnet: 10.200.1.0/24 #서비스 IP를 위한 CIDR 범위를 정의합니다. 서비스는 이 범위에서 IP를 할당받습니다.
EOT
# k8s 클러스터 설치
kind create cluster --config kind-svc-2w.yaml --name myk8s --image kindest/node:v1.31.0
# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils ipset ipvsadm nfacct tcpdump ngrep iputils-ping arping git vim arp-scan -y'
for i in worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils ipset ipvsadm nfacct tcpdump ngrep iputils-ping arping -y'; echo; done
## mypc 컨테이너를 기동해 둡니다. (src ip 가 될 컨테이너입니다.)
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity
각 노드의 IP 정보를 확인할 수 있습니다.
// 1. pod를 생성합니다.
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
// 1. Kubernetes manifests 로 MetalLB 설치
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/refs/heads/main/config/manifests/metallb-native-prometheus.yaml
// 2. metallb crd 확인
bfdprofiles.metallb.io 2024-10-05T12:02:54Z
// metallb 네트워크 장애 custom Resource DEFINITION
bgpadvertisements.metallb.io 2024-10-05T12:02:54Z
// bgp 정의 설정 (metallb가 클러스터의 ip를 bgp를 통해 광고하는데 필요한 정보입니다.)
bgppeers.metallb.io 2024-10-05T12:02:54Z
// bgp 피어 설정 (bgp를 사용해서 다른 라우터와 연결하기 위해 설정하는 피어 정보를 포함)
communities.metallb.io 2024-10-05T12:02:54Z
ipaddresspools.metallb.io 2024-10-05T12:02:54Z
// MetalLB 가 할당할 수 있는 IP 주소 풀을 설정 합니다. (LB에서 설정할 수 있는 IP 주소 범위 설정)
l2advertisements.metallb.io 2024-10-05T12:02:54Z
servicel2statuses.metallb.io 2024-10-05T12:02:54Z
## metallb 데몬셋으로 배포되는 스피커 파드는 hostNetwork 를 사용함
kubectl get ds -n metallb-system -o yaml
name: speaker
ports:
- containerPort: 7472
name: monitoring
protocol: TCP
- containerPort: 7946
name: memberlist-tcp
protocol: TCP
- containerPort: 7946
name: memberlist-udp
protocol: UDP
readinessProbe:
failureThreshold: 3
httpGet:
path: /metrics
port: monitoring
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources: {}
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_RAW
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/ml_secret_key
name: memberlist
readOnly: true
- mountPath: /etc/metallb
name: metallb-excludel2
readOnly: true
dnsPolicy: ClusterFirst
hostNetwork: true
// 서비스(External-IP) 대역을 노드가 속한 eth0의 대역이 아니여도 상관없다!
// GW 역할의 라우터에서 노드들로 라우팅 경로 지정합니다
# IPAddressPool 생성 : LoadBalancer External IP로 사용할 IP 대역
## MetalLB는 서비스를 위한 외부 IP 주소를 관리하고, 서비스가 생성될 때 해당 IP 주소를 동적으로 할당할 수 있습니다.
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
# L2Advertisement 생성 : 설정한 IPpool을 기반으로 Layer2 모드로 LoadBalancer IP 사용 허용
## Kubernetes 클러스터 내의 서비스가 외부 네트워크에 IP 주소를 광고하는 방식을 정의
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-advertise
namespace: metallb-system
spec:
ipAddressPools:
- my-ippool
EOF
# LoadBalancer 타입의 서비스 생성 확인 : EXTERNAL-IP가 서비스 마다 할당되며, 실습 환경에 따라 다를 수 있음
## LoadBalancer 타입의 서비스는 NodePort 와 ClusterIP 를 포함함 - 'allocateLoadBalancerNodePorts : true' 기본값
## ExternalIP 로 접속 시 사용하는 포트는 PORT(S) 의 앞에 있는 값을 사용 (아래의 경우는 TCP 80 임)
## 만약 노드의 IP에 NodePort 로 접속 시 사용하는 포트는 PORT(S) 의 뒤에 있는 값을 사용 (아래는 30485 임)
kubectl get service,ep
# LoadBalancer 타입은 기본적으로 NodePort를 포함 사용. NodePort는 ClusterIP를 포함 사용.
## 클라우드사업자 LB Type이나 온프레미스환경 HW LB Type 경우 LB 사용 시 NodePort 미사용 설정 가능
kubectl describe svc svc1
## 아래 처럼 LB VIP 별로 이던 speaker 배포된 노드가 리더 역할을 하는지 확인 가능
kubectl describe svc | grep Events: -A5
external ip 할당 확인할 수 있습니다.
arp table에도 externalIP 할당되어있는것을 확인할 수 있습니다.
// EXTERNAL IP 를 통해 ETAL LB 스피커가 잘 동작하는지 확인해봅시다.
// 1. PING을 던져봅시다.
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc ping -c 1 -w 1 -W 1 $i; done
# arp scan 해두기
// 2. docker control plane 의 arp scan을 해보면, lb에서 mac address로 매칭되는것을 볼 수 있습니다.
docker exec -it myk8s-control-plane arp-scan --interfac=eth0 --localnet
// 부하분산 접속 확인
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"
## 지속적으로 반복 접속
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"
# 장애 재연
## 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)를 중지
docker stop <svc1 번 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)> --signal 9
docker stop myk8s-worker --signal 9
## 지속적으로 반복 접속 상태 모니터링
### 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"
# mypc/mypc2 에서 arp 테이블 정보 확인 >> SVC IP별로 리더 파드(스피커) 역할의 노드를 확인!
docker exec -it mypc ip -c neigh | sort
kubectl get node -owide # mac 주소에 매칭되는 IP(노드) 찾기
잠시동안, 통신이 안되는것을 확인할 수 있습니다.
다시 리더가 존재하는 노드를 생성했을때, 원복된것을 확인할 수 있었다.
Speaker 파드가 BGP 로 서비스 정보를 전파합니다. 외부에서 ECMP 라우팅으로 부하분산 접속합니다.
클라이언트 IP 보존이 가능합니다.
BGP 모드는 외부의 라우터가 트래픽을 분산해서 효율적입니다.