3. Service (1/2) - LoadBalancer

개요

쿠버네티스는 파드를 실시간으로 생성한다. 파드에 문제가 생기면 파드를 수정하지 않고, 종료하고 새롭게 생성한다. 이러한 환경에서는 파드간의 연결이 자동으로 구성되지 않으면, 파드에 문제가 생겼을 때 문제를 복구하고 네트워크를 통해 서비스를 노출하는데 상당한 시간이 소요 될 것이다. 이렇게 네트워크 연결이 자동으로 구성되고, 서비스로 노출되는 개념을 서비스 디스커버리(Service Discovery)라고 한다. 쿠버네티스에서 서비스 디스커버리가 어떻게 구현 되는지 알아보자.

클러스터 내부 혹은 외부 연결에 따라 두가지로 서비스를 구분할 수 있다.

  • 클러스터 내부에서 파드간 통신 : 클러스터IP(ClusterIP), 헤드리스(Headless)
  • 클러스터 외부에서 내부파드로 연결 : 노드포트(NodePort), 로드밸런서(LoadBalancer)

 

클러스터IP(ClusterIP) 서비스 생성

먼저 nginx deployment 를 작성하자, Selector를 적용하기 위해 label을 지정한다. 파일명은 nginx-deployment.yaml 로 하였다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-hello
  namespace: default
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello
kubectl apply -f nginx-deployment.yaml

다음으로 클러스터IP 타입의 nginx 서비스를 만들자. 파일명은 nginx-clusterip-scv.yaml 로 하였다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:  # POD 선택
    app: nginx
  type: ClusterIP
  ports:
  - name: tcp
    port: 80 # 외부로 서비스 하는 포트
    targetPort: 80 # POD 의 nginx 포트
kubectl apply -f nginx-clusterip-svc.yaml

내부 아이피가 지정되어 있음을 볼 수 있다. 클러스터 내부 노드에서 해당 아이피로 확인할 수 있다.

크러스터 노드(VM) 에 브라우저가 설치되어 있다면 브라우저로도 볼 수 있다. 나는 주로 CentOS 계열인 RocyLinux 의 DVD 버전을 옵션 변경없이 설치해서 기본으로 데스크탑이 설치되어 있다.

아래 172.16.31.66 아이피는 엔드포인트의 IP 이며 여기서는 파드가 최종 목적지이기 때문에 파드의 IP가 된다.

kubectl get endpoints

 

레플리카로 생성된 경우에는 여러 아이피가 출력된다.

 

실제 파드간의 통신을 테스트하기 위해 ping, nslookup 이 내장된 busybox를 파드로 설치하자.

kubectl run busybox --image=busybox --restart=Never -- sleep 1d

busybox로 들어가서 파드간 통신이 가능한지 확인해보자.

kubectl exec -it busybox /bin/sh

nginx-svc 서비스명으로 ping 을 했을 때, 목적지 IP를 제대로 가져오는 것을 확인할 수 있다. nslookup 도 해보자

클러스터 내부에 구성된 네임서버로부터 nginx-svc 도메인의 아이피 정보를 가져오는 걸 확인 할 수 있다.

참고로 도메인명은 자동 구성되는데, {이름}-{namespace}-{type}-cluster.local 의 규칙으로 생성된다.

 

NodePort

노드 포트 타입의 서비스는 클러스터 외부에서 클러스터 노드의 네트워크 포트를 통해 내부 파드와 연결된다.

노드포트 타입의 서비스를 생성해보자. 파일 이름은 nginx-nodeport-svc.yaml 로 했다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport-svc
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    nodePort: 30080
  selector:
    app: nginx 
  type: NodePort
kubectl apply -f nginx-nodeport-svc.yaml
# 확인
kubectl get svc -A

kubectl get endpoints

위에서 보면 노트포트 타입의 서비스도 클러스터IP 타입의 서비스와 동일하게 아이피, 포트가 할당된다. 노드포트는 내부의 클러스터IP 주소(VIP)를 통해 실제 파드로 연결된다.

실제 노드 IP를 확인해보자.

kubectl get nodes -o wide

브라우저에서 실행해 보면, 노드 아이피 영역의 네트워크 어디에서도 접속이 됨을 알 수 있다.

MetalLB

Kubernetes는 네트워크 로드 밸런서 구현을 제공하지 않는다. 네트워크 로드 밸런서의 구현은 모두 다양한 IaaS 플랫폼(GCP, AWS, Azure)에서 자체적으로 지원되기 때문에. 지원되는 IaaS 플랫폼(GCP, AWS, Azure)에서 실행하지 않는 경우LoadBalancer는 생성 시 "pending" 상태로 유지된다. 베어메탈의 경우 "NodePort" 및 "externalIPs" 서비스 있지만, 이 두 옵션 모두 프로덕션 사용에 있어 상당한 단점(Layer 7 미지원)이 있다. MetalLB는 베어 메탈 환경에서도 표준 네트워크 장비와 통합되는 네트워크 로드 밸런서 구현을 제공하여 이러한 불균형을 해결하는 것을 목표로 한다.

 

MetalLB 설치

https://metallb.universe.tf/installation/ 의 가이드 대로 하면된다.

 

설치 전

kubectl edit configmap -n kube-system kube-proxy

아래처럼 ipvs.strictARP 를 true 로 설정한다.

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
  strictARP: true

설치하기 위해서 메니페스를 적용한다. 23년 10월 기준 최신 버전은 v0.13.12 이다. 내 경우에는 k8s가 1.28.2 이기 때문에 최신버전을 적용했다.

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

LoadBalancer 타입의 서비스가 생성될 때 External IP 가 매핑된다. k8s 클러스터 밖에서 접속하기 위해서는 k8s 노드와 같은 영역의 IP 에서 비어있는 아이피 영역을 할당하면 된다. 아래 메니페스를 실행하자, 파일이름은 ip-addr-pool.yaml 로 하였다.

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.180.140-192.168.180.160
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: my-l2
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool
kubectl apply -f ip-addr-pool.yaml

설치된 모습은 아래와 같다.

kubectl get pods -n metallb-system -o wide

노드마다 speaker 라는 녀석이 붙어 있는 걸 보니, 데몬셋으로 생성되어 있음을 알 수 있다, 노드의 아이피가 할당되어 있고, 이 녀석이 External IP를 클러스터로 전파한다.

먼저 nginx 디플로이먼트를 생성하자. 파일이름은 nginx-deploy.yaml 로 하였다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-hello
  namespace: default
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello
kubectl apply -f nginx-deploy.yaml

이제 LoadBalancer 타입의 서비스를 생성해 보자. 메니페스트 파일 이름은 nginx-svc.yaml 로 하였다.

위 에서 labels.app 이 nginx 이기 때문에 selector 를 nginx로 하였다.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx-hello
  namespace: default
spec:
  ports:
  -  port: 80
  selector:
    app: nginx
  type: LoadBalancer
kubectl apply -f nginx-svc.yaml

서비스 목록을 보면 CLUSTER-IP 에 EXTERNAL-IP 가 매핑되어 있는 걸 볼 수 있다.

브라우저로 보면 아래와 같다.

IP 를 통한 Layer 4 서비스는 프러덕션 환경에서 제약이 많다. 프러덕션 환경에서는 Viratul Host, Reverse Proxy 등이 복합적으로 쓰인다. 이를 위해 Layer 7 서비스가 가능한 인그레스(Ingress) 가 필요하다. 인그레스는 클러스터 외부의 HTTP 및 HTTPS 경로를 클러스터 내의 서비스에 노출한다. 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 따라 제어된다.

 

인그레스가 모든 트래픽을 하나의 서비스로 보내는 간단한 개념도이다.

Ingress Nginx Controller 설치

ingress-nginx 는 NGINX 를 리버스 프록시 및 로드 밸런서로 사용하는 쿠버네티스용 Ingress Controller 이다. 인그레스 컨트롤러는 L7 LoadBalancer 의 역할을 한다. 먼저 메니페스트를 통한 설치를 진행하자.

https://github.com/kubernetes/ingress-nginx 깃허브에 방문하면 버전 목록과 k8s 버전 호환 및 해당버전의 Nginx 버전을 확인할 수 있다. 23년 10월 최신 버전인 v1.9.4 를 설치한다.

https://kubernetes.github.io/ingress-nginx/deploy/ 여기에서 설치 방법을 확인한다.

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

설치가 끝나면 여러 오브젝트들이 배포된다. 아래와 같이 확인해보자

kubectl get all -n ingress-nginx

 

VirtualHost 생성

ingress-nginx-controller 서비스에 EXTERNAL-IP 가 할당된 것을 볼 수 있다. 이제 Ingress 를 작성하자. 파일이름은 nginx-hello-ingress.yaml 로 하였다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: "ingress.test.com"
    http:
      paths:
      - pathType: Prefix
        path: /hello
        backend:
          service:
            name: nginx-hello-svc
            port:
              number: 80
kubectl apply -f nginx-hello-ingress.yaml

위에서 정의한 Ingress 에 service.name 이 nginx-hello-svc 이니, 해당 이름으로 서비스를 생성한다. 메니페스트 파일 이름은 nginx-hello-svc.yaml 로 하였다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-hello-svc
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
kubectl apply -f nginx-hello-svc.yaml

위에서 정의한 Ingress 내용 중에 host: "ingress.test.com" 이라고 있는데, 이 도메인으로 VirtualHosting 이 가능하다. 즉 여러개의 도메인을 정의하고 쓸 수 있다. "ingress.test.com" 은 퍼블릭 도메인이 아닌 사설 도메인이므로 리눅스 계열에서는 "/etc/hosts", 윈도우즈 계열에서는 "System32\drivers\etc/hosts" 파일에 등록하여 쓸 수 있다.

ingress-nginx-controller 의 EXTERNAL-IP 가 "192.168.180.141" 로 매핑되어 있으니, hosts 파일에 사설 도메인을 등록하자

# /etc/hosts 혹은 C:\Windows\System32\drivers\etc\hosts"

192.168.180.141 ingress.test.com

 

클러스터 밖에서 "ingress.test.com/hello" 를 호출해 보았다.

새로 고침할 때마다 파드 아이피(Server address), 파드 호스트명 (Server name) 이 계속 바뀌는 걸 볼 수있다, 즉 부하분산(LoadBalancing)이 잘 되고 있음을 알 수 있다.

'DevOps와 Infra > Kubernetes On Premise' 카테고리의 다른 글

1.2. Kubernetes(k8s) 클러스터 구성 - RHEL  (2) 2023.12.02
5. Storage  (0) 2023.11.06
4. Service (2/2) - Istio  (0) 2023.11.05
2. k8s 플러그인 활용  (1) 2023.10.30
1.1. Kubernetes(k8s) 클러스터 구성 - Ubuntu  (1) 2023.10.23
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유