데브옵스 & 인프라/Kubernetes

[Kubernetes] 쿠버네티스 서비스 (책 실습)

턴태 2022. 9. 9. 18:16

서비스

쿠버네티스에서는 서비스라는 개념이 있습니다. 일반적인 서비스의 의미와는 다르게, 외부에서 쿠버네티스 클러스터에 접속하는 방법을 서비스라고 합니다. 

 

노드포트

노드포트는 외부에서 쿠버네티스 클러스터에 접근할 수 있는 방법입니다. 노드포트는 워커 노드의 특정한 포트를 설정하여 그 포트에 들어오는 요청이 노드포트 서비스로 전달이 됩니다. 그리고 사용자의 요청을 처리할 수 있는 파드에 요청을 전달하여 사용자가 클러스터 내부에 접근할 수 있도록 하는 것입니다. 간단하게 하청을 보내는 중간자 역할을 하는 서비스입니다.

 

하나의 파드를 만들어둔 후에 오브젝트 스펙을 사용하여 노드포트 서비스를 생성합니다.

 

apiVersion: v1
kind: Service
metadata:
  name: np-svc
spec:
  selector:
    app: np-pods
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30000
  type: NodePort

야믈 파일의 설정을 보면, 먼저 오브젝트의 종류가 서비스이다. 그리고 스펙에서는 네트워크 관련 속성들이 정의되어 있습니다. 셀렉터의 레이블은 np-pods로 정하였습니다. 이 셀렉터가 파드를 추적하여 접속을 처리합니다. 그래서 디플로이먼트에서 레플리카셋을 통해 파드 수를 늘리더라도, 그 파드들을 모두 추적하여 사용자가 접속할 수 있게 합니다.

 

직접 부하 테스트를 통해 확인해 볼 수 있습니다.

 

야믈파일에서 노드포트의 포트는 30000번 포트로 설정했으며, 각 워커 노드에서 접근할 수 있도록 했기 때문에 각 워커 노드의 아이피를 통해서 접근할 수 있게 됩니다.

kubectl get services

services를 조회하여 포트를 확인할 수도 있습니다.

 

각 워커 노드는 192.168.1.10x (x = 1, 2, 3)으로 설정했기 때문에  192.168.1.101:30000, 192.168.1.102:30000, 192.168.1.103:30000 을 통해 클러스터 내부에 접속할 수 있습니다.

 

이제 파워셸을 통해서 책 예제에 나온 부하 테스트를 해보겠습니다.

$i=0; while($true)
{
 % { $i++; write-host -NoNewline "$i $_" }
  (Invoke-RestMethod "http://192.168.1.101:30000")-replace "\n", " "
}

를 입력하여 파드 이름을 화면에 표시합니다. 이제 마스터 노드에서 파드를 3개로 증가시킵니다.

kubectl scale deployment np-pods --replicas=3

그런 후에 파워셸로 돌아가면, 파드 3개가 순차적으로 셸에 표시되는 것을 볼 수 있습니다. 이는 노드포트 오브젝트의 스펙에서 셀렉터의 레이블을 np-pods로 설정하여, 이 디플로이먼트의 이름을 확인해 같은 파드로 간주하기 때문입니다.

 

expose

물론 expose라는 명령어로도 노드포트 서비스를 생성할 수 있습니다.

kubectl expose deployment [파드명] --service=NodePort --name=[서비스명] --port=[포트]

서비스는 노드포트라고 지정한 다음 그 서비스가 연결할 파드를 정합니다. 그리고, 노드포트가 파드에 연결할 포트도 지정해줍니다.

 

이때, 노드포트의 포트 번호는 따로 지정하지 않아서 30000~32767에서 임의로 지정됩니다. 이도 동일하게 워커노드의 아이피와 노드포트의 포트를 통해서 클러스터 내부로 접근할 수 있습니다.

 

인그레스 - 용도 별로 다르게 연결하는 서비스

노드포트 서비스를 단순하게 사용하면 노드포트 서비스 한 개에 하나의 디플로이먼트가 적용되기 때문에, 디플로이먼트 개수만큼 노드포트 서비스를 생성해야 합니다. 이때, 쿠버네티스의 인그레스를 통해서 여러 개의 디플로이먼트에 응답할 수 있게 합니다. 백엔드 개발에서 각 경로에 따라서 라우터를 설정하는 것처럼, 주소에 따라서 다른 응답을 제공할 수 있습니다.

 

인력사무소에서 각 부서에 따라 팀을 짜서 일과를 부여하는 느낌입니다.

 

사용자는 노드포트에서 설정한 포트를 통해서 노드포트 서비스로 접속합니다. 이 노드포트 서비스는 인그레스 컨트롤러로 구성되어 있으며, 이 인그레스 컨트롤러가 사용자의 접속 경로에 따라서 클러스터 IP 서비스를 통해 경로를 안내하여 적합한 파드로 연결시켜줍니다.

 

인그레스는 경로와 작동을 미리 정의해야 합니다. 이를 위해 미리 파일을 작성해서 설정을 적용할 수 있게 합니다.

apiVersion: [api 버전]
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: / # 메타데이터의 기록, 타겟은 "/"
spec:
  rules:
  - http:
      paths:
      - path:
        backend:
          serviceName: hname-svc-default # 클러스터 IP 서비스 이름
          servicePort: 80
      - path: /ip
        backend:
          serviceName: ip-svc
          servicePort: 80
      - path: [경로]
        backend:
          serviceName: [클러스터 IP 서비스 이름]
          servicePort: [서비스 포트]

경로에 따라서 서비스를 다르게 설정할 수 있습니다.

 

apply를 통해 인그레스 설정을 등록합니다. 조회할 때는

kubectl get ingress

로 조회할 수 있습니다. 

 

NGINX 인그레스 컨트롤러와 인그레스 설정을 모두 마쳤으면, 외부에서 NGINX 인그레스 컨트롤러에 접속할 수 있도록 노드포트 서비스로 NGINX 인그레스 컨트롤러를 노출시킵니다.

apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
spec:
  ports:
  - name: http
    protocol: TCP
    port: 80
    target: 80
    nodePort: 30100
  - name: https
    protocol: TCP
    port: 443
    target: 443
    nodePort: 30101
  selector:
    app.kubernetes.io/name: ingress-nginx
  type: NodePort

http에서 접속할 때는 노드포트 서비스가 30100 포트를 통해 접속할 수 있도록 했고, https에서 접속할 때는 30101 포트를 통해 접속할 수 있도록 설정했다.

 

이제 expose를 통해서 디플로이먼트도 서비스로 노출시킨다면 인그레스 컨트롤러를 통해서 원하는 목적에 따라 클러스터로 접근할 수 있게 됩니다. 각 디플로이먼트를 서비스로 노출 시킬 때, 각 인그레스 설정에 등록된 이름으로 서비스로 노출시키도록 합니다.

 

같은 아이피의 같은 포트이지만, 경로에 따라서 다른 파드 클러스터로 접근한 것을 볼 수 있습니다.

 

로드밸런서

앞선 인그레스는 노드에서 노드포트로 노드포트에서 클러스터IP를 거쳐서 파드로 접속하는 과정을 거쳤습니다. 이러한 과정은 여러 과정을 거치므로 비효율적입니다. 그래서 쿠버네티스에서 로드밸런서 서비스 타입을 사용해서 파드를 외부에 노출시키고 부하를 분산합니다.

 

클라우드에서는 기존 업체들의 도움을 받아 쿠버네티스 클러스터 외부에 로드밸런서를 구현하여 사용하는데, 온프레미스에서는 MetalLB를 사용해서 로드밸런서 서비스를 받아주도록 합니다. MetalLB는 L2네트워크(ARP/NDP)와 L3 네트워크(BGP)로 로드밸런서를 구현합니다.

 

MetalLB 컨트롤러는 프로토콜을 정의하고 외부 IP(EXTERNAL-IP)를 부여합니다. MetalLB Speaker는 작동 방식에 따라 경로를 만들고 네트워크 정보를 전달하며 경로를 제공합니다. L2는 각 노드에 위치한 스피커 중에서 하나를 정해서 총괄하도록 합니다.

 

파드를 만들어서 각각 레플리카셋을 3개로 만들어서 총 6개의 파드를 배포시킵니다. 그 이후에 정의해놓은 오브젝트 스펙 파일로 MetalLB를 구성합니다.

 

MetalLB를 설정하기 위해서 ConfigMap이라는 오브젝트를 사용해서 설정을 정의합니다.

 

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: nginx-ip-range
      protocol: layer2 # 로드 밸런서의 동작 방식
      address:
      - 192.168.1.11-192.168.1.13 # 로드 밸런서의 Ext 주소

컨피그맵의 생성을 확인하기 위해서는 get 명령어를 사용해서 확인할 수 있습니다.

kubectl get configmap -n metallb-system

서비스와 서비스 설정을 모두 정의했으므로 이 서비스에 파드를 노출시키도록 합니다.

kubectl expose deployment [파드명] --type=LoadBalancer --name=[서비스명] --port=80

kubectl get services를 통해서 Ext 주소를 확인하고 여기에 접속하면 온프레미스 환경에서 클러스터에 접속할 수 있도록 구현할 수 있습니다.

HPA 부하에 따른 파드 조절

갑자기 사용자가 늘어나서 파드가 감당하지 못할 정도가 되면 API 등 서비스를 정상적으로 제공하지 못하게 됩니다. 그렇기 때문에 부하가 늘어남에 따라서 파드도 유동적으로 관리해야 합니다. 이때 쿠버네티스에서 제공하는 기능이 HPA(Horizontal Pod Autoscaler)입니다.

 

이 HPA를 사용하기 위해서 메트릭 서버(Metric-Server)를 통해서 계측값을 받아야 합니다. 이 계측값을 API서버에서 HPA에 전달하여 스케일 요청으로 파드를 늘릴지 줄일지 조절하는 데에 사용하기 때문입니다.

 

메트릭 서버를 오브젝트 스펙 파일로 생성한 후에 생성한 파드의 top 값을 확인할 수 있습니다.

kubectl top pods

각 파드가 점유하고 있는 CPU와 MEMORY 값이 터미널에 출력되게 됩니다.

 

이제 scale을 어떻게 증감시킬지 기준 값을 설정해야 합니다. 이를 위해서 기존 디플로이먼트 내용을 edit 명령으로 수정하는 방법이 있습니다.

kubectl edit deployment [파드명]

vi에디터를 통해서 디플로이먼트의 내용을 확인-수정할 수 있습니다. 여기서 resources: {} 부분을 찾아서 requests와 limits 항목을 만듭니다. 여기서 사용할 단위인 m은 milliunits의 약어로 1000m는 1개의 CPU와 같습니다.

resources:
  requests:
    cpu: "10m"
  limits:
    cpu: "50m"

수정을 마치고 나온 후에는 스펙이 변경되어 새로운 파드가 생성됩니다. 이제 autoscale 명령을 사용하여 부하에 따라서 파드를 조절할 수 있습니다.

kubectl autoscale deployment [파드명] --min=[최소 파드 수] --max=[최대 파드 수] --cpu-percent=[cpu 사용량]

cpu 사용량을 넘으면 autoscale이 시작됩니다. 즉, 앞서 requests에서 cpu를 10m으로 설정했고 cpu 사용량을 50%로 설정했다면, 파드가 5m의 부하를 받을 때, 파드를 증설한다는 의미입니다. 부하의 총량이 정해져 있어서 일부 파드는 5m보다 더 많은 부하를 받을 수도 있습니다.

 

watch 명령어로 각 파드의 부하와 파드 목록을 열람합니다.

watch kubectl get pods
watch kubectl top pods

부하를 늘려서 cpu 사용량을 높이면 아래와 같이 파드의 수가 증가하게 됩니다.

이제 요청을 줄여서 부하를 없애면 파드가 다시 줄어듭니다.

감사합니다.