Kubernetes 에서의 Autoscaling Part 1

 

번개장터

안녕하세요. 번개장터 백엔드 엔지니어 박상조입니다. 이번에는 Autoscaling에 대해 알아보도록 하겠습니다. 실제 운영 환경에서는 여러 가지 요인에 의해 필요한 자원이 달라지는데요. 예를 들어보면 사용자가 몰리는 시간에는 많은 자원이 필요하고 그렇지 않은 경우에는 적은 자원이 필요할 것입니다. 이런 상황에서 항상 자원을 최대로 유지한다면 비용적으로 엄청난 부담이 될 것입니다.

이런 문제를 해결하기 위한 기법이 바로 Aoutoscaling입니다. 쉽게 말해 자원이 필요하면 늘리고 필요 없어지면 줄이는 것이죠. 컨테이너가 등장하고 이런 일이 확실히 쉬워졌습니다. 여기에 k8s까지 있으면 이전과는 비교도 할 수 없을 것입니다.

두 가지 고려사항

k8s에서 Autoscaling을 하려면 두 가지를 고려해야 합니다. 바로 Pod와 Cluster입니다. 부하가 증가하는 상황을 가정해 봅시다. 처음 그 부하를 받게 되는 곳은 바로 Pod입니다. Pod의 부하가 증가하고 그럼 Pod를 늘려주면 됩니다. 서비스가 부하를 잘 분산해 각 Pod의 부하를 낮춰주고 원활한 서비스를 할 수 있게 될 겁니다.

하지만 여기에는 문제가 하나 있습니다. 바로 Pod를 무한정 배포할 수 없다는 점입니다. Pod를 배포하려면 작업자 노드가 필요한데 이 작업자 노드가 무한정 존재하지 않기 때문에 발생하는 문제입니다. 그래서 Pod를 배포해서 작업자 노드가 부족해지면 이 작업자 노드를 늘려 줍니다. 이제 이론상으로는 아무리 많은 부하가 걸려도 서비스를 원활히 제공할 수 있습니다.

Metrics Server

앞에서 Pod의 부하가 증가하면 Pod를 늘려주면 된다고 했는데 그렇다면 부하의 증가를 어떻게 알 수 있을까요?

지금 상태로는 방법이 없습니다. 그래서 필요한 것이 Metrics Server입니다. Metrics Server는 각 Pod와 Node의 사용량을 모니터링하고 API를 통해 볼 수 있게 해줍니다. 설치하기 위해 많은 오브젝트가 필요하지만 한 yaml 파일로 만들어 배포할 수 있습니다.

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:aggregated-metrics-reader
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
rules:
- apiGroups: ["metrics.k8s.io"]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: metrics-server:system:auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: metrics-server-auth-reader
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system
---
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
  name: v1beta1.metrics.k8s.io
spec:
  service:
    name: metrics-server
    namespace: kube-system
  group: metrics.k8s.io
  version: v1beta1
  insecureSkipTLSVerify: true
  groupPriorityMinimum: 100
  versionPriority: 100
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-server
  namespace: kube-system
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    k8s-app: metrics-server
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  template:
    metadata:
      name: metrics-server
      labels:
        k8s-app: metrics-server
    spec:
      serviceAccountName: metrics-server
      volumes:
      # mount in tmp so we can safely use from-scratch images and/or read-only containers
      - name: tmp-dir
        emptyDir: {}
      containers:
      - name: metrics-server
        image: k8s.gcr.io/metrics-server-amd64:v0.3.1
        args:
        - --kubelet-insecure-tls
        imagePullPolicy: Always
        volumeMounts:
        - name: tmp-dir
          mountPath: /tmp
---
apiVersion: v1
kind: Service
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    kubernetes.io/name: "Metrics-server"
spec:
  selector:
    k8s-app: metrics-server
  ports:
  - port: 443
    protocol: TCP
    targetPort: 443
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: system:metrics-server
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - nodes
  - nodes/stats
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:metrics-server
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:metrics-server
subjects:
- kind: ServiceAccount
  name: metrics-server
  namespace: kube-system

원래는 여러 yaml 파일로 된 오브젝트들을 하나로 묶었습니다. 내용은 천천히 확인해 보시길 바랍니다. 그럼 우리는 Metrics Server를 배포해 봅시다.

kubectl create -f metrics.yaml

위 명령어를 통해 Metrics Server를 배포하면

위처럼 간단하게 오브젝트들이 생성됩니다.

kubectl top po
kubectl top node

이제 위 명령어를 통해 Pod와 Node의 CPU와 메모리 사용량을 한눈에 볼 수 있습니다. Metrics Server를 구성하는 데 시간이 걸릴 수 있으니 배포 후 잠시 기다려야 할 수 있습니다.

Horizontal Pod Autoscaler

그럼 이제 모니터링은 가능해졌습니다. 이제 계속 Pod의 부하를 모니터링하다가 부하가 올라가면 Pod의 복제본 수를 늘려주고 부하가 줄어들면 복제본 수를 줄이면 됩니다. 이런 기능을 할 수 있는 오브젝트가 있습니다. 바로 Horizontal Pod Autoscaler(이하 hpa)입니다. CPU나 메모리를 모니터링해 기준에 따라 복제본의 수를 늘리거나 줄여주는 오브젝트입니다. 그럼 생성해 보겠습니다.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: [YOUR HPA NAME]
spec:
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: [YOUR TARGET]
  minReplicas: 1
  maxReplicas: 3
  targetCPUUtilizationPercentage: 60

보시면 아시겠지만 hpa가 Deployment의 복제본 수를 조절하는 방식입니다. 위 yaml 파일로 hpa를 생성하고 확인합니다.

kubectl -f hap.yaml
kubectl get hpa

그럼 위처럼 최소, 최대, 현재 Pod의 수, 타겟의 사용량 등을 잘 모니터링하고 있는 것을 알 수 있습니다.

만약 타겟의 사용량이 제대로 표시되지 않는다면 Pod에 제한을 걸어두지 않아서 그럴 수 있습니다. CPU나 메모리 사용률을 Pod안에 있는 container의 request를 기준으로 하므로 request가 지정되지 않으면 hpa가 제대로 동작하지 않습니다.

처음 Deployment에서 지정한 Pod의 수가 최소 Pod보다 크고 Pod가 현재 부하가 걸려있지 않는다면 곧 Pod의 수가 줄어드는 것을 보실 수 있습니다. 또 Jmeter 등을 통해 부하를 걸면 Pod가 증가하는 것을 보실 수 있습니다.

hpa는 기본적으로 30초 간격으로 필요한 Pod 수를 계산합니다. 그리고 한 번 Pod의 수를 증가시키면 3분의 대기시간이 있습니다. 이런 값들은 현재 EKS에서 설정할 수 없습니다. 사용상에 큰 문제는 없을 것 같지만 중요한 부분이니 체크하시길 바랍니다.

이렇게 하면 기본적으로 Pod를 유연하게 사용할 수 있습니다. 이제 다음 포스팅에서 Cluster를 Autoscaling하는 방법에 대해 알아보겠습니다.

읽어주셔서 감사합니다.

기업문화 엿볼 때, 더팀스

로그인

/