5주차 (LoadBalancer(MetalLB)
LoadBalancer란?
Kubernetes에서 LoadBalancer 서비스 유형은 외부 클라이언트가 클러스터 내에서 실행 중인 애플리케이션에 접근할 수 있도록 외부 로드 밸런서를 사용하여 서비스를 노출하는 역할을 합니다. 이 기능은 서비스로 들어오는 트래픽을 적절한 노드 및 파드로 전달하는 데 중요합니다.
주요 개념
- 외부 노출
- LoadBalancer 서비스 유형은 Kubernetes 서비스가 외부에서 접근 가능하도록 설계되어 있습니다. Kubernetes 자체는 로드 밸런싱 기능을 제공하지 않지만, 외부 클라우드 공급자와의 통합을 통해 이 기능을 활용할 수 있습니다.
- 클라우드 공급자 의존성
- Kubernetes는 외부 트래픽을 클러스터 내부의 서비스로 전달하기 위해 외부 로드 밸런서를 필요로 합니다. AWS, GCP, Azure와 같은 클라우드 환경은 자체 로드 밸런서를 제공하며, Kubernetes는 이를 자동으로 설정하여 트래픽을 처리합니다.
- NodePort 설정
- LoadBalancer 서비스를 생성하면 Kubernetes는 먼저 NodePort 유형의 서비스를 프로비저닝합니다. 이 과정에서 클러스터 내 각 노드에 열려 있는 포트가 할당되고, 이 포트를 통해 외부 트래픽이 적절한 서비스로 전달됩니다. 이후 클라우드 공급자의 로드 밸런서는 이 NodePort로 트래픽을 라우팅하도록 설정됩니다.
- 트래픽 라우팅 및 DNAT
- 트래픽 흐름을 보면, 외부에서 들어오는 트래픽은 로드 밸런서를 통해 분산되어 Kubernetes 노드에 도달합니다. 노드에 도착한 트래픽은 iptables 규칙을 통해 목적지 주소 변환(DNAT) 이 이루어지며, 올바른 파드로 전달됩니다.
- ExternalTrafficPolicy 설정
- externalTrafficPolicy 옵션은 로드 밸런서가 트래픽을 처리하는 방식을 결정하는데, 이 설정을 Local로 지정하면 해당 트래픽이 도착한 노드에 있는 파드로만 트래픽을 전달하게 됩니다. 이를 통해 불필요한 네트워크 홉을 줄일 수 있지만, 노드 및 파드의 가용성 관리가 중요합니다.
- 헬스 체크
- 로드 밸런서에는 보통 헬스 체크 기능이 포함되어 있어, 트래픽이 정상적으로 동작하는 노드 및 파드로만 전달되도록 합니다. 특정 노드에 건강한 파드가 없을 경우, 로드 밸런서는 그 노드로 트래픽을 보내지 않습니다.
Service - LoadBalancer의 한계
- 리소스 비효율성
- 새로운 LoadBalancer 서비스를 생성할 때마다 새로운 외부 로드 밸런서 인스턴스가 생성됩니다. 클라우드 환경에서 이 과정은 리소스를 많이 사용하고 비용 효율성이 떨어질 수 있습니다. 특히 HTTP 서비스에서 이런 비효율성이 두드러집니다.
- Ingress 컨트롤러 추천
- HTTP(S) 트래픽의 경우, Ingress 컨트롤러를 사용하는 것이 더 효율적입니다. Ingress는 TLS 종료를 처리하고 도메인 또는 경로 기반으로 트래픽을 라우팅하여 리소스를 더 효율적으로 사용합니다.
- 온프레미스 환경의 제한
- LoadBalancer 서비스는 클라우드 환경에서 원활하게 동작하지만, 온프레미스(on-premises) 환경에서는 클라우드 기반 로드 밸런서가 없기 때문에 로드 밸런싱 구현이 어렵습니다. 이런 환경에서는 MetalLB나 OpenELB 같은 솔루션을 사용하여 클라우드 없이 로드 밸런서 기능을 구현할 수 있습니다.
MetalLB ?
MetalLB는 온프레미스 환경에서 사용 가능한 서비스로, 클라우드 환경의 LoadBalancer 서비스와 비슷한 기능을 제공합니다.
MetalLB의 주요 특징
- BareMetal LoadBalancer 역할
- MetalLB는 클라우드 공급자의 외부 로드 밸런서가 없는 환경, 특히 온프레미스 또는 자체 데이터 센터에서 Kubernetes 서비스에 외부 IP를 할당하고, 외부 트래픽을 클러스터 내로 라우팅하는 기능을 제공합니다.
- External IP 전파 방식
- MetalLB는 서비스에 할당된 External IP를 전파하기 위해 ARP(IPv4) 와 NDP(IPv6), 또는 BGP와 같은 프로토콜을 사용합니다. 이 중 Layer 2(ARP/NDP)와 BGP 두 가지 모드를 지원하여 네트워크 트래픽을 적절한 노드로 라우팅합니다.
- BGP 모드
- MetalLB는 BGP(Border Gateway Protocol) 를 활용하여 외부 네트워크 장비와 연동할 수 있습니다. 이를 통해 BGP 스피커로서 클러스터의 외부 IP를 라우터에 전파하고, 트래픽이 클러스터로 올 수 있게 합니다. 그러나 일반적인 BGP 데몬과 달리, MetalLB는 외부에서 광고하는 네트워크 대역 정보를 받지 않습니다.
- CNI 호환성
- MetalLB는 일부 네트워크 플러그인(CNI)과 호환되며, 특히 Calico IPIP(BGP) 와 같이 동시에 작동할 수 있습니다. 하지만 네트워크 구성에 따라 연동 이슈가 발생할 수 있으므로, 설정 시 주의가 필요합니다.
MetalLB의 두 가지 모드
1. Layer 2 모드 (ARP/NDP)
- 동작 방식 - MetalLB는 ARP/NDP를 사용하여 해당 노드의 IP를 외부에 광고하고, 트래픽을 해당 노드로 전달합니다. 이때, 서비스의 외부 IP를 수신한 클라이언트는 ARP 요청을 통해 해당 IP와 연결된 노드를 찾게 되고, 노드로 트래픽을 전송하게 됩니다.
- 리더 스피커 - MetalLB는 여러 노드 중 하나를 리더 스피커로 지정하여 해당 노드로 트래픽을 전달합니다. 리더 노드에 장애가 발생하면 다른 스피커 노드가 리더로 선출되어 서비스 중단을 최소화합니다.
2. BGP 모드
- 동작 방식 - MetalLB는 BGP를 통해 외부 라우터에 클러스터의 IP 정보를 전파합니다. 외부 라우터는 MetalLB에서 전송하는 BGP 정보를 받아 클러스터의 서비스로 트래픽을 전달할 수 있게 됩니다.
- ECMP(등가 비용 다중 경로) - BGP 모드는 ECMP를 지원하여 트래픽을 여러 노드로 부하 분산할 수 있습니다. 이를 통해 고가용성과 확장성을 높일 수 있습니다.
- 라우터 설정 - BGP 모드를 사용할 때는 네트워크 팀과의 협력이 필요합니다. 외부 라우터에서 MetalLB가 광고하는 IP 정보를 적절히 처리할 수 있도록 설정해야 하기 때문입니다.
MetalLB의 제한 사항
1. Single-node 병목 문제
- MetalLB는 하나의 리더 스피커 노드에 모든 트래픽을 집중시키기 때문에, 리더 노드가 병목 현상을 일으킬 수 있습니다. 공식 가이드는 아니지만, 외부 라우터에서 ECMP 설정을 통해 트래픽을 분산시키는 방법이 있습니다.
2. Failover 속도
- 리더 노드에 장애가 발생할 경우, 새로운 리더 노드가 선출되기까지 시간이 다소 걸릴 수 있습니다(약 10초에서 20초). 이로 인해 짧은 시간 동안 서비스에 영향을 미칠 수 있습니다.
MetalLB 설치
Kubernetes manifests 로 설치
**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**
생성된 리소스 확인 : metallb-system 네임스페이스 생성, 파드(컨트롤러, 스피커) 생성, RBAC(서비스/파드/컨피그맵 조회 등등 권한들), SA 등
# 생성된 리소스 확인 : metallb-system 네임스페이스 생성, 파드(컨트롤러, 스피커) 생성, RBAC(서비스/파드/컨피그맵 조회 등등 권한들), SA 등
kubectl get-all -n metallb-system # kubectl krew 플러그인 get-all 설치 후 사용 가능
kubectl get all,configmap,secret,ep -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**
컨피그맵 생성 : 모드 및 서비스 대역 지정
서비스(External-IP) 대역을 노드가 속한 eth0의 대역이 아니여도 상관없다! → 다만 이 경우 GW 역할의 라우터에서 노드들로 라우팅 경로 지정 필요
kind network 중 컨테이너(노드) IP(대역) 확인 : 172.18.0.2~ 부터 할당되며, control-plane 이 꼭 172.18.0.2가 안될 수 도 있습니다.
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"]*
# (옵션) 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 # 매칭 사용 가능
서비스 생성 및 확인
서비스(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
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
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 # 매칭 사용 가능
$ kubectl describe servicel2status -n metallb-system
Name: l2-g4nn9
Namespace: metallb-system
Labels: metallb.io/node=myk8s-worker3
metallb.io/service-name=svc3
metallb.io/service-namespace=default
Annotations: <none>
API Version: metallb.io/v1beta1
Kind: ServiceL2Status
Metadata:
Creation Timestamp: 2024-10-04T06:22:40Z
Generate Name: l2-
Generation: 1
Owner References:
API Version: v1
Kind: Pod
Name: speaker-c9cxk
UID: ed331a86-4ecc-4437-b434-e8ade5a59b9c
Resource Version: 14804
UID: 31f1279e-a041-44c6-9bb0-fffb3d829efb
Spec:
Status:
Node: myk8s-worker3
Service Name: svc3
Service Namespace: default
Events: <none>
Name: l2-p2v6s
Namespace: metallb-system
Labels: metallb.io/node=myk8s-worker
metallb.io/service-name=svc1
metallb.io/service-namespace=default
Annotations: <none>
API Version: metallb.io/v1beta1
Kind: ServiceL2Status
Metadata:
Creation Timestamp: 2024-10-04T06:22:40Z
Generate Name: l2-
Generation: 1
Owner References:
API Version: v1
Kind: Pod
Name: speaker-wpz8t
UID: c3603a4d-8774-428d-aca9-fb6f2d5a4b8c
Resource Version: 14805
UID: 19bccf99-7610-48af-a8aa-3ac7ee509f6f
Spec:
Status:
Node: myk8s-worker
Service Name: svc1
Service Namespace: default
Events: <none>
Name: l2-q7f8n
Namespace: metallb-system
Labels: metallb.io/node=myk8s-worker
metallb.io/service-name=svc2
metallb.io/service-namespace=default
Annotations: <none>
API Version: metallb.io/v1beta1
Kind: ServiceL2Status
Metadata:
Creation Timestamp: 2024-10-04T06:22:40Z
Generate Name: l2-
Generation: 1
Owner References:
API Version: v1
Kind: Pod
Name: speaker-wpz8t
UID: c3603a4d-8774-428d-aca9-fb6f2d5a4b8c
Resource Version: 14806
UID: 19dc62b3-1958-414e-a90a-fb7f76594b16
Spec:
Status:
Node: myk8s-worker
Service Name: svc2
Service Namespace: default
Events: <none>
$ kubectl get servicel2status -n metallb-system -o json --watch # watch 모드
{
"apiVersion": "metallb.io/v1beta1",
"kind": "ServiceL2Status",
"metadata": {
"creationTimestamp": "2024-10-04T06:22:40Z",
"generateName": "l2-",
"generation": 1,
"labels": {
"metallb.io/node": "myk8s-worker3",
"metallb.io/service-name": "svc3",
"metallb.io/service-namespace": "default"
},
"name": "l2-g4nn9",
"namespace": "metallb-system",
"ownerReferences": [
{
"apiVersion": "v1",
"kind": "Pod",
"name": "speaker-c9cxk",
"uid": "ed331a86-4ecc-4437-b434-e8ade5a59b9c"
}
],
"resourceVersion": "14804",
"uid": "31f1279e-a041-44c6-9bb0-fffb3d829efb"
},
"spec": {},
"status": {
"node": "myk8s-worker3",
"serviceName": "svc3",
"serviceNamespace": "default"
}
}
{
"apiVersion": "metallb.io/v1beta1",
"kind": "ServiceL2Status",
"metadata": {
"creationTimestamp": "2024-10-04T06:22:40Z",
"generateName": "l2-",
"generation": 1,
"labels": {
"metallb.io/node": "myk8s-worker",
"metallb.io/service-name": "svc1",
"metallb.io/service-namespace": "default"
},
"name": "l2-p2v6s",
"namespace": "metallb-system",
"ownerReferences": [
{
"apiVersion": "v1",
"kind": "Pod",
"name": "speaker-wpz8t",
"uid": "c3603a4d-8774-428d-aca9-fb6f2d5a4b8c"
}
],
"resourceVersion": "14805",
"uid": "19bccf99-7610-48af-a8aa-3ac7ee509f6f"
},
"spec": {},
"status": {
"node": "myk8s-worker",
"serviceName": "svc1",
"serviceNamespace": "default"
}
}
{
"apiVersion": "metallb.io/v1beta1",
"kind": "ServiceL2Status",
"metadata": {
"creationTimestamp": "2024-10-04T06:22:40Z",
"generateName": "l2-",
"generation": 1,
"labels": {
"metallb.io/node": "myk8s-worker",
"metallb.io/service-name": "svc2",
"metallb.io/service-namespace": "default"
},
"name": "l2-q7f8n",
"namespace": "metallb-system",
"ownerReferences": [
{
"apiVersion": "v1",
"kind": "Pod",
"name": "speaker-wpz8t",
"uid": "c3603a4d-8774-428d-aca9-fb6f2d5a4b8c"
}
],
"resourceVersion": "14806",
"uid": "19dc62b3-1958-414e-a90a-fb7f76594b16"
},
"spec": {},
"status": {
"node": "myk8s-worker",
"serviceName": "svc2",
"serviceNamespace": "default"
}
}
서비스 접속 테스트
# 현재 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
# 부하분산 접속됨
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
Failover 테스트
리더 Speaker 파드가 존재하는 노드를 재부팅! → curl 접속 테스트 시 10~20초 정도의 장애 시간이 발생하였다 ⇒ 이후 자동 원복 되며, 원복 시 5초 정도 장애 시간 발생!
**# 사전 준비**
## 지속적으로 반복 접속
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 9
docker stop myk8s-worker --signal 9
혹은
docker stop <svc1 번 리더 Speaker 파드가 존재하는 노드(실제는 컨테이너)> --signal 15
docker stop myk8s-worker --signal 15
docker ps -a
docker ps -a | grep worker$
IPVS Proxy 모드
# 파일 작성
cat <<EOT> kind-svc-2w-ipvs.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
"InPlacePodVerticalScaling": true
"MultiCIDRServiceAllocator": true
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:
runtime-config: api/all=true
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
ipvs:
strictARP: true
- 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
serviceSubnet: 10.200.1.0/24
kubeProxyMode: "ipvs"
EOT
# k8s 클러스터 설치
kind create cluster --config kind-svc-2w-ipvs.yaml --name myk8s --image kindest/node:v1.31.0
docker ps
# 노드에 기본 툴 설치
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
# kube-proxy configmap 확인
kubectl describe cm -n kube-system kube-proxy
...
mode: ipvs
ipvs: # 아래 각각 옵션 의미 조사해보자!
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: true # MetalLB 동작을 위해서 true 설정 변경 필요
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
...
# strictARP: true는 ARP 패킷을 보다 엄격하게 처리하겠다는 설정입니다.
## IPVS 모드에서 strict ARP가 활성화되면, 노드의 인터페이스는 자신에게 할당된 IP 주소에 대해서만 ARP 응답을 보내게 됩니다.
## 이는 IPVS로 로드밸런싱할 때 ARP 패킷이 잘못된 인터페이스로 전달되는 문제를 방지합니다.
## 이 설정은 특히 클러스터 내에서 여러 노드가 동일한 IP를 갖는 VIP(Virtual IP)를 사용하는 경우 중요합니다.
# 노드 별 네트워트 정보 확인 : kube-ipvs0 네트워크 인터페이스 확인
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -c route; echo; done
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -c addr; echo; done
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -br -c addr show kube-ipvs0; echo; done
>> node myk8s-control-plane <<
kube-ipvs0 DOWN 10.200.1.1/32 10.200.1.10/32
>> node myk8s-worker <<
kube-ipvs0 DOWN 10.200.1.10/32 10.200.1.1/32
>> node myk8s-worker2 <<
kube-ipvs0 DOWN 10.200.1.1/32 10.200.1.10/32
>> node myk8s-worker3 <<
kube-ipvs0 DOWN 10.200.1.10/32 10.200.1.1/32
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -d -c addr show kube-ipvs0; echo; done
>> node myk8s-control-plane <<
11: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether 9e:1d:ca:21:c6:d1 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
inet 10.200.1.10/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.200.1.1/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
>> node myk8s-worker <<
11: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether fa:21:0a:00:a7:6c brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
inet 10.200.1.1/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.200.1.10/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
>> node myk8s-worker2 <<
11: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether ba:e9:75:56:db:00 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
inet 10.200.1.10/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.200.1.1/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
>> node myk8s-worker3 <<
11: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether a2:1d:9c:e6:ad:84 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
inet 10.200.1.1/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
inet 10.200.1.10/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
# kube-ipvs0 에 할당된 IP(기본 IP + 보조 IP들) 정보 확인
kubectl get svc,ep -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.200.1.1 <none> 443/TCP 3m8s
kube-system kube-dns ClusterIP 10.200.1.10 <none> 53/UDP,53/TCP,9153/TCP 3m7s
# ipvsadm 툴로 부하분산 되는 정보 확인 : 서비스의 IP와 서비스에 연동되어 있는 파드의 IP 를 확인
## Service IP(VIP) 처리를 ipvs 에서 담당 -> 이를 통해 iptables 에 체인/정책이 상당 수준 줄어듬
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln ; echo; done
## IPSET 확인
docker exec -it myk8s-worker ipset -h
docker exec -it myk8s-worker ipset -L
# iptables 정보 확인 : 정책 갯수를 iptables proxy 모드와 비교해보자
for i in filter nat mangle raw ; do echo ">> IPTables Type : $i <<"; docker exec -it myk8s-control-plane iptables -t $i -S ; echo; done
>> IPTables Type : filter <<
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N KUBE-FIREWALL
-N KUBE-FORWARD
-N KUBE-IPVS-FILTER
-N KUBE-IPVS-OUT-FILTER
-N KUBE-KUBELET-CANARY
-N KUBE-NODE-PORT
-N KUBE-PROXY-FIREWALL
-N KUBE-SOURCE-RANGES-FIREWALL
-A INPUT -m comment --comment "kubernetes ipvs access filter" -j KUBE-IPVS-FILTER
-A INPUT -m comment --comment "kube-proxy firewall rules" -j KUBE-PROXY-FIREWALL
-A INPUT -m comment --comment "kubernetes health check rules" -j KUBE-NODE-PORT
-A INPUT -j KUBE-FIREWALL
-A FORWARD -m comment --comment "kube-proxy firewall rules" -j KUBE-PROXY-FIREWALL
-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD
-A OUTPUT -m comment --comment "kubernetes ipvs access filter" -j KUBE-IPVS-OUT-FILTER
-A OUTPUT -j KUBE-FIREWALL
-A KUBE-FIREWALL ! -s 127.0.0.0/8 -d 127.0.0.0/8 -m comment --comment "block incoming localnet connections" -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A KUBE-IPVS-FILTER -m set --match-set KUBE-LOAD-BALANCER dst,dst -j RETURN
-A KUBE-IPVS-FILTER -m set --match-set KUBE-CLUSTER-IP dst,dst -j RETURN
-A KUBE-IPVS-FILTER -m set --match-set KUBE-EXTERNAL-IP dst,dst -j RETURN
-A KUBE-IPVS-FILTER -m set --match-set KUBE-EXTERNAL-IP-LOCAL dst,dst -j RETURN
-A KUBE-IPVS-FILTER -m set --match-set KUBE-HEALTH-CHECK-NODE-PORT dst -j RETURN
-A KUBE-IPVS-FILTER -m conntrack --ctstate NEW -m set --match-set KUBE-IPVS-IPS dst -j REJECT --reject-with icmp-port-unreachable
-A KUBE-NODE-PORT -m comment --comment "Kubernetes health check node port" -m set --match-set KUBE-HEALTH-CHECK-NODE-PORT dst -j ACCEPT
-A KUBE-SOURCE-RANGES-FIREWALL -j DROP
What's next:
Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug myk8s-control-plane
Learn more at https://docs.docker.com/go/debug-cli/
>> IPTables Type : nat <<
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER_OUTPUT
-N DOCKER_POSTROUTING
-N KIND-MASQ-AGENT
-N KUBE-KUBELET-CANARY
-N KUBE-LOAD-BALANCER
-N KUBE-MARK-MASQ
-N KUBE-NODE-PORT
-N KUBE-POSTROUTING
-N KUBE-SERVICES
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -d 192.168.65.254/32 -j DOCKER_OUTPUT
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -d 192.168.65.254/32 -j DOCKER_OUTPUT
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A POSTROUTING -d 192.168.65.254/32 -j DOCKER_POSTROUTING
-A POSTROUTING -m addrtype ! --dst-type LOCAL -m comment --comment "kind-masq-agent: ensure nat POSTROUTING directs all non-LOCAL destination traffic to our custom KIND-MASQ-AGENT chain" -j KIND-MASQ-AGENT
-A DOCKER_OUTPUT -d 192.168.65.254/32 -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.11:46763
-A DOCKER_OUTPUT -d 192.168.65.254/32 -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.11:37612
-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p tcp -m tcp --sport 46763 -j SNAT --to-source 192.168.65.254:53
-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p udp -m udp --sport 37612 -j SNAT --to-source 192.168.65.254:53
-A KIND-MASQ-AGENT -d 10.10.0.0/16 -m comment --comment "kind-masq-agent: local traffic is not subject to MASQUERADE" -j RETURN
-A KIND-MASQ-AGENT -m comment --comment "kind-masq-agent: outbound traffic is subject to MASQUERADE (must be last in chain)" -j MASQUERADE
-A KUBE-LOAD-BALANCER -j KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
-A KUBE-POSTROUTING -m comment --comment "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose" -m set --match-set KUBE-LOOP-BACK dst,dst,src -j MASQUERADE
-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
-A KUBE-SERVICES -s 127.0.0.0/8 -j RETURN
-A KUBE-SERVICES ! -s 10.10.0.0/16 -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst -j KUBE-MARK-MASQ
-A KUBE-SERVICES -m addrtype --dst-type LOCAL -j KUBE-NODE-PORT
-A KUBE-SERVICES -m set --match-set KUBE-CLUSTER-IP dst,dst -j ACCEPT
What's next:
Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug myk8s-control-plane
Learn more at https://docs.docker.com/go/debug-cli/
>> IPTables Type : mangle <<
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N KUBE-IPTABLES-HINT
-N KUBE-KUBELET-CANARY
What's next:
Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug myk8s-control-plane
Learn more at https://docs.docker.com/go/debug-cli/
>> IPTables Type : raw <<
-P PREROUTING ACCEPT
-P OUTPUT ACCEPT
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
# 각 노드 bash 접속
docker exec -it myk8s-control-plane bash
docker exec -it myk8s-worker bash
docker exec -it myk8s-worker2 bash
docker exec -it myk8s-worker3 bash
----------------------------------------
exit
----------------------------------------
# mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 직접 지정 혹은 IP 지정 없이 배포
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity
혹은
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity
docker ps
목적지(backend) 파드(Pod) 생성 : 3pod.yaml
cat <<EOT> 3pod.yaml
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
---
apiVersion: v1
kind: Pod
metadata:
name: webpod3
labels:
app: webpod
spec:
nodeName: myk8s-worker3
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
EOT
클라이언트(TestPod) 생성 : netpod.yaml
cat <<EOT> netpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: net-pod
spec:
nodeName: myk8s-control-plane
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOT
서비스(ClusterIP) 생성 : svc-clusterip.yaml
cat <<EOT> svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
ports:
- name: svc-webport
port: 9000 # 서비스 IP 에 접속 시 사용하는 포트 port 를 의미
targetPort: 80 # 타킷 targetPort 는 서비스를 통해서 목적지 파드로 접속 시 해당 파드로 접속하는 포트를 의미
selector:
app: webpod # 셀렉터 아래 app:webpod 레이블이 설정되어 있는 파드들은 해당 서비스에 연동됨
type: ClusterIP # 서비스 타입
EOT
생성 및 확인 : IPVS Proxy 모드
# 생성
kubectl apply -f 3pod.yaml,netpod.yaml,svc-clusterip.yaml
# 파드와 서비스 사용 네트워크 대역 정보 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
# 확인
kubectl get pod -owide
kubectl get svc svc-clusterip
kubectl describe svc svc-clusterip
kubectl get endpoints svc-clusterip
kubectl get endpointslices -l kubernetes.io/service-name=svc-clusterip
# 노드 별 네트워트 정보 확인 : kube-ipvs0 네트워크 인터페이스 확인
## ClusterIP 생성 시 kube-ipvs0 인터페이스에 ClusterIP 가 할당되는 것을 확인
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -c addr; echo; done
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -br -c addr show kube-ipvs0; echo; done
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -d -c addr show kube-ipvs0; echo; done
# 변수 지정
CIP=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.clusterIP}")
CPORT=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.ports[0].port}")
echo $CIP $CPORT
# ipvsadm 툴로 부하분산 되는 정보 확인
## 10.200.1.216(TCP 9000) 인입 시 3곳의 목적지로 라운드로빈(rr)로 부하분산하여 전달됨을 확인 : 모든 노드에서 동일한 IPVS 분산 설정 정보 확인
## 3곳의 목적지는 각각 서비스에 연동된 목적지 파드 3개이며, 전달 시 출발지 IP는 마스커레이딩 변환 처리
docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln -t $CIP:$CPORT ; echo; done
# ipvsadm 툴로 부하분산 되는 현재 연결 정보 확인 : 추가로 --rate 도 있음
docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT --stats
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln -t $CIP:$CPORT --stats ; echo; done
docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT --rate
for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln -t $CIP:$CPORT --rate ; echo; done
# iptables 규칙 확인 : ipset list 를 활용
docker exec -it myk8s-control-plane iptables -t nat -S | grep KUBE-CLUSTER-IP
# ipset list 정보를 확인 : KUBE-CLUSTER-IP 이름은 아래 6개의 IP:Port 조합을 지칭
# 예를 들면 ipset list 를 사용하지 않을 경우 6개의 iptables 규칙이 필요하지만, ipset 사용 시 1개의 규칙으로 가능
docker exec -it myk8s-control-plane ipset list KUBE-CLUSTER-IP
Name: KUBE-CLUSTER-IP
Type: hash:ip,port
Revision: 7
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0x6343ff52
Size in memory: 456
References: 3
Number of entries: 5
Members:
10.200.1.1,tcp:443
10.200.1.10,tcp:53
10.200.1.10,udp:53
10.200.1.245,tcp:9000
10.200.1.10,tcp:9153
IPVS 정보 확인 및 서비스 접속 확인
for i in **control-plane worker worker2 worker3**; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i **ipvsadm -Ln -t $CIP:$CPORT** ; echo; done
# 변수 지정
CIP=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.clusterIP}")
CPORT=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.ports[0].port}")
echo $CIP $CPORT
****# **컨트롤플레인** 노드에서 ipvsadm **모니터링** 실행 : ClusterIP 접속 시 아래 처럼 연결 정보 확인됨
watch -d "docker exec -it myk8s-control-plane **ipvsadm -Ln -t $CIP:$CPORT --stats; echo;** docker exec -it myk8s-control-plane **ipvsadm -Ln -t $CIP:$CPORT --rate"**
--------------------------
# 서비스 IP 변수 지정 : svc-clusterip 의 ClusterIP주소
SVC1=$(kubectl get svc svc-clusterip -o jsonpath={.spec.clusterIP})
echo $SVC1
# TCP 80,9000 포트별 접속 확인 : 출력 정보 의미 확인
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000 | grep Hostname
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000 | grep Hostname
# 서비스(ClusterIP) 부하분산 접속 확인 : 부하분산 비률 확인
kubectl exec -it net-pod -- zsh -c "for i in {1..10}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
kubectl exec -it net-pod -- zsh -c "for i in {1..1000}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
혹은
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 1; done"
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.1; done"
kubectl exec -it net-pod -- zsh -c "for i in {1..10000}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.01; done"
# 반복 접속
kubectl exec -it net-pod -- zsh -c "while true; do curl -s --connect-timeout 1 $SVC1:9000 | egrep 'Hostname|RemoteAddr|Host:'; date '+%Y-%m-%d %H:%M:%S' ; echo '--------------' ; sleep 1; done"
'K8s' 카테고리의 다른 글
AKS-eks (0) | 2024.11.02 |
---|---|
Cilium CNL (1) | 2024.10.26 |
Istio - Mode : Sidecar, Ambient (0) | 2024.10.19 |