클러스터에서 서비스에 접속하기 위해서는 service를 거쳐야 한다. 서비스 타입 별로 어떤 서비스가 외부로 서비스를 노출하기 적합한지 살펴보자.
서비스는 ClusterIP, NodePort, LoadBalancer 등이 있다. clusterIP는 내부 리소스 간 접속하기 위한 용도이기 때문에 제외해야 한다. NodePort, LoadBalancer 를 활용하여 외부에서 내부로 접속할 수 있도록 구현할 수 있다.
NodePort는 클러스터 안의 각 노드에 특정 포트를 열어서 외부에서 접근 가능하게 만드는 방식이다.
예를 들어,
라면 접근 가능한 URL은 http://192.168.1.10:30982 가 된다. 즉, 사용자는 NodePort 방식의 서비스에 접근할때 노드의 IP와 포트를 알아야 한다. 만약 해당 노드나 네트워크가 죽으면 사용자는 http://192.168.1.10:30982 로 더 이상 접근할 수 없으며, 다른 노드가 살아있더라도 접속 경로가 끊긴 것 처럼 보이게 된다. 그래서 운영자가 사용자한테
"접속 IP 바뀌었어요! 이제 192.168.1.11:30982로 접속하세요"
이렇게 알려줘야 한다는 번거로움이 있다.
반면 LoadBalancer는 IP 하나를 고정으로 잡아주는 방식이다. 해당 IP는 한 노드가 죽더라도 다른 노드로 자동으로 옮겨가기 때문에 사용자는 항상 같은 IP로 접속할 수 있다.
AWS, Azure, GCP와 같은 퍼블릭 클라우드 환경에서는 LoadBalancer 타입의 서비스를 생성하면, 클라우드에서 자동으로 외부 IP가 할당되어 바로 사용할 수 있다. 그러나 온프레미스 환경에서는 이러한 자동화된 LoadBalancer 기능이 없기 때문에, 별도의 외부 IP 할당 및 로드밸런싱을 수행할 수 있는 서비스가 필요하다.
온프라미스에서 LoadBalancer 를 제공하는 서비스는 MetalLB가 있다.
구성은 https://metallb.universe.tf/installation/#installation-by-manifest 페이지를 참고하여 진행하였다.
본 테스트 환경에서는 ingress nginx로 pod에 example.com 도메인을 할당하였다. example.com 도메인을 외부로 노출시키기 위해 MetalLB를 사용하였다.
root@k8s-m1:~# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
namespace/metallb-system created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/servicel2statuses.metallb.io created
serviceaccount/controller created
serviceaccount/speaker created
role.rbac.authorization.k8s.io/controller created
role.rbac.authorization.k8s.io/pod-lister created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/controller created
rolebinding.rbac.authorization.k8s.io/pod-lister created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
configmap/metallb-excludel2 created
secret/metallb-webhook-cert created
service/metallb-webhook-service created
deployment.apps/controller created
daemonset.apps/speaker created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created
root@k8s-m1:~# kubectl get pod -o wide -n=metallb-system
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-75b7b4c4b-l9rzj 1/1 Running 0 60s 10.233.74.71 k8s-w1 <none> <none>
speaker-9snc2 1/1 Running 0 60s 172.16.10.93 k8s-w2 <none> <none>
speaker-gq547 1/1 Running 0 60s 172.16.10.91 k8s-m1 <none> <none>
speaker-vrbsc 1/1 Running 0 60s 172.16.10.92 k8s-w1 <none> <none>
root@k8s-m1:~# kubectl get all -n=metallb-system
NAME READY STATUS RESTARTS AGE
pod/controller-75b7b4c4b-l9rzj 1/1 Running 0 61s
pod/speaker-9snc2 1/1 Running 0 61s
pod/speaker-gq547 1/1 Running 0 61s
pod/speaker-vrbsc 1/1 Running 0 61s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/metallb-webhook-service ClusterIP 10.233.58.74 <none> 443/TCP 61s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/speaker 3 3 3 3 3 kubernetes.io/os=linux 61s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/controller 1/1 1 1 61s
NAME DESIRED CURRENT READY AGE
replicaset.apps/controller-75b7b4c4b 1 1 1 61s
이제 서비스를 LoadBalencer 타입으로 생성하면 MetalLB가 해당 서비스에 ip를 할당한다. MetalLB가 할당한 IP Pool 을 설정한다. 여기서 IP Pool 대역은 클러스터 노드들이 접근할 수 있고, DHCP가 관리하지 않는 IP 대역을 설정해야 한다.
## 노드에서 통신 가능한 IP 대역은 172.16.10.91/24이다.
root@k8s-m1:~# ip -4 -o a |grep ens2
2: ens2 inet 172.16.10.91/24 brd 172.16.10.255 scope global ens2\ valid_lft forever preferred_lft forever
root@k8s-m1:~# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-m1 Ready control-plane 2d5h v1.27.10 172.16.10.91 <none> Ubuntu 22.04.3 LTS 5.15.0-86-generic containerd://1.7.13
k8s-w1 Ready <none> 2d5h v1.27.10 172.16.10.92 <none> Ubuntu 22.04.3 LTS 5.15.0-86-generic containerd://1.7.13
k8s-w2 Ready <none> 2d5h v1.27.10 172.16.10.93 <none> Ubuntu 22.04.3 LTS 5.15.0-86-generic containerd://1.7.13
root@k8s-m1:~# vi ip-pool.metallb.yml
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ip-pool
namespace: metallb-system
spec:
addresses:
- 172.16.10.97-172.16.10.99
root@k8s-m1:~# kubectl apply -f ip-pool.metallb.yml
ipaddresspool.metallb.io/my-ip-pool created
root@k8s-m1:~# kubectl get ipaddresspools.metallb.io -n metallb-system
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
my-ip-pool true false ["172.16.10.97-172.16.10.99"]
## example.com 도메인을 외부로 노출시키기 위해 해당 ingress controller의 서비스를 LoadBalancer 타입으로 변경해주자.
root@k8s-m1:~# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress nginx example.com 10.233.53.77 80, 443 2d1h
## 현재 NodePort 타입로 설정되어 있다.
root@k8s-m1:~# kubectl get svc -n=ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.233.53.77 <none> 80:30982/TCP,443:30859/TCP 2d1h
ingress-nginx-controller-admission ClusterIP 10.233.16.31 <none> 443/TCP 2d1h
root@k8s-m1:~# kubectl edit svc ingress-nginx-controller -n ingress-nginx
---
...
spec:
type: LoadBalancer #NodePort -> LoadBalancer 로 변경
---
## MetalLB 에서 설정한 ip pool 대역으로 EXTERNAL-IP를 할당 받았다.
root@k8s-m1:~# kubectl get svc -n=ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.233.53.77 172.16.10.97 80:30982/TCP,443:30859/TCP 2d1h
ingress-nginx-controller-admission ClusterIP 10.233.16.31 <none> 443/TCP 2d1h
root@client:~# cat /etc/hosts |grep example.com
172.16.10.97 example.com
root@client:~# curl -k https://example.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
MetalLB는 온프라미스에서 로스밸런서를 간편하게 구현할 수 있다.
MetalLB는 내부적으로 L2 스위치의 동작을 소프트웨어적으로 구현한 것과 유사한 방식으로 동작한다. 서비스 규모가 작은 환경에서는 MetalLB가 좋은 선택지가 될 수 있겠다.
규모가 큰 엔터프라이즈 환경에서는 MetalLB 대신 외부의 L3 BGP 라우터를 사용한 구성 방식을 권장한다고 한다.
[1] https://andrewpage.tistory.com/23
[2] https://metallb.universe.tf/installation/
[3] https://www.youtube.com/watch?v=hJO1nxsB5uY