[k8s 구축] 3. MetalLB와 IngressController를 이용한 Kubernetes 로드밸런싱

필자는 온프레미스 환경에서 k8s를 사용하기에 별도의 로드밸런서가 필요하다.
또한 HAProxy같은 리버스프록시를 사용하지 않기에 도메인 네임을 통해 서비스에 접근할 다른 방법이 필요하다.
MetalLB와 IngressController는 이런 상황에서 일반적으로 사용되는 솔루션이다.

이 글에서는 MetalLB와 IngressController를 통해 온프레미스 서버팜에서 k8s 서비스를 어떻게 로드밸런싱하고 도메인네임으로 접근할 수 있는지를 중점적으로 설명한다.

이전 글에서 구축한 Rocky Linux 9.5 + k8s + calico 환경을 사용한다.

0. 구성 방안

필자가 생각하는 k8s 구성 방안은 다음과 같다.

  • MetalLB를 통한 공인 IP 부여 (공인 IP Pool 보유 중)
  • 웹 서비스는 IngressController를 통해 통합 제공 (MetalLB로부터 부여된 IP 사용)
  • 웹 이외의 서비스는 MetalLB에 직결하여 사용

이 구성을 통해 웹 서비스에 대한 도메인 기반 라우팅과 TLS 서비스가 가능하며, 웹 이외의 서비스에 대해서도 공인 IP를 부여할 수 있다.

중요한 점은 웹 서비스 여부에 따라 서비스를 ClusterIP로 할 지, LoadBalancer로 할 지가 달라진다는 점이다.
웹 서비스인 경우 IngressContoller에 의해 라우팅되므로 ClusterIP로 서비스해야하지만 웹 서비스가 아닌 경우 고유 IP를 부여받아야 하므로 LoadBalancer로 서비스해야 한다.

1. MetalLB 설치

커맨드를 복사하기 쉽도록 쉘 표시($)를 생략하였다.
MetalLB는 ARP를 사용하기에 kube-proxy에서 ARP를 사용하도록 강제해야 한다.

kubectl edit configmap -n kube-system kube-proxy
# 파일 내에서 strictARP: false -> true로 변경

그 후 MetalLB를 설치한다. [MetalLB]

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml

MetalLB가 사용할 설정 파일을 새로 만들고 적용한다.
다음은 metallb-config.yaml 파일의 예시이다.

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 10.1.1.0/24  # 사용 가능한 IP 범위로 변경
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool
kubectl apply -f metallb-config.yaml

MetalLB 설치가 완료되었다.

2. IngressController 설치

본인의 k8s 버전에 맞추어 IngressController를 설치한다. [IngressController]

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.1/deploy/static/provider/cloud/deploy.yaml

IngressController 설치가 완료되었다.

참고로 v1.12.1 버전에서는 IngressController의 default 네트워크가 LoadBalancer로 되어있다.
필자처럼 IP를 고정해 사용하고 싶은 경우 wget으로 위 yaml 파일을 내려받아 loadBalancerIP 구문을 추가하여 배포하면 된다. (가장 마지막 줄 참고)

# deploy.yaml 파일 중
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.12.1
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  externalTrafficPolicy: Local
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: LoadBalancer
  loadBalancerIP: <고정할 IP>

3. 서비스 배포

이제 서비스를 배포해보자.
이전 글에서 만든 서비스와 더불어 Ingress를 만들어주면 된다.
여기서부터는 커맨드와 출력 결과를 구분하기 위해 셀 기호($)를 표시하였다.

Ingress는 서비스와 분리하여 yaml로 관리하는 게 편리하나 편의상 하나의 파일로 만들어 설명한다.
다음은 세 개의 nginx replica를 ingress로 서비스하는 web-ingress.yaml 파일의 내용이다.
파일을 만들어 배포해주자.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80          # 서비스 포트 (클러스터 내부)
      targetPort: 80     # 컨테이너 포트 (Pod 내부)
  type: ClusterIP  # 웹 서비스이기에 MetalLB를 사용하지 않고 Ingress를 사용할 것이다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: my-web-test-service.com  # 실제 도메인으로 변경하세요
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
$ kubectl apply -f web-ingress.yaml

서비스가 잘 배포되었는지 확인해보자.
마스터 노드에서 아래 커맨드를 실행했을 때 nginx 응답이 출력되면 정상이다.
참고로 http://10.1.1.0은 IngressController의 외부 IP 주소이며 아래 커맨드를 통해 확인할 수 있다.

$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.111.189.38   10.1.1.0      80:32464/TCP,443:32359/TCP   19m
ingress-nginx-controller-admission   ClusterIP      10.109.170.26   <none>        443/TCP                      19m

$ curl -H "Host: my-web-test-service.com" http://10.1.1.0
<!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>

참고로 외부에서 이 서비스에 접근하려면 my-web-test-service.com가 DNS에 등록되어있어야 한다.
필자의 경우 MetalLB의 IP Pool로 공인 IP 대역을 사용하며 이를 이용해 DNS 서버를 운영하고 있기에 외부에서도 위 서비스에 정상적으로 접근할 수 있다.

MetalLB와 IngressController의 활용 방법은 매우 다양하지만 여기서는 이 정도로만 소개하고 글을 마무리한다.

Series Navigation<< [k8s 구축] 2. Kubernetes 환경 구축하기[k8s 구축] 4. Let’s Encrypt와 bind DNS 서버를 이용한 와일드카드 SSL 적용 및 reflector 자동 배포 >>

댓글 남기기