후니의 IT인프라 사전
[KANS-5주차] service- LoadBalancer Type과 MetalLB 본문
이번 학습과정은 Service의 LoadBalancer Type과 이를 온프레미스에서 활용할 수 있는 MetalLB를 알아보겠습니다.
Load Balancer 타입
일단 Load Balancer Type은 NodePort와 동일하게 외부 클라이언트가 쿠버네티스 내부로 들어올 수 있도록 만들어주는 기능입니다. 다만 Node의 IP를 직접적으로 알지 않아도 External IP라는 외부 IP를 통해서 접근할 수 있게 일종의 프록시 역할을 대신하고 있습니다.
쿠버네티스에서는 직접적으로 LB 컴포넌트는 없기 때문에 MetalLB같은 오픈소스 소프트웨어를 사용하고 있습니다. 지난번 글에서 LoadBalancer 타입을 약간 언급했는데, 클라우드 환경에서는 편하게 LoadBalancer 서비스에서 대신하고 있습니다. 그래서 AWS에서는 실제로 ELB를 사용해 로드밸런서 타입을 오픈할 수 있으며, 자동으로 생성해줍니다. 아주 간편하죠. On Premise 환경의 경우는 이것을 별도로 생성하고 작업을 해줘야합니다.
만약 클라우드 처럼 직접적으로 VPC내에 속하는 리소스로 로드밸런서에서 직접적으로 Pod를 접근할 수 있다면 NodePort 할당을 off 할 수 있습니다. https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-nodeport-allocation
LoadBalancer Type의 pod 다이렉트 통신
참고로 AWS의 LoadBalancer Controller를 사용할 경우 어떻게 pod IP를 식별하냐하면, TargetGroupBinding이라는 CRD를 사용해서 AWS Loadbalancer Controller가 해당 로드밸런서와 TargetGroup을 연결시켜서 자동으로 Pod의 IP를 획득해 갑니다.
여기에서 예시 yaml을 가져왔습니다. 직접 Service이름과 포트를 연결해서 해당 targetgroup과 연결할 수 있도록 구성되어 있습니다.
apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
name: my-tgb
spec:
serviceRef:
name: awesome-service # route traffic to the awesome-service
port: 80
targetGroupARN: <arn-to-targetGroup>
MetalLB
BareMetalLoadBalancer의 약자로 온프레미스 환경에서 표준 프로토콜(ARP, BGP)를 사용해 Load Balancer를 구현해주는 소프트웨어 입니다.
작동 방식은 쿠버네티스의 DaemonSet을 통해 Speaker 파드를 생성하고, External IP를 생성 및 전파합니다. 따라서 외부 클라이언트는 External IP를 통해서 쿠버네티스 내부로 트래픽을 전송할 수 있습니다.
따라서 스피커 파드는 External IP 전파를 할때 ARP(Layer2)와 BGP 중 프로토콜을 선택해서 배포하는데 일반적으로 BGP를 사용하도록 권장합니다. (ARP는 대규모 환경일 때 부하가 심할 수 있고 지속적으로 ARP 호출이 일어나는 등의 단점이 있을 수 있습니다.)
또한 퍼블릭 클라우드 회사(CSP)에서는 대부분 ARP를 차단하기 때문에 이용하기가 어렵습니다.
이는 MetalLB 공식문서에서도 서술하고 있으니 퍼블릭클라우드에서는 사용불가하다고 생각하시는게 좋습니다.
ARP모드에서의 동작
리더 파드가 선출되고 해당 리더 파드가 생성된 노드로만 트래픽이 인입됩니다. 그 트래픽은 해당 노드의 iptables에 맞춰 분산되고 그렇기 때문에 해당 노드가 아니라 다른 노드의 파드로도 전송될 수 있습니다.
로드밸런서타입의 서비스가 생성되면 일단 External IP가 생성되는데 이 때 speaker 파드들 중에서 1개의 리더 파드를 선정합니다. 이 때 speaker 파드는 데몬셋으로 동작하며, 호스트 네트워크를 사용합니다.
리더는 ARP(GARP, Gratutious ARP)를 통해서 external IP를 boardcast하게 되므로 해당 IP를 다른 곳에서 점유할 일은 절대 없습니다.
만약 장애 발생시 자동으로 다른 speaker 중에서 하나의 리더를 선출합니다. 멤버 리스트 업 및 장애 발견은 hashicorp의 memberlist를 사용해서 진행합니다.
ARP모드의 단점
다만 제한사항도 있는데 무조건 리더 파드로 향하는 속성 때문에 병목현상이 발생해도 대처가 어렵다는 점입니다. 물론 서비스를 여러 개 만들어서 리더가 정말 많아지면 어느정도 부하분산이 될 수 있으나 현실적으로 어렵고, BGP + ECMP를 사용해 분산을 하고 트래픽을 해결할 수 있지만 이는 공식적인 방식은 아닙니다.
MetalLB 설치
설치는 공식문서의 manifest 설치방식으로 진행했습니다.
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
설치를 완료하고 CRD를 확인해보면 bpg모드에서 사용하는 crd를 확인해 볼 수 있습니다.
그리고 추가적으로 리소스를 확인해보면 다음과 같습니다.
일단 각 node에는 speaker pod가 배포되었습니다. 이는 다음과 같이 daemonset으로 배포된 것입니다. 그리고 controller한 개는 deployment로 배포됨을 확인할 수 있습니다.
또한 speaker의 경우 node의 ip대역을 따르고 있는 것으로 보아 host 네임스페이스를 사용하는 것을 알 수 있습니다. controller의 경우 pod 자체의 IP를 가지므로 노드의 네임스페이스를 가지게 됩니다.
그리고 아직 Node에는 external IP는 없는 것을 확인할 수 있습니다.
External IP할당
일단 metallb에서 사용할 수 있도록 IP pool을 등록해줘야합니다. 공식문서의 가이드에 따라 다음과 같이 작성하겠습니다.
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ippool
namespace: metallb-system
spec:
addresses:
- 172.18.255.200-172.18.255.250
해당 yaml을 배포하게 되면 아래 명령어를 통해서 ip주소 pool을 확인할 수 있습니다. 실습에서는 172.18.255.200에서 172.18.255.250 내의 대역의 IP를 External IP로 쓸 수 있게됩니다.
kubectl get ipaddresspools -n metallb-system
이후에 L2Advertisement를 생성해 줍니다. 이걸 생성하는 이유는 설정한 IPpool을 기반으로 Layer2 모드로 LoadBalancer IP를 사용할 수 있도록 허용해주는 것입니다. Kubernetes 클러스터 내의 서비스가 외부 네트워크에 IP 주소를 광고하는 방식을 정의하게 됩니다. 다음과 같이 정의하겠습니다. 공식문서에서 확인할 수 있습니다.
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-advertise
namespace: metallb-system
spec:
ipAddressPools:
- my-ippool
다음 명령어를 통해서 잘 설정되었는지 확인해 보겠습니다.
kubectl get l2advertisements -n metallb-system
이렇게 사전작업을 다 마치고 service를 설정하면 됩니다. 실습 간에는 service를 3개를 만들었습니다. service는 다음과 같이 80/80을 타겟으로 하고 webpod pod를 타겟으로 하는 객체입니다.
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
서비스를 describe를 통해서보면 announcing from node 라는 식으로 현재 리더 노드가 myk8s-worker과 myk8s-worker3로 선정된 것을 확인할 수 있습니다.
kubectl describe svc | grep Events: -A5
저는 net-tools가 깔린 컨테이너 이미지로 띄워진 외부 컨테이너를 통해서 external IP로 arp ping을 날려보겠습니다.
우리가 주목해볼건 저 MAC 주소입니다. 해당 MAC주소로 speaker 파드의 리더를 살펴보면 svc1,2는 worker노드의 speaker pod가, svc3는 worker3 노드의 speaker pod가 master가 됩니다.
docker exec -it mypc ip -c neigh | sort
트래픽이 서비스에 도달하게 되면 리더 speaker 파드로 향하기 때문에 해당 노드에 부하가 걸릴 수 있다는 사실은 주의해야할 부분입니다.
IP 테이블의 정책적용 확인
SVC1(External-IP) 접속 시 iptables 에서 DNAT 되어 파드로 전달되는데, 전달 과정을 한 번 살펴보면 다음과 같습니다. 추후 IPVS와 비교해봅시다.
FailOver 테스트
만약 리더 speaker가 장애가 난다면 어떻게 될까요? 결론적으로는 자동으로 failover되며, 다시 원래의 leader speaker가 회복되면 다시 리더를 되찾아오게됩니다.
다음 명령어로 worker노드에 장애를 발생시켜보겠습니다. 확인해보면 worker가 NotReady인 것을 확인할 수 있습니다.
docker stop myk8s-worker --signal 9
이때 arp 테이블을 확인해 리더 speaker 노드를 확인하고, 다시 노드를 원복시켜보겠습니다.
docker start myk8s-worker
확인결과 장애 복구시에 다시 leader가 회복되는 것을 확인할 수 있습니다. 그리고 다시 한 번 svc에 대한 describe 정보를 확인해보면 기존 worker node를 리더로 가지고 있던 svc의 경우 잠시 worker2를 assign 했다가 다시 worker로 변경된 것을 확인할 수 있습니다.
이상으로 LoadBalancer 타입 및 MetalLB에 대한 실습을 마치겠습니다.