Service-(3)LoadBalancer

Gyullbb·2024년 10월 4일
0

K8S

목록 보기
8/13

LoadBalancer

LoadBalancer 타입 서비스는 크게 2가지 방식으로 동작한다.
각 방식에 대하여 살펴보자.

LoadBalancer - Nodeport - Pod

외부 클라이언트가 LoadBalancer로 접속을 하면, LoadBalancer는 노드의 NodePort를 목적지 포트로 트래픽을 전송한다. 이후 노드의 iptables 정보로 Pod에 랜덤 부하분산을 통해 전달된다.

LoadBalancer Controller (LoadBalancer - Pod)

LoadBalancer Controller에서 Kubernetes Endpoints API를 통해 특정 서비스에 연결된 Pod들의 IP주소를 수집한 후, LB에서 수집된 Pod들에 직접 접속한다.

LoadBalancer Controller의 대표 예시로는 AWS LoadBalancer Controller가 있다.
AWS LoadBalancer Controller에서 Target Group Binding CRD를 배포하여 Service Endpoint 주소를 수집하는 코드를 간략하게 살펴보자.

AWS LoadBalancer Controller

https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/pkg/targetgroupbinding/resource_manager.go

Service에서 바라보는 Endpoint 슬라이스를 반환한다.

package targetgroupbinding
...
func (m *defaultResourceManager) reconcileWithIPTargetType(ctx context.Context, tgb *elbv2api.TargetGroupBinding) error {
	svcKey := buildServiceReferenceKey(tgb, tgb.Spec.ServiceRef)
...
	var endpoints []backend.PodEndpoint
	var containsPotentialReadyEndpoints bool
	var err error
  // Service에서 바라보는 Endpoint 슬라이스를 반환한다. ResolvePodEndpoints함수는 pkg/backend/endpoint_resolver.go에 존재한다.
	endpoints, containsPotentialReadyEndpoints, err = m.endpointResolver.ResolvePodEndpoints(ctx, svcKey, tgb.Spec.ServiceRef.Port, resolveOpts...) 
  ...
  if err := m.networkingManager.ReconcileForPodEndpoints(ctx, tgb, endpoints); err != nil {
		m.eventRecorder.Event(tgb, corev1.EventTypeWarning, k8s.TargetGroupBindingEventReasonFailedNetworkReconcile, err.Error())
		needNetworkingRequeue = true
	}
  ...
  //TargetGroup에 등록되지 않은 Endpoint를 AWS TargetGroup에 등록한다.
  if len(unmatchedEndpoints) > 0 {
		if err := m.registerPodEndpoints(ctx, tgARN, vpcID, unmatchedEndpoints); err != nil {
			return err
		}
	}
func (r *defaultEndpointResolver) ResolvePodEndpoints(ctx context.Context, svcKey types.NamespacedName, port intstr.IntOrString, opts ...EndpointResolveOption) ([]PodEndpoint, bool, error) {
	resolveOpts := defaultEndpointResolveOptions()
	resolveOpts.ApplyOptions(opts)

	_, svcPort, err := r.findServiceAndServicePort(ctx, svcKey, port)
	if err != nil {
		return nil, false, err
	}
	endpointsDataList, err := r.computeServiceEndpointsData(ctx, svcKey)
	if err != nil {
		return nil, false, err
	}
	return r.resolvePodEndpointsWithEndpointsData(ctx, svcKey, svcPort, endpointsDataList, resolveOpts.PodReadinessGates)
}

...
// 실제 ready상태의 Pod Endpoint를 반환한다.
func (r *defaultEndpointResolver) resolvePodEndpointsWithEndpointsData(ctx context.Context, svcKey types.NamespacedName, svcPort corev1.ServicePort, endpointsDataList []EndpointsData, podReadinessGates []corev1.PodConditionType) ([]PodEndpoint, bool, error) {
...

반환된 Endpoint를 AWS TargetGroup에 등록한다.

func (m *defaultResourceManager) registerPodEndpoints(ctx context.Context, tgARN, tgVpcID string, endpoints []backend.PodEndpoint) error {
	vpcID := m.vpcID
	// Target group is in a different VPC from the cluster's VPC
	if tgVpcID != "" && tgVpcID != m.vpcID {
		vpcID = tgVpcID
		m.logger.Info("registering endpoints using the targetGroup's vpcID", tgVpcID,
			"which is different from the cluster's vpcID", m.vpcID)
	}
  ...

	sdkTargets := make([]elbv2types.TargetDescription, 0, len(endpoints))
	for _, endpoint := range endpoints {
		target := elbv2types.TargetDescription{
			Id:   awssdk.String(endpoint.IP),
			Port: awssdk.Int32(endpoint.Port),
		}
		podIP, err := netip.ParseAddr(endpoint.IP)
		if err != nil {
			return err
		}
		if !networking.IsIPWithinCIDRs(podIP, vpcCIDRs) {
			target.AvailabilityZone = awssdk.String("all")
		}
		sdkTargets = append(sdkTargets, target)
	}
  //Endpoint를 AWS TargetGroup에 등록한다.
	return m.targetsManager.RegisterTargets(ctx, tgARN, sdkTargets)
}

AWS LoadBalancer는 TargetGroup을 대상으로 트래픽을 분산하기 때문에 Pod IP에 직접적으로 트래픽 분산이 가능하다.

MetalLB

온프레미스 환경에서 대표적으로 사용되는 LoadBalancer 서비스인 MetalLB에 대해 알아본다.
MetalLB는 Layer2 모드와 BGP 모드로 동작한다.

Layer2 모드에 대해 알아본다.

Layer2 Mode

MetalLB를 설치하면 모든 노드에 스피커 파드가 Daemonset으로 뜨게되는데, LoadBalancer 서비스 리소스 생성 시 MetalLB 스피커 파드 중 리더 스피커 파드가 선출되어 연결된다.

리더 스피커 파드는 ARP 메시지로 LoadBalancer 서비스의 External IP를 전파한다.

클라이언트가 LoadBalancer 서비스로 접속을 시도하면 연결된 리더 스피커 파드가 뜬 노드로 트래픽이 들어오고, 해당 노드의 Iptables룰을 통해 각 Pod들에 트래픽이 전달된다.

클라이언트로부터 Pod까지 트래픽 전달 과정 중 DNAT는 두 번 일어난다.
클라이언트에서 LB로 전달된 트래픽이 LB에서 Node로 향할 때 NodeIP:Port로 DNAT되고, Node에서 Iptables룰에 의해 Pod로 향할 때 PodIP:Port로 DNAT된다.

MetalLB 리소스 확인

MetalLB와 L2 통신 관련 리소스 설치 후 설치된 리소스를 확인해본다.

# kubectl get crd | grep metallb  
bfdprofiles.metallb.io                      2024-10-03T07:15:10Z
bgpadvertisements.metallb.io                2024-10-03T07:15:10Z
bgppeers.metallb.io                         2024-10-03T07:15:10Z
communities.metallb.io                      2024-10-03T07:15:10Z
ipaddresspools.metallb.io                   2024-10-03T07:15:10Z
l2advertisements.metallb.io                 2024-10-03T07:15:10Z
servicel2statuses.metallb.io                2024-10-03T07:15:10Z
CRD Name설명
bfdprofiles.metallb.ioBFD (Bidirectional Forwarding Detection) 프로파일을 정의하여 BGP 세션의 장애를 감지하는 데 사용된다.
bgpadvertisements.metallb.ioBGP(Border Gateway Protocol) 광고를 관리하는 CRD로, MetalLB가 클러스터의 IP 주소를 광고할 때 사용된다.
bgppeers.metallb.ioMetalLB가 연결할 BGP 피어를 정의한다. 이 CRD를 통해 다른 BGP 라우터와의 관계를 설정할 수 있다.
communities.metallb.ioBGP 커뮤니티를 정의하는 CRD로, 특정 라우팅 정책을 구현하기 위해 BGP 광고에 추가할 수 있는 커뮤니티이다.
ipaddresspools.metallb.ioMetalLB가 사용할 IP 주소 풀을 정의한다. 이 풀에서 IP 주소를 할당하여 서비스를 제공한다.
l2advertisements.metallb.ioL2 모드에서 IP 주소를 광고하기 위한 CRD로, L2 브로드캐스트를 통해 IP 주소를 광고한다.
servicel2statuses.metallb.ioL2 모드에서 서비스의 상태를 추적하는 CRD로, 서비스의 상태와 관련된 메타데이터를 제공한다.

Speacker Pod가 데몬셋으로 뜨고, MetalLB Controller가 하나 생성된다.
이 때, Speaker Pod는 HostNS와 동일한 NS를 사용한다.

# kubectl get ds,deploy -n metallb-system
NAME                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/speaker   4         4         4       4            4           kubernetes.io/os=linux   32h

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/controller   1/1     1            1           32h

# kubectl get pod -o wide -n metallb-system | grep speaker 
speaker-2mfpd                 2/2     Running   0          32h   172.18.0.4   myk8s-worker2         <none>           <none>
speaker-bvm4p                 2/2     Running   0          32h   172.18.0.3   myk8s-worker3         <none>           <none>
speaker-f5nhm                 2/2     Running   0          32h   172.18.0.5   myk8s-control-plane   <none>           <none>
speaker-sntgs                 2/2     Running   0          32h   172.18.0.2   myk8s-worker          <none>           <none>

# kubectl get nodes -o wide                               
NAME                  STATUS   ROLES           AGE    VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION    CONTAINER-RUNTIME
myk8s-control-plane   Ready    control-plane   3d3h   v1.31.0   172.18.0.5    <none>        Debian GNU/Linux 12 (bookworm)   6.4.16-linuxkit   containerd://1.7.18
myk8s-worker          Ready    <none>          3d3h   v1.31.0   172.18.0.2    <none>        Debian GNU/Linux 12 (bookworm)   6.4.16-linuxkit   containerd://1.7.18
myk8s-worker2         Ready    <none>          3d3h   v1.31.0   172.18.0.4    <none>        Debian GNU/Linux 12 (bookworm)   6.4.16-linuxkit   containerd://1.7.18
myk8s-worker3         Ready    <none>          3d3h   v1.31.0   172.18.0.3    <none>        Debian GNU/Linux 12 (bookworm)   6.4.16-linuxkit   containerd://1.7.18

ipaddresspools.metallb.io와 l2advertisements.metallb.io를 배포하여 서비스의 IPAddressPool을 관리하고, 해당 Pool을 기반으로 Layer2모드로 LoadBalancer 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

cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: my-l2-advertise
  namespace: metallb-system
spec:
  ipAddressPools:
  - my-ippool
EOF

MetalLB 리소스와 서비스

각 서비스 별 Leader Speacker Pod 확인

서비스의 Event로 각 서비스의 리더 스피커 파드가 어떤 파드인지 확인한다.

# kubectl describe svc svc1 | grep Events: -A5
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   39s   metallb-controller  Assigned IP ["172.18.255.200"]
  Normal  nodeAssigned  39s   metallb-speaker     announcing from node "myk8s-worker" with protocol "layer2"

# kubectl describe svc svc2 | grep Events: -A5
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   51s   metallb-controller  Assigned IP ["172.18.255.201"]
  Normal  nodeAssigned  51s   metallb-speaker     announcing from node "myk8s-worker" with protocol "layer2"

# kubectl describe svc svc3 | grep Events: -A5
Events:
  Type    Reason        Age   From                Message
  ----    ------        ----  ----                -------
  Normal  IPAllocated   55s   metallb-controller  Assigned IP ["172.18.255.202"]
  Normal  nodeAssigned  55s   metallb-speaker     announcing from node "myk8s-worker3" with protocol "layer2"

서비스를 생성함과 동시에 Node에서 arp tcpdump를 확인하면, 리더 스피커 파드가 각 Node에 ARP 통신을 보내서 자신이 리더 스피커 파드임을 알리는 것을 확인할 수 있다.

# docker exec -it myk8s-worker tcpdump -i eth0 arp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
21:28:14.714811 ARP, Request who-has myk8s-worker3.kind tell myk8s-worker, length 28
21:28:14.715200 ARP, Reply myk8s-worker3.kind is-at 02:42:ac:12:00:03 (oui Unknown), length 28
21:28:19.834218 ARP, Request who-has myk8s-worker tell myk8s-worker2.kind, length 28
21:28:19.834233 ARP, Reply myk8s-worker is-at 02:42:ac:12:00:02 (oui Unknown), length 28
21:28:29.579059 ARP, Request who-has 172.18.255.200 (Broadcast) tell 172.18.255.200, length 46
21:28:29.579226 ARP, Reply 172.18.255.200 is-at 02:42:ac:12:00:02 (oui Unknown), length 46
...
21:28:29.610111 ARP, Request who-has 172.18.255.201 (Broadcast) tell 172.18.255.201, length 46
21:28:29.610142 ARP, Reply 172.18.255.201 is-at 02:42:ac:12:00:02 (oui Unknown), length 46
...
21:28:29.647648 ARP, Request who-has 172.18.255.202 (Broadcast) tell 172.18.255.202, length 46
21:28:29.647748 ARP, Reply 172.18.255.202 is-at 02:42:ac:12:00:03 (oui Unknown), length 46

mypc에서 arping을 통해 SVC EXTERNAL-IP를 담당하는 리더 스피커 파드를 찾을 수 있다.

# docker exec -it mypc arping -I eth0 -f -c 1 $SVC1EXIP
ARPING 172.18.255.200 from 172.18.0.6 eth0
Unicast reply from 172.18.255.200 [02:42:AC:12:00:02]  1.858ms # 172.18.255.200에서 응답을 받음
Sent 1 probes (1 broadcast(s)) # ARP 요청을 브로드캐스트 방식으로 전송함
Received 1 response(s) # 응답을 수신

# docker exec -it mypc arping -I eth0 -f -c 1 $SVC2EXIP
ARPING 172.18.255.201 from 172.18.0.6 eth0
Unicast reply from 172.18.255.201 [02:42:AC:12:00:02]  4.066ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)

# docker exec -it mypc arping -I eth0 -f -c 1 $SVC3EXIP
ARPING 172.18.255.202 from 172.18.0.6 eth0
Unicast reply from 172.18.255.202 [02:42:AC:12:00:03]  1.900ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)

mypc에서 arp 테이블 정보를 확인하여 SVC별로 리더 스피커 역할을 하는 노드를 확인한다.

# docker exec -it mypc ip -c neigh | sort
172.18.0.1 dev eth0 lladdr 02:42:58:36:2d:23 STALE
172.18.0.2 dev eth0 lladdr 02:42:ac:12:00:02 STALE
172.18.0.3 dev eth0 lladdr 02:42:ac:12:00:03 STALE
172.18.0.4 dev eth0 lladdr 02:42:ac:12:00:04 STALE
172.18.0.5 dev eth0 lladdr 02:42:ac:12:00:05 STALE
172.18.255.200 dev eth0 lladdr 02:42:ac:12:00:02 STALE # -> myk8s-worker
172.18.255.201 dev eth0 lladdr 02:42:ac:12:00:02 STALE # -> myk8s-worker
172.18.255.202 dev eth0 lladdr 02:42:ac:12:00:03 STALE # -> myk8s-worker3

서비스 접속

mypc에서 각 서비스를 호출한 후 응답을 확인해본다.

# or 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

>> Access Service External-IP : 172.18.255.200 <<
Hostname: webpod1
RemoteAddr: 10.10.3.1:32676
Host: 172.18.255.200

>> Access Service External-IP : 172.18.255.201 <<
Hostname: webpod2
RemoteAddr: 172.18.0.2:47331
Host: 172.18.255.201

>> Access Service External-IP : 172.18.255.202 <<
Hostname: webpod1
RemoteAddr: 172.18.0.3:53125
Host: 172.18.255.202

arp 테이블에 등록된 대로 External-IP로 트래픽이 들어오면(Access Service External-IP), 해당 트래픽을 연결된 리더 스피커 파드가 있는 노드로 보내는 것을 확인할 수 있다.

각 서비스를 호출하여 부하분산을 확인해본다.

# docker exec mypc zsh -c  "for i in {1..1000}; do curl -s --connect-timeout 1 $SVC1EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
    504 Hostname: webpod1
    496 Hostname: webpod2

# docker exec mypc zsh -c  "for i in {1..1000}; do curl -s --connect-timeout 1 $SVC2EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
    515 Hostname: webpod1
    485 Hostname: webpod2

# docker exec mypc zsh -c  "for i in {1..1000}; do curl -s --connect-timeout 1 $SVC3EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
    510 Hostname: webpod1
    490 Hostname: webpod2

거의 균일하게 부하분산 되는 것을 확인할 수 있다.

iptables 확인

myk8s-worker에서 iptables 규칙을 확인해본다.

  • PREROUTING 체인 : 클러스터 내부 트래픽이 Kubernetes Service iptables에 의해 처리되도록 하는 역할.
# docker exec -it myk8s-worker bash

# iptables -t nat -S PREROUTING
-P PREROUTING ACCEPT
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -d 192.168.65.254/32 -j DOCKER_OUTPUT
  • KUBE-SERVICES 체인: SVC의 ExternalIP 172.18.255.200에 대한 요청을 KUBE-EXT-DLGPAL4ZCYSJ7UPR 체인이 처리함.

  • KUBE-EXT-DLGPAL4ZCYSJ7UPR 체인 :
    - 외부 목적지로 향하는 트래픽에 대해 MARK 처리하여, 클러스터 외부로 패킷이 빠져나갈 때 SNAT 되도록 함.
    - 그 외 트래픽을 KUBE-SVC-DLGPAL4ZCYSJ7UPR로 전달함.

# SVC1EXIP=172.18.255.200

# iptables -t nat -S KUBE-SERVICES |grep $SVC1EXIP
-A KUBE-SERVICES -d 172.18.255.200/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
-N 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
-N KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
  • KUBE-SVC-DLGPAL4ZCYSJ7UPR 체인 :
    - 10.10.0.0/16 source가 아닌 패킷(외부 패킷)이 Cluster IP:80으로 들어올 경우 KUBE-MARK-MASQ 체인으로 mark 설정을 해서, 클러스터 외부로 패킷이 빠져나갈 때 SNAT 되도록 함.
    - probability에 따라 KUBE-SEP-YYY 체인으로 패킷을 보냄.
# iptables -t nat -S KUBE-SVC-DLGPAL4ZCYSJ7UPR
-N KUBE-SVC-DLGPAL4ZCYSJ7UPR
-A KUBE-SVC-DLGPAL4ZCYSJ7UPR ! -s 10.10.0.0/16 -d 10.200.1.217/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.5:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-NZKLDL653RQ74MZI
-A KUBE-SVC-DLGPAL4ZCYSJ7UPR -m comment --comment "default/svc1:svc1-webport -> 10.10.3.3:80" -j KUBE-SEP-L6SWHLHTULTEJGNO

# iptables -t nat -S KUBE-SEP-NZKLDL653RQ74MZI
-N KUBE-SEP-NZKLDL653RQ74MZI
-A KUBE-SEP-NZKLDL653RQ74MZI -s 10.10.1.5/32 -m comment --comment "default/svc1:svc1-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-NZKLDL653RQ74MZI -p tcp -m comment --comment "default/svc1:svc1-webport" -m tcp -j DNAT --to-destination 10.10.1.5:80

# iptables -t nat -S KUBE-SEP-L6SWHLHTULTEJGNO
-N KUBE-SEP-L6SWHLHTULTEJGNO
-A KUBE-SEP-L6SWHLHTULTEJGNO -s 10.10.3.3/32 -m comment --comment "default/svc1:svc1-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-L6SWHLHTULTEJGNO -p tcp -m comment --comment "default/svc1:svc1-webport" -m tcp -j DNAT --to-destination 10.10.3.3:80
  • POSTROUTING 체인 : 패킷이 NIC를 빠져나가기 전에 적용되는 규칙

  • KUBE-POSTROUTING 체인 :
    - 0X4000 마킹이 되어있지 않은 경우 RETRUN되어 SNAT 규칙을 통해 라우팅되지 않음.

# iptables -t nat -S POSTROUTING
-P POSTROUTING ACCEPT
-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

# iptables -t nat -S KUBE-POSTROUTING
-N 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

Failover

MetalLB L2 Mode의 경우, 리더 스피커 파드가 있는 노드에 장애가 발생하면, 남아있는 스피커 파드들이 해당 노드의 장애를 인식한다.
장애가 발생한 스피커 파드와 연결된 서비스의 ExternalIP의 새로운 리더 스피커 파드를 선출하게 되고, 리더가 선출이 되면 GARP로 서비스 ExternalIP를 광고한다.

장애 인지 시간과 새로운 리더 스피커 파드 선출 후 광고까지의 시간이 짧게는 20초, 길게는 1분이 걸려, 장애 지속 시간이 길어진다는 단점이 있다.

Failover과정에 대해 테스트를 진행해본다.

장애 상황 발생

# docker stop myk8s-worker --signal 9

myk8s-worker

SVC1 호출 상태 확인
따로 떠있는 서비스가 많지 않은 상황에서도 장애 발생 이후 정상화까지 약 30초의 시간이 소요되는 것을 확인할 수 있다.

# 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: 10.10.3.1:33831
2024-10-05 14:27:21

Hostname: webpod2
RemoteAddr: 172.18.0.2:25693
2024-10-05 14:27:22

# 장애 발생 시작
2024-10-05 14:27:24

2024-10-05 14:27:26

...

Hostname: webpod2
RemoteAddr: 10.10.1.1:54046
2024-10-05 14:27:44

2024-10-05 14:27:46
...

# 장애 복구 완료
Hostname: webpod2
RemoteAddr: 10.10.1.1:5946
2024-10-05 14:27:55

Hostname: webpod2
RemoteAddr: 10.10.1.1:18744
2024-10-05 14:27:56

새로운 리더 스피커 파드 선정 확인
새로운 리더 스피커 파드가 서비스의 External IP를 소유하고 있음을 확인할 수 있다.

172.18.0.1 dev eth0 lladdr 02:42:58:36:2d:23 STALE
172.18.0.2 dev eth0 lladdr 02:42:ac:12:00:02 STALE
172.18.0.3 dev eth0 lladdr 02:42:ac:12:00:03 STALE
172.18.0.4 dev eth0 lladdr 02:42:ac:12:00:04 STALE
172.18.0.5 dev eth0 lladdr 02:42:ac:12:00:05 STALE
172.18.255.200 dev eth0 lladdr 02:42:ac:12:00:04 REACHABLE # myk8s-worker -> myk8s-worker2
172.18.255.201 dev eth0 lladdr 02:42:ac:12:00:03 STALE # myk8s-worker -> myk8s-worker3
172.18.255.202 dev eth0 lladdr 02:42:ac:12:00:03 STALE # myk8s-worker3

장애 노드 정상화 이후 확인
장애 노드를 정상화하고 나면, 리더 스피커 노드가 원복된다.

# docker start myk8s-worker

# kubectl get nodes
NAME                  STATUS   ROLES           AGE    VERSION
myk8s-control-plane   Ready    control-plane   4d2h   v1.31.0
myk8s-worker          Ready    <none>          4d2h   v1.31.0
myk8s-worker2         Ready    <none>          4d2h   v1.31.0
myk8s-worker3         Ready    <none>          4d2h   v1.31.0

# docker exec -it mypc ip -c neigh | sort
172.18.0.1 dev eth0 lladdr 02:42:58:36:2d:23 STALE
172.18.0.2 dev eth0 lladdr 02:42:ac:12:00:02 STALE
172.18.0.3 dev eth0 lladdr 02:42:ac:12:00:03 STALE
172.18.0.4 dev eth0 lladdr 02:42:ac:12:00:04 STALE
172.18.0.5 dev eth0 lladdr 02:42:ac:12:00:05 STALE
172.18.255.200 dev eth0 lladdr 02:42:ac:12:00:02 REACHABLE # myk8s-worker2 -> myk8s-worker
172.18.255.201 dev eth0 lladdr 02:42:ac:12:00:02 STALE # myk8s-worker3 -> myk8s-worker
172.18.255.202 dev eth0 lladdr 02:42:ac:12:00:03 STALE # myk8s-worker3

0개의 댓글