1 - 디플로이먼트(Deployment)로 스테이트리스 애플리케이션 실행하기

이 페이지에서는 쿠버네티스 디플로이먼트 오브젝트를 사용하여 애플리케이션을 실행하는 방법을 설명한다.

목적

  • nginx 디플로이먼트 생성하기
  • kubectl을 사용하여 디플로이먼트 정보 나열하기
  • 디플로이먼트 업데이트하기

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

쿠버네티스 서버의 버전은 다음과 같거나 더 높아야 함. 버전: v1.9. 버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

nginx 디플로이먼트 생성하고 탐색하기

쿠버네티스 디플로이먼트 오브젝트를 생성하여 애플리케이션을 실행할 수 있으며, 디플로이먼트에 대한 명세를 YAML 파일에 기술할 수 있다. 예를 들어 이 YAML 파일은 nginx:1.14.2 도커 이미지를 실행하는 디플로이먼트에 대한 명세를 담고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. YAML 파일을 기반으로 디플로이먼트를 생성한다.

     kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  2. 디플로이먼트에 대한 정보를 살펴본다.

     kubectl describe deployment nginx-deployment
    

    출력은 다음과 유사하다.

     Name:     nginx-deployment
     Namespace:    default
     CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
     Labels:     app=nginx
     Annotations:    deployment.kubernetes.io/revision=1
     Selector:   app=nginx
     Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
     StrategyType:   RollingUpdate
     MinReadySeconds:  0
     RollingUpdateStrategy:  1 max unavailable, 1 max surge
     Pod Template:
       Labels:       app=nginx
       Containers:
        nginx:
         Image:              nginx:1.14.2
         Port:               80/TCP
         Environment:        <none>
         Mounts:             <none>
       Volumes:              <none>
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     True    MinimumReplicasAvailable
       Progressing   True    NewReplicaSetAvailable
     OldReplicaSets:   <none>
     NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
     No events.
    
  3. 디플로이먼트에 의해 생성된 파드를 나열한다.

     kubectl get pods -l app=nginx
    

    출력은 다음과 유사하다.

     NAME                                READY     STATUS    RESTARTS   AGE
     nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
     nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  4. 파드에 대한 정보를 살펴본다.

     kubectl describe pod <pod-name>
    

    <pod-name>은 파드 중 하나의 이름이다.

디플로이먼트 업데이트하기

새 YAML 파일을 적용하여 디플로이먼트를 업데이트할 수 있다. 이 YAML 파일은 nginx 1.16.1을 사용하도록 디플로이먼트를 업데이트해야 함을 명시하고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Update the version of nginx from 1.14.2 to 1.16.1
        ports:
        - containerPort: 80
  1. 새 YAML 파일을 적용한다.

      kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  2. 디플로이먼트가 새 이름으로 파드를 생성하고 이전 파드를 삭제하는 것을 확인한다.

      kubectl get pods -l app=nginx
    

레플리카 수를 늘려 애플리케이션 확장하기

새 YAML 파일을 적용하여 디플로이먼트의 파드 수를 늘릴 수 있다. 이 YAML 파일은 replicas를 4로 설정하여 디플로이먼트에 4개의 파드가 있어야 함을 명시하고 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. 새 YAML 파일을 적용한다.

     kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  2. 디플로이먼트에 4개의 파드가 있는지 확인한다.

     kubectl get pods -l app=nginx
    

    출력은 다음과 유사하다.

     NAME                               READY     STATUS    RESTARTS   AGE
     nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
     nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
     nginx-deployment-148880595-fxcez   1/1       Running   0          2m
     nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

디플로이먼트 삭제하기

이름으로 디플로이먼트를 삭제한다.

kubectl delete deployment nginx-deployment

ReplicationControllers -- 예전 방식

애플리케이션을 복제하여 생성하는 기본적인 방법은 내부적으로 레플리카셋(ReplicaSet)을 활용하는 디플로이먼트를 사용하는 것이다. 쿠버네티스에 디플로이먼트 및 레플리카셋이 도입되기 전에는 레플리케이션컨트롤러(ReplicationController)를 사용하여 복제 애플리케이션을 구성했었다.

다음 내용

2 - 단일 인스턴스 스테이트풀 애플리케이션 실행하기

이 페이지에서는 쿠버네티스 클러스터에서 퍼시스턴트볼륨(PersistentVolume)과 디플로이먼트(Deployment)를 사용하여, 단일 인스턴스 스테이트풀 애플리케이션을 실행하는 방법을 보인다. 해당 애플리케이션은 MySQL이다.

목적

  • 사용자 환경의 디스크를 참조하는 퍼시스턴트볼륨 생성하기
  • MySQL 디플로이먼트 생성하기
  • 알려진 DNS 이름으로 클러스터의 다른 파드에 MySQL 서비스 노출하기

시작하기 전에

  • 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

    버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

  • 여기에서 사용된 퍼시스턴트볼륨클레임을 만족하기 위하여, 기본 스토리지클래스정적으로(statically) 프로비저닝 된 퍼시스턴트볼륨을 갖는 퍼시스턴트 볼륨 프로비저너가 필요하다.

MySQL 배포하기

쿠버네티스 디플로이먼트를 생성하고 퍼시스턴트볼륨클레임(PersistentVolumeClaim)을 사용하는 기존 퍼시스턴트볼륨에 연결하여 스테이트풀 애플리케이션을 실행할 수 있다. 예를 들어, 다음 YAML 파일은 MySQL을 실행하고 퍼시스턴트볼륨클레임을 참조하는 디플로이먼트를 기술한다. 이 파일은 /var/lib/mysql에 대한 볼륨 마운트를 정의한 후에, 20G의 볼륨을 요청하는 퍼시트턴트볼륨클레임을 생성한다. 이 클레임은 요구 사항에 적합한 기존 볼륨이나 동적 프로비저너에 의해서 충족된다.

참고: config yaml 파일에 정의된 비밀번호는 안전하지 않다. 더 안전한 해결방법을 위해 쿠버네티스 시크릿 을 보자

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

  1. YAML 파일의 PV와 PVC를 배포한다.

     kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  2. YAML 파일의 다른 오브젝트들을 배포한다.

     kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  3. 디플로이먼트에 관한 정보를 확인한다.

     kubectl describe deployment mysql
    

    출력은 다음과 유사하다.

     Name:                 mysql
     Namespace:            default
     CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
     Labels:               app=mysql
     Annotations:          deployment.kubernetes.io/revision=1
     Selector:             app=mysql
     Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
     StrategyType:         Recreate
     MinReadySeconds:      0
     Pod Template:
       Labels:       app=mysql
       Containers:
        mysql:
         Image:      mysql:5.6
         Port:       3306/TCP
         Environment:
           MYSQL_ROOT_PASSWORD:      password
         Mounts:
           /var/lib/mysql from mysql-persistent-storage (rw)
       Volumes:
        mysql-persistent-storage:
         Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
         ClaimName:  mysql-pv-claim
         ReadOnly:   false
     Conditions:
       Type          Status  Reason
       ----          ------  ------
       Available     False   MinimumReplicasUnavailable
       Progressing   True    ReplicaSetUpdated
     OldReplicaSets:       <none>
     NewReplicaSet:        mysql-63082529 (1/1 replicas created)
     Events:
       FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
       ---------    --------    -----    ----                -------------    --------    ------            -------
       33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  4. 디플로이먼트로 생성된 파드를 나열한다.

     kubectl get pods -l app=mysql
    

    출력은 다음과 유사하다.

     NAME                   READY     STATUS    RESTARTS   AGE
     mysql-63082529-2z3ki   1/1       Running   0          3m
    
  5. 퍼시스턴트볼륨클레임을 살펴본다.

     kubectl describe pvc mysql-pv-claim
    

    출력은 다음과 유사하다.

     Name:         mysql-pv-claim
     Namespace:    default
     StorageClass:
     Status:       Bound
     Volume:       mysql-pv-volume
     Labels:       <none>
     Annotations:    pv.kubernetes.io/bind-completed=yes
                     pv.kubernetes.io/bound-by-controller=yes
     Capacity:     20Gi
     Access Modes: RWO
     Events:       <none>
    

MySQL 인스턴스 접근하기

이전의 YAML 파일은 클러스터의 다른 파드가 데이터베이스에 접근할 수 있는 서비스를 생성한다. clusterIP: None 서비스 옵션을 사용하면 서비스의 DNS 이름을 직접 파드의 IP 주소로 해석하도록 처리한다. 이 방법은 서비스에서 연결되는 파드가 오직 하나 뿐이고, 파드의 수를 더 늘릴 필요가 없는 경우에 가장 적합하다.

서버에 접속하기 위하여 MySQL 클라이언트를 실행한다.

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

이 명령어는 MySQL 클라이언트를 실행하는 파드를 클러스터에 생성하고, 서비스를 통하여 서버에 연결한다. 연결된다면, 스테이트풀 MySQL 데이터베이스가 실행 중임을 알 수 있다.

Waiting for pod default/mysql-client-274442439-zyp6i to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

mysql>

업데이트하기

kubectl apply 명령을 사용하여 기존과 동일한 방식으로 디플로이먼트의 이미지나 다른 부분을 변경할 수 있다. 스테이트풀 애플리케이션과 관련하여 몇 가지 주의 사항이 있다.

  • 애플리케이션을 스케일링하지 않는다. 이 설정은 단일 인스턴스 애플리케이션 전용이다. 기본적인 퍼시스턴트볼륨은 하나의 파드에서만 마운트할 수 있다. 클러스터 형태의 스테이트풀 애플리케이션에 대해서는 스테이트풀셋을 보자.
  • 디플로이먼트 구성 YAML 파일에서 strategy: type: Recreate 를 사용한다. 이는 쿠버네티스가 롤링 업데이트를 사용하지 않도록 지시한다. 동시에 두 개 이상의 파드를 생성할 수 없으므로, 롤링 업데이트는 일어나지 않게 된다. Recreate 전략을 사용하면 변경된 구성으로 새로운 파드를 생성하기에 앞서 기존의 파드를 중단한다.

디플로이먼트 삭제하기

이름으로 배포된 오브젝트를 삭제한다.

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

퍼시스턴트볼륨을 수동으로 프로비저닝한 경우라면, 동일하게 수동으로 삭제하고 기본 리소스도 해제해야 한다. 동적 프로비저너를 사용한 경우, 퍼시스턴트볼륨클레임이 삭제되었을 때에 퍼시스턴트볼륨 또한 자동으로 삭제된다. 일부 동적 프로비저너(EBS 와 PD와 같은)는 퍼시스턴트볼륨을 삭제할 때에 기본 리소스도 해제한다.

다음 내용

3 - 스테이트풀셋(StatefulSet) 삭제하기

이 작업은 스테이트풀셋을 삭제하는 방법을 설명한다.

시작하기 전에

  • 이 작업은 클러스터에 스테이트풀셋으로 표시되는 애플리케이션이 있다고 가정한다.

스테이트풀셋 삭제

쿠버네티스에서 다른 리소스를 삭제하는 것과 같은 방식으로 스테이트풀셋을 삭제할 수 있다. kubectl delete 명령어를 사용하고 파일 또는 이름으로 스테이트풀셋을 지정하자.

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>

스테이트풀셋 자체를 삭제한 후 연결된 헤드리스 서비스는 별도로 삭제해야 할 수도 있다.

kubectl delete service <service-name>

kubectl을 통해 스테이트풀셋을 삭제하면, 스테이트풀셋의 크기가 0으로 설정되고 이로 인해 스테이트풀셋에 포함된 모든 파드가 삭제된다. 파드가 아닌 스테이트풀셋만 삭제하려면, --cascade=orphan 옵션을 사용한다. 예시는 다음과 같다.

kubectl delete -f <file.yaml> --cascade=orphan

kubectl delete--cascade=orphan 를 사용하면 스테이트풀셋 오브젝트가 삭제된 후에도 스테이트풀셋에 의해 관리된 파드는 남게 된다. 만약 파드가 app=myapp 레이블을 갖고 있다면, 다음과 같이 파드를 삭제할 수 있다.

kubectl delete pods -l app=myapp

퍼시스턴트볼륨(PersistentVolume)

스테이트풀셋의 파드들을 삭제하는 것이 연결된 볼륨을 삭제하는 것은 아니다. 이것은 볼륨을 삭제하기 전에 볼륨에서 데이터를 복사할 수 있는 기회를 준다. 파드가 종료된 후 PVC를 삭제하면 스토리지 클래스와 반환 정책에 따라 백업 퍼시스턴트볼륨 삭제가 트리거될 수 있다. 클레임 삭제 후 볼륨에 접근할 수 있다고 가정하면 안된다.

스테이트풀셋의 완벽한 삭제

연결된 파드를 포함해서 스테이트풀셋의 모든 것을 삭제하기 위해 다음과 같이 일련의 명령을 실행한다.

grace=$(kubectl get pods <stateful-set-pod> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app=myapp
sleep $grace
kubectl delete pvc -l app=myapp

위의 예에서 파드에는 app=myapp 라는 레이블이 있다. 사용자에게 적절한 레이블로 대체하자.

스테이트풀셋 파드의 강제 삭제

스테이트풀셋의 일부 파드가 오랫동안 'Terminating' 또는 'Unknown' 상태에 있는 경우, apiserver에 수동적으로 개입하여 파드를 강제 삭제할 수도 있다. 이것은 잠재적으로 위험한 작업이다. 자세한 설명은 스테이트풀셋 파드 강제 삭제하기를 참고한다.

다음 내용

스테이트풀셋 파드 강제 삭제하기에 대해 더 알아보기.

4 - 스테이트풀셋(StatefulSet) 파드 강제 삭제하기

이 페이지에서는 스테이트풀셋의 일부인 파드를 삭제하는 방법을 보여주고 이 과정에서 고려해야 할 사항을 설명한다.

시작하기 전에

  • 이것은 상당히 고급 태스크이며 스테이트풀셋 고유의 속성 중 일부를 위반할 가능성이 있다.
  • 계속하기 전에 아래 나열된 고려 사항을 숙지하도록 한다.

스테이트풀셋 고려 사항

정상적인 스테이트풀셋의 작동에서는 스테이트풀셋 파드를 강제로 삭제할 필요가 절대 없다. 스테이트풀셋 컨트롤러는 스테이트풀셋의 멤버 생성, 스케일링, 삭제를 담당한다. 서수 0부터 N-1까지 지정된 수의 파드가 활성 상태이고 준비되었는지 확인한다. 스테이트풀셋은 언제든지 클러스터에서 실행 중인 지정된 신원을 가진 최대 하나의 파드가 있는지 확인한다. 이를 스테이트풀셋에서 제공하는 최대 하나 의미론이라고 한다.

수동 강제 삭제는 스테이트풀셋 고유의 최대 하나 의미론을 위반할 가능성이 있으므로 주의해서 수행해야 한다. 스테이트풀셋은 안정적인 네트워크 신원과 안정적인 스토리지가 필요한 분산 클러스터 애플리케이션을 실행하는 데 사용할 수 있다. 이러한 애플리케이션은 종종 고정된 신원을 가진 고정된 수의 구성원 앙상블에 의존하는 구성을 가진다. 여러 구성원이 동일한 신원을 갖는 것은 재앙이 될 수 있으며 데이터 손실로 이어질 수 있다(예: 쿼럼 기반 시스템의 스플릿 브레인(split-brain) 시나리오).

파드 삭제

다음 명령을 사용하여 파드를 단계적으로 삭제할 수 있다.

kubectl delete pods <pod>

위의 내용이 단계적인 종료로 이어지려면 파드가 pod.Spec.TerminationGracePeriodSeconds를 0으로 지정하지 않아야 한다. pod.Spec.TerminationGracePeriodSeconds를 0초로 설정하는 관행은 안전하지 않으며 스테이트풀셋 파드에서 강력히 권장하지 않는다. 단계적 삭제는 안전하며 kubelet이 apiserver에서 이름을 삭제하기 전에 파드가 정상적으로 종료되도록 한다.

노드에 연결할 수 없는 경우 파드는 자동으로 삭제되지 않는다. 연결할 수 없는 노드에서 실행 중인 파드는 타임아웃 후에 'Terminating'이나 'Unknown' 상태가 된다.

사용자가 연결할 수 없는 노드에서 파드를 단계적으로 삭제하려고 하면 파드가 이러한 상태에 들어갈 수도 있다. 이러한 상태의 파드를 apiserver에서 제거할 수 있는 유일한 방법은 다음과 같다.

  • 노드 오브젝트가 삭제된다(사용자 또는 노드 컨트롤러에 의해).
  • 응답하지 않는 노드의 kubelet이 응답을 시작하고 파드를 종료하여 apiserver에서 항목을 제거한다.
  • 사용자가 파드를 강제로 삭제한다.

권장되는 모범 사례는 첫 번째 또는 두 번째 방법을 사용하는 것이다. 노드가 죽은 것으로 확인되면(예: 네트워크에서 영구적으로 연결이 끊기거나 전원이 꺼진 경우 등) 노드 오브젝트를 삭제한다. 노드에 네트워크 파티션이 있는 경우 이를 해결하거나 해결될 때까지 기다린다. 파티션이 복구되면 kubelet은 파드 삭제를 완료하고 apiserver에서 해당 이름을 해제한다.

일반적으로 시스템은 파드가 노드에서 더 이상 실행되지 않거나 노드가 관리자에 의해 삭제되면 삭제를 완료한다. 파드를 강제로 삭제하여 이를 재정의할 수 있다.

강제 삭제

강제 삭제는 파드가 종료되었다는 kubelet의 확인을 기다리지 않는다. 강제 삭제가 파드를 죽이는 데 성공했는지 여부와 관계없이 즉시 apiserver에서 이름을 해제한다. 이렇게 하면 스테이트풀셋 컨트롤러가 동일한 신원으로 대체 파드를 생성할 수 있다. 이것은 여전히 실행 중인 파드와 중복될 수 있으며, 해당 파드가 여전히 스테이트풀셋의 다른 멤버와 통신할 수 있다면 스테이트풀셋이 보장하도록 설계된 최대 하나 의미론을 위반할 것이다.

스테이트풀셋 파드를 강제로 삭제하는 것은 문제의 파드가 스테이트풀셋의 다른 파드와 다시는 접촉하지 않으며 대체 생성을 위해 해당 이름을 안전하게 해제할 수 있다고 주장하는 것이다.

kubectl 버전 >= 1.5를 사용하여 파드를 강제로 삭제하려면, 다음을 수행한다.

kubectl delete pods <pod> --grace-period=0 --force

kubectl <= 1.4 버전을 사용하는 경우, --force 옵션을 생략하고 다음을 사용해야 한다.

kubectl delete pods <pod> --grace-period=0

이러한 명령 후에도 파드가 Unknown 상태에서 멈추면, 다음 명령을 사용하여 클러스터에서 파드를 제거한다.

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

항상 관련된 위험에 대해 완전히 이해한 상태에서 주의 깊게 스테이트풀셋 파드의 강제 삭제를 수행한다.

다음 내용

스테이트풀셋 디버깅하기에 대해 더 알아보기.

5 - Horizontal Pod Autoscaler

Horizontal Pod Autoscaler는 CPU 사용량 (또는 사용자 정의 메트릭, 아니면 다른 애플리케이션 지원 메트릭)을 관찰하여 레플리케이션 컨트롤러(ReplicationController), 디플로이먼트(Deployment), 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)의 파드 개수를 자동으로 스케일한다. Horizontal Pod Autoscaler는 크기를 조정할 수 없는 오브젝트(예: 데몬셋(DaemonSet))에는 적용되지 않는다.

Horizontal Pod Autoscaler는 쿠버네티스 API 리소스 및 컨트롤러로 구현된다. 리소스는 컨트롤러의 동작을 결정한다. 컨트롤러는 평균 CPU 사용률, 평균 메모리 사용률 또는 다른 커스텀 메트릭과 같은 관찰 대상 메트릭이 사용자가 지정한 목표값과 일치하도록 레플리케이션 컨트롤러 또는 디플로이먼트에서 레플리카 개수를 주기적으로 조정한다.

Horizontal Pod Autoscaler는 어떻게 작동하는가?

Horizontal Pod Autoscaler 다이어그램

Horizontal Pod Autoscaler는 컨트롤러 관리자의 --horizontal-pod-autoscaler-sync-period 플래그(기본값은 15초)에 의해 제어되는 주기를 가진 컨트롤 루프로 구현된다.

각 주기 동안 컨트롤러 관리자는 각 HorizontalPodAutoscaler 정의에 지정된 메트릭에 대해 리소스 사용률을 질의한다. 컨트롤러 관리자는 리소스 메트릭 API(파드 단위 리소스 메트릭 용) 또는 사용자 지정 메트릭 API(다른 모든 메트릭 용)에서 메트릭을 가져온다.

  • 파드 단위 리소스 메트릭(예 : CPU)의 경우 컨트롤러는 HorizontalPodAutoscaler가 대상으로하는 각 파드에 대한 리소스 메트릭 API에서 메트릭을 가져온다. 그런 다음, 목표 사용률 값이 설정되면, 컨트롤러는 각 파드의 컨테이너에 대한 동등한 자원 요청을 퍼센트 단위로 하여 사용률 값을 계산한다. 대상 원시 값이 설정된 경우 원시 메트릭 값이 직접 사용된다. 그리고, 컨트롤러는 모든 대상 파드에서 사용된 사용률의 평균 또는 원시 값(지정된 대상 유형에 따라 다름)을 가져와서 원하는 레플리카의 개수를 스케일하는데 사용되는 비율을 생성한다.

    파드의 컨테이너 중 일부에 적절한 리소스 요청이 설정되지 않은 경우, 파드의 CPU 사용률은 정의되지 않으며, 따라서 오토스케일러는 해당 메트릭에 대해 아무런 조치도 취하지 않는다. 오토스케일링 알고리즘의 작동 방식에 대한 자세한 내용은 아래 알고리즘 세부 정보 섹션을 참조하기 바란다.

  • 파드 단위 사용자 정의 메트릭의 경우, 컨트롤러는 사용률 값이 아닌 원시 값을 사용한다는 점을 제외하고는 파드 단위 리소스 메트릭과 유사하게 작동한다.

  • 오브젝트 메트릭 및 외부 메트릭의 경우, 문제의 오브젝트를 표현하는 단일 메트릭을 가져온다. 이 메트릭은 목표 값과 비교되어 위와 같은 비율을 생성한다. autoscaling/v2beta2 API 버전에서는, 비교가 이루어지기 전에 해당 값을 파드의 개수로 선택적으로 나눌 수 있다.

HorizontalPodAutoscaler는 보통 일련의 API 집합(metrics.k8s.io, custom.metrics.k8s.io, external.metrics.k8s.io)에서 메트릭을 가져온다. metrics.k8s.io API는 대개 별도로 시작해야 하는 메트릭-서버에 의해 제공된다. 더 자세한 정보는 메트릭-서버를 참조한다.

자세한 사항은 메트릭 API를 위한 지원을 참조한다.

오토스케일러는 스케일 하위 리소스를 사용하여 상응하는 확장 가능 컨트롤러(예: 레플리케이션 컨트롤러, 디플로이먼트, 레플리케이션 셋)에 접근한다. 스케일은 레플리카의 개수를 동적으로 설정하고 각 현재 상태를 검사 할 수 있게 해주는 인터페이스이다. 하위 리소스 스케일에 대한 자세한 내용은 여기에서 확인할 수 있다.

알고리즘 세부 정보

가장 기본적인 관점에서, Horizontal Pod Autoscaler 컨트롤러는 원하는(desired) 메트릭 값과 현재(current) 메트릭 값 사이의 비율로 작동한다.

원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]

예를 들어 현재 메트릭 값이 200m이고 원하는 값이 100m인 경우 200.0 / 100.0 == 2.0이므로 복제본 수가 두 배가 된다. 만약 현재 값이 50m 이면, 50.0 / 100.0 == 0.5 이므로 복제본 수를 반으로 줄일 것이다. 비율이 1.0(0.1을 기본값으로 사용하는 -horizontal-pod-autoscaler-tolerance 플래그를 사용하여 전역적으로 구성 가능한 허용 오차 내)에 충분히 가깝다면 스케일링을 건너 뛸 것이다.

targetAverageValue 또는 targetAverageUtilization가 지정되면, currentMetricValue는 HorizontalPodAutoscaler의 스케일 목표 안에 있는 모든 파드에서 주어진 메트릭의 평균을 취하여 계산된다. 허용치를 확인하고 최종 값을 결정하기 전에, 파드 준비 상태와 누락된 메트릭을 고려한다.

삭제 타임 스탬프가 설정된 모든 파드(즉, 종료 중인 파드) 및 실패한 파드는 모두 폐기된다.

특정 파드에 메트릭이 누락된 경우, 나중을 위해 처리를 미뤄두는데, 이와 같이 누락된 메트릭이 있는 모든 파드는 최종 스케일 량을 조정하는데 사용된다.

CPU를 스케일할 때, 어떤 파드라도 아직 준비가 안되었거나 (즉, 여전히 초기화 중인 경우) * 또는 * 파드의 최신 메트릭 포인트가 준비되기 전이라면, 마찬가지로 해당 파드는 나중에 처리된다.

기술적 제약으로 인해, HorizontalPodAutoscaler 컨트롤러는 특정 CPU 메트릭을 나중에 사용할지 말지 결정할 때, 파드가 준비되는 시작 시간을 정확하게 알 수 없다. 대신, 파드가 아직 준비되지 않았고 시작 이후 짧은 시간 내에 파드가 준비되지 않은 상태로 전환된다면, 해당 파드를 "아직 준비되지 않음(not yet ready)"으로 간주한다. 이 값은 --horizontal-pod-autoscaler-initial-readiness-delay 플래그로 설정되며, 기본값은 30초 이다. 일단 파드가 준비되고 시작된 후 구성 가능한 시간 이내이면, 준비를 위한 어떠한 전환이라도 이를 시작 시간으로 간주한다. 이 값은 --horizontal-pod-autoscaler-cpu-initialization-period 플래그로 설정되며 기본값은 5분이다.

현재 메트릭 값 / 원하는 메트릭 값 기본 스케일 비율은 나중에 사용하기로 되어 있거나 위에서 폐기되지 않은 남아있는 파드를 사용하여 계산된다.

누락된 메트릭이 있는 경우, 파드가 스케일 다운의 경우 원하는 값의 100%를 소비하고 스케일 업의 경우 0%를 소비한다고 가정하여 평균을 보다 보수적으로 재계산한다. 이것은 잠재적인 스케일의 크기를 약화시킨다.

또한 아직-준비되지-않은 파드가 있는 경우 누락된 메트릭이나 아직-준비되지-않은 파드를 고려하지 않고 스케일 업했을 경우, 아직-준비되지-않은 파드가 원하는 메트릭의 0%를 소비한다고 보수적으로 가정하고 스케일 확장의 크기를 약화시킨다.

아직-준비되지-않은 파드나 누락된 메트릭을 고려한 후에 사용 비율을 다시 계산한다. 새 비율이 스케일 방향을 바꾸거나, 허용 오차 내에 있으면 스케일링을 건너뛴다. 그렇지 않으면, 새 비율을 사용하여 스케일한다.

평균 사용량에 대한 원래 값은 새로운 사용 비율이 사용되는 경우에도 아직-준비되지-않은 파드 또는 누락된 메트릭에 대한 고려없이 HorizontalPodAutoscaler 상태를 통해 다시 보고된다.

HorizontalPodAutoscaler에 여러 메트릭이 지정된 경우, 이 계산은 각 메트릭에 대해 수행된 다음 원하는 레플리카 수 중 가장 큰 값이 선택된다. 이러한 메트릭 중 어떠한 것도 원하는 레플리카 수로 변환할 수 없는 경우(예 : 메트릭 API에서 메트릭을 가져오는 중 오류 발생) 스케일을 건너뛴다. 이는 하나 이상의 메트릭이 현재 값보다 높은 desiredReplicas 을 제공하는 경우 HPA가 여전히 확장할 수 있음을 의미한다.

마지막으로, HPA가 목표를 스케일하기 직전에 스케일 권장 사항이 기록된다. 컨트롤러는 구성 가능한 창(window) 내에서 가장 높은 권장 사항을 선택하도록 해당 창 내의 모든 권장 사항을 고려한다. 이 값은 --horizontal-pod-autoscaler-downscale-stabilization 플래그를 사용하여 설정할 수 있고, 기본값은 5분이다. 즉, 스케일 다운이 점진적으로 발생하여 급격히 변동하는 메트릭 값의 영향을 완만하게 한다.

API 오브젝트

Horizontal Pod Autoscaler는 쿠버네티스 autoscaling API 그룹의 API 리소스이다. CPU에 대한 오토스케일링 지원만 포함하는 안정된 버전은 autoscaling/v1 API 버전에서 찾을 수 있다.

메모리 및 사용자 정의 메트릭에 대한 스케일링 지원을 포함하는 베타 버전은 autoscaling/v2beta2에서 확인할 수 있다. autoscaling/v2beta2에서 소개된 새로운 필드는 autoscaling/v1로 작업할 때 어노테이션으로 보존된다.

HorizontalPodAutoscaler API 오브젝트 생성시 지정된 이름이 유효한 DNS 서브도메인 이름인지 확인해야 한다. API 오브젝트에 대한 자세한 내용은 HorizontalPodAutoscaler 오브젝트에서 찾을 수 있다.

kubectl에서 Horizontal Pod Autoscaler 지원

Horizontal Pod Autoscaler는 모든 API 리소스와 마찬가지로 kubectl에 의해 표준 방식으로 지원된다. kubectl create 커맨드를 사용하여 새로운 오토스케일러를 만들 수 있다. kubectl get hpa로 오토스케일러 목록을 조회할 수 있고, kubectl describe hpa로 세부 사항을 확인할 수 있다. 마지막으로 kubectl delete hpa를 사용하여 오토스케일러를 삭제할 수 있다.

또한 Horizontal Pod Autoscaler를 생성할 수 있는 kubectl autoscale이라는 특별한 명령이 있다. 예를 들어 kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80을 실행하면 레플리케이션 셋 foo 에 대한 오토스케일러가 생성되고, 목표 CPU 사용률은 80 %, 그리고 2와 5 사이의 레플리카 개수로 설정된다. kubectl autoscale에 대한 자세한 문서는 여기에서 찾을 수 있다.

롤링 업데이트 중 오토스케일링

쿠버네티스는 디플로이먼트에 대한 롤링 업데이트를 지원한다. 이 경우, 디플로이먼트가 기저 레플리카셋을 알아서 관리한다. 디플로이먼트에 오토스케일링을 설정하려면, 각 디플로이먼트에 대한 HorizontalPodAutoscaler를 생성한다. HorizontalPodAutoscaler는 디플로이먼트의 replicas 필드를 관리한다. 디플로이먼트 컨트롤러는 기저 레플리카셋에 replicas 값을 적용하여 롤아웃 과정 중/이후에 적절한 숫자까지 늘어나도록 한다.

오토스케일된 레플리카가 있는 스테이트풀셋의 롤링 업데이트를 수행하면, 스테이트풀셋이 직접 파드의 숫자를 관리한다(즉, 레플리카셋과 같은 중간 리소스가 없다).

쿨-다운 / 지연에 대한 지원

Horizontal Pod Autoscaler를 사용하여 레플리카 그룹의 스케일을 관리할 때, 평가된 메트릭의 동적인 특징 때문에 레플리카 수가 자주 변동할 수 있다. 이것은 때로는 스래싱 (thrashing) 이라고도 한다.

v1.6 부터 클러스터 운영자는 kube-controller-manager 컴포넌트의 플래그로 노출된 글로벌 HPA 설정을 튜닝하여 이 문제를 완화할 수 있다.

v1.12부터는 새로운 알고리즘 업데이트가 업스케일 지연에 대한 필요성을 제거하였다.

  • --horizontal-pod-autoscaler-downscale-delay : 다운스케일이 안정화되기까지의 시간 간격을 지정한다. Horizontal Pod Autoscaler는 이전의 권장하는 크기를 기억하고, 이 시간 간격에서의 가장 큰 크기에서만 작동한다. 기본값은 5분(5m0s)이다.

리소스 메트릭 지원

모든 HPA 대상은 스케일링 대상에서 파드의 리소스 사용량을 기준으로 스케일링할 수 있다. 파드의 명세를 정의할 때는 cpumemory 와 같은 리소스 요청을 지정해야 한다. 이것은 리소스 사용률을 결정하는 데 사용되며 HPA 컨트롤러에서 대상을 스케일링하거나 축소하는 데 사용한다. 리소스 사용률 기반 스케일링을 사용하려면 다음과 같은 메트릭 소스를 지정해야 한다.

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

이 메트릭을 사용하면 HPA 컨트롤러는 스케일링 대상에서 파드의 평균 사용률을 60%로 유지한다. 사용률은 파드의 요청된 리소스에 대한 현재 리소스 사용량 간의 비율이다. 사용률 계산 및 평균 산출 방법에 대한 자세한 내용은 알고리즘을 참조한다.

컨테이너 리소스 메트릭

FEATURE STATE: Kubernetes v1.20 [alpha]

HorizontalPodAutoscaler 는 대상 리소스를 스케일링하기 위해 HPA가 파드 집합에서 개별 컨테이너의 리소스 사용량을 추적할 수 있는 컨테이너 메트릭 소스도 지원한다. 이를 통해 특정 파드에서 가장 중요한 컨테이너의 스케일링 임계값을 구성할 수 있다. 예를 들어 웹 애플리케이션 프로그램과 로깅 사이드카가 있는 경우 사이드카 컨테이너와 해당 리소스 사용을 무시하고 웹 애플리케이션의 리소스 사용을 기준으로 스케일링할 수 있다.

대상 리소스를 다른 컨테이너 세트를 사용하여 새 파드 명세를 갖도록 수정하는 경우 새로 추가된 컨테이너도 스케일링에 사용해야 한다면 HPA 사양을 수정해야 한다. 메트릭 소스에 지정된 컨테이너가 없거나 파드의 하위 집합에만 있는 경우 해당 파드는 무시되고 권장 사항이 다시 계산된다. 계산에 대한 자세한 내용은 알고리즘을 을 참조한다. 컨테이너 리소스를 오토스케일링에 사용하려면 다음과 같이 메트릭 소스를 정의한다.

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

위의 예에서 HPA 컨트롤러는 모든 파드의 application 컨테이너에 있는 CPU의 평균 사용률이 60%가 되도록 대상을 조정한다.

멀티 메트릭을 위한 지원

Kubernetes 1.6은 멀티 메트릭을 기반으로 스케일링을 지원한다. autoscaling/v2beta2 API 버전을 사용하여 Horizontal Pod Autoscaler가 스케일을 조정할 멀티 메트릭을 지정할 수 있다. 그런 다음 Horizontal Pod Autoscaler 컨트롤러가 각 메트릭을 평가하고, 해당 메트릭을 기반으로 새 스케일을 제안한다. 제안된 스케일 중 가장 큰 것이 새로운 스케일로 사용된다.

사용자 정의 메트릭을 위한 지원

쿠버네티스 1.6에서는 Horizontal Pod Autoscaler에서 사용자 정의 메트릭을 사용할 수 있도록 지원한다. autoscaling/v2beta2 API에서 사용할 Horizontal Pod Autoscaler에 대한 사용자 정의 메트릭을 추가 할 수 있다. 그리고 쿠버네티스는 새 사용자 정의 메트릭 API에 질의하여 적절한 사용자 정의 메트릭의 값을 가져온다.

요구 사항은 메트릭을 위한 지원을 참조한다.

메트릭 API를 위한 지원

기본적으로 HorizontalPodAutoscaler 컨트롤러는 일련의 API에서 메트릭을 검색한다. 이러한 API에 접속하려면 클러스터 관리자는 다음을 확인해야 한다.

  • API 애그리게이션 레이어 활성화

  • 해당 API 등록:

    • 리소스 메트릭의 경우, 일반적으로 이것은 메트릭-서버가 제공하는 metrics.k8s.io API이다. 클러스터 애드온으로 시작할 수 있다.

    • 사용자 정의 메트릭의 경우, 이것은 custom.metrics.k8s.io API이다. 메트릭 솔루션 공급 업체에서 제공하는 "어댑터" API 서버에서 제공한다. 메트릭 파이프라인 또는 알려진 솔루션 목록으로 확인한다. 직접 작성하고 싶다면 샘플을 확인한다.

    • 외부 메트릭의 경우, 이것은 external.metrics.k8s.io API이다. 위에 제공된 사용자 정의 메트릭 어댑터에서 제공될 수 있다.

이런 다양한 메트릭 경로와 각각의 다른 점에 대한 상세 내용은 관련 디자인 제안서인 HPA V2, custom.metrics.k8s.io, external.metrics.k8s.io를 참조한다.

어떻게 사용하는지에 대한 예시는 커스텀 메트릭 사용하는 작업 과정외부 메트릭스 사용하는 작업 과정을 참조한다.

구성가능한 스케일링 동작 지원

v1.18 부터 v2beta2 API는 HPA behavior 필드를 통해 스케일링 동작을 구성할 수 있다. 동작은 behavior 필드 아래의 scaleUp 또는 scaleDown 섹션에서 스케일링 업과 다운을 위해 별도로 지정된다. 안정화 윈도우는 스케일링 대상에서 레플리카 수의 플래핑(flapping)을 방지하는 양방향에 대해 지정할 수 있다. 마찬가지로 스케일링 정책을 지정하면 스케일링 중 레플리카 변경 속도를 제어할 수 있다.

스케일링 정책

스펙의 behavior 섹션에 하나 이상의 스케일링 폴리시를 지정할 수 있다. 폴리시가 여러 개 지정된 경우 가장 많은 양의 변경을 허용하는 정책이 기본적으로 선택된 폴리시이다. 다음 예시는 스케일 다운 중 이 동작을 보여준다.

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds 는 폴리시가 참(true)으로 유지되어야 하는 기간을 나타낸다. 첫 번째 정책은 (파드들) 이 1분 내에 최대 4개의 레플리카를 스케일 다운할 수 있도록 허용한다. 두 번째 정책은 비율 로 현재 레플리카의 최대 10%를 1분 내에 스케일 다운할 수 있도록 허용한다.

기본적으로 가장 많은 변경을 허용하는 정책이 선택되기에 두 번째 정책은 파드의 레플리카 수가 40개를 초과하는 경우에만 사용된다. 레플리카가 40개 이하인 경우 첫 번째 정책이 적용된다. 예를 들어 80개의 레플리카가 있고 대상을 10개의 레플리카로 축소해야 하는 경우 첫 번째 단계에서 8개의 레플리카가 스케일 다운 된다. 레플리카의 수가 72개일 때 다음 반복에서 파드의 10%는 7.2 이지만, 숫자는 8로 올림된다. 오토스케일러 컨트롤러의 각 루프에서 변경될 파드의 수는 현재 레플리카의 수에 따라 재계산된다. 레플리카의 수가 40 미만으로 떨어지면 첫 번째 폴리시 (파드들) 가 적용되고 한번에 4개의 레플리카가 줄어든다.

확장 방향에 대해 selectPolicy 필드를 확인하여 폴리시 선택을 변경할 수 있다. 레플리카의 수를 최소로 변경할 수 있는 폴리시를 선택하는 최소(Min)로 값을 설정한다. 값을 Disabled 로 설정하면 해당 방향으로 스케일링이 완전히 비활성화 된다.

안정화 윈도우

안정화 윈도우는 스케일링에 사용되는 메트릭이 계속 변동할 때 레플리카의 플래핑을 다시 제한하기 위해 사용된다. 안정화 윈도우는 스케일링을 방지하기 위해 과거부터 계산된 의도한 상태를 고려하는 오토스케일링 알고리즘에 의해 사용된다. 다음의 예시에서 scaleDown 에 대해 안정화 윈도우가 지정되어 있다.

scaleDown:
  stabilizationWindowSeconds: 300

메트릭이 대상을 축소해야하는 것을 나타내는 경우 알고리즘은 이전에 계산된 의도한 상태를 살펴보고 지정된 간격의 최고 값을 사용한다. 위의 예시에서 지난 5분 동안 모든 의도한 상태가 고려된다.

기본 동작

사용자 지정 스케일링을 사용하려면 일부 필드를 지정해야 한다. 사용자 정의해야 하는 값만 지정할 수 있다. 이러한 사용자 지정 값은 기본값과 병합된다. 기본값은 HPA 알고리즘의 기존 동작과 일치한다.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

안정화 윈도우의 스케일링 다운의 경우 300 초 (또는 제공된 경우--horizontal-pod-autoscaler-downscale-stabilization 플래그의 값)이다. 스케일링 다운에서는 현재 실행 중인 레플리카의 100%를 제거할 수 있는 단일 정책만 있으며, 이는 스케일링 대상을 최소 허용 레플리카로 축소할 수 있음을 의미한다. 스케일링 업에는 안정화 윈도우가 없다. 메트릭이 대상을 스케일 업해야 한다고 표시된다면 대상이 즉시 스케일 업된다. 두 가지 폴리시가 있다. HPA가 정상 상태에 도달 할 때까지 15초 마다 4개의 파드 또는 현재 실행 중인 레플리카의 100% 가 추가된다.

예시: 다운스케일 안정화 윈도우 변경

사용자 지정 다운스케일 안정화 윈도우를 1분 동안 제공하기 위해 다음 동작이 HPA에 추가된다.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

예시: 스케일 다운 비율 제한

HPA에 의해 파드가 제거되는 속도를 분당 10%로 제한하기 위해 다음 동작이 HPA에 추가된다.

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

마지막으로 5개의 파드를 드롭하기 위해 다른 폴리시를 추가하고, 최소 선택 전략을 추가할 수 있다. 분당 5개 이하의 파드가 제거되지 않도록, 고정 크기가 5인 두 번째 축소 정책을 추가하고, selectPolicy 를 최소로 설정하면 된다. selectPolicyMin 으로 설정하면 자동 스케일러가 가장 적은 수의 파드에 영향을 주는 정책을 선택함을 의미한다.

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

예시: 스케일 다운 비활성화

selectPolicyDisabled 값은 주어진 방향으로의 스케일링을 끈다. 따라서 다운 스케일링을 방지하기 위해 다음 폴리시가 사용된다.

behavior:
  scaleDown:
    selectPolicy: Disabled

암시적 유지 관리 모드 비활성화

HPA 구성 자체를 변경할 필요없이 대상에 대한 HPA를 암시적으로 비활성화할 수 있다. 대상의 의도한 레플리카 수가 0으로 설정되고, HPA의 최소 레플리카 수가 0 보다 크면, 대상의 의도한 레플리카 수 또는 HPA의 최소 레플리카 수를 수동으로 조정하여 다시 활성화할 때까지 HPA는 대상 조정을 중지한다(그리고 ScalingActive 조건 자체를 false로 설정).

다음 내용

6 - Horizontal Pod Autoscaler 연습

Horizontal Pod Autoscaler는 CPU 사용량(또는 베타 지원의 다른 애플리케이션 지원 메트릭)을 관찰하여 레플리케이션 컨트롤러, 디플로이먼트, 레플리카셋(ReplicaSet) 또는 스테이트풀셋(StatefulSet)의 파드 개수를 자동으로 스케일한다.

이 문서는 php-apache 서버를 대상으로 Horizontal Pod Autoscaler를 동작해보는 예제이다. Horizontal Pod Autoscaler 동작과 관련된 더 많은 정보를 위해서는 Horizontal Pod Autoscaler 사용자 가이드를 참고하기 바란다.

시작하기 전에

이 예제는 버전 1.2 또는 이상의 쿠버네티스 클러스터와 kubectl을 필요로 한다. 메트릭 서버 모니터링을 클러스터에 배포하여 메트릭 API를 통해 메트릭을 제공해야 한다. Horizontal Pod Autoscaler가 메트릭을 수집할때 해당 API를 사용한다. 메트릭-서버를 배포하는 방법에 대해 배우려면, 메트릭-서버 문서를 참고한다.

Horizontal Pod Autoscaler에 다양한 자원 메트릭을 적용하고자 하는 경우, 버전 1.6 또는 이상의 쿠버네티스 클러스터와 kubectl를 사용해야 한다. 사용자 정의 메트릭을 사용하기 위해서는, 클러스터가 사용자 정의 메트릭 API를 제공하는 API 서버와 통신할 수 있어야 한다. 마지막으로 쿠버네티스 오브젝트와 관련이 없는 메트릭을 사용하는 경우, 버전 1.10 또는 이상의 쿠버네티스 클러스터와 kubectl을 사용해야 하며, 외부 메트릭 API와 통신이 가능해야 한다. 자세한 사항은 Horizontal Pod Autoscaler 사용자 가이드를 참고하길 바란다.

php-apache 서버 구동 및 노출

Horizontal Pod Autoscaler 시연을 위해 php-apache 이미지를 맞춤 제작한 Docker 이미지를 사용한다. Dockerfile은 다음과 같다.

FROM php:5-apache
COPY index.php /var/www/html/index.php
RUN chmod a+rx index.php

index.php는 CPU 과부하 연산을 수행한다.

<?php
  $x = 0.0001;
  for ($i = 0; $i <= 1000000; $i++) {
    $x += sqrt($x);
  }
  echo "OK!";
?>

첫 번째 단계로, 다음 구성을 사용해서 실행 중인 이미지의 디플로이먼트를 시작하고 서비스로 노출시킨다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  replicas: 1
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: k8s.gcr.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m

---

apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

다음의 명령어를 실행한다.

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created

Horizontal Pod Autoscaler 생성

이제 서비스가 동작중이므로, kubectl autoscale를 사용하여 오토스케일러를 생성한다. 다음 명령어는 첫 번째 단계에서 만든 php-apache 디플로이먼트 파드의 개수를 1부터 10 사이로 유지하는 Horizontal Pod Autoscaler를 생성한다. 간단히 얘기하면, HPA는 (디플로이먼트를 통한) 평균 CPU 사용량을 50%로 유지하기 위하여 레플리카의 개수를 늘리고 줄인다. kubectl run으로 각 파드는 200 밀리코어를 요청하므로, 여기서 말하는 평균 CPU 사용은 100 밀리코어를 말한다. 이에 대한 자세한 알고리즘은 여기를 참고하기 바란다.

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled

실행 중인 오토스케일러의 현재 상태를 확인해본다.

kubectl get hpa
NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

아직 서버로 어떠한 요청도 하지 않았기 때문에, 현재 CPU 소비는 0%임을 확인할 수 있다. (TARGET은 디플로이먼트에 의해 제어되는 파드들의 평균을 나타낸다)

부하 증가

이번에는 부하가 증가함에 따라 오토스케일러가 어떻게 반응하는지를 살펴볼 것이다. 먼저 컨테이너를 하나 실행하고, php-apache 서비스에 무한루프의 쿼리를 전송한다(다른 터미널을 열어 수행하기 바란다).

kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

실행 후, 약 1분 정도 후에 CPU 부하가 올라가는 것을 볼 수 있다.

kubectl get hpa
NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

CPU 소비가 305%까지 증가하였다. 결과적으로, 디플로이먼트의 레플리카 개수는 7개까지 증가하였다.

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

부하 중지

본 예제를 마무리하기 위해 부하를 중단시킨다.

busybox 컨테이너를 띄운 터미널에서, <Ctrl> + C로 부하 발생을 중단시킨다.

그런 다음 (몇 분 후에) 결과를 확인한다.

kubectl get hpa
NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m
kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

CPU 사용량은 0으로 떨어졌고, HPA는 레플리카의 개수를 1로 낮췄다.

다양한 메트릭 및 사용자 정의 메트릭을 기초로한 오토스케일링

php-apache 디플로이먼트를 오토스케일링할 때, autoscaling/v2beta2 API 버전을 사용하여 추가적인 메트릭을 제공할 수 있다.

첫 번째로, autoscaling/v2beta2 형식으로 HorizontalPodAutoscaler YAML 파일을 생성한다.

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

에디터로 /tmp/hpa-v2.yaml 파일을 열면, 다음과 같은 YAML을 확인할 수 있다.

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
      current:
        averageUtilization: 0
        averageValue: 0

targetCPUUtilizationPercentage 필드가 metrics 배열로 대체되었다. CPU 사용량 메트릭은 resource metric 으로 파드 컨테이너 자원의 백분율로 표현된다. CPU 외에 다른 메트릭을 지정할 수 있는데, 기본적으로 지원되는 다른 메트릭은 메모리뿐이다. 이 자원들은 한 클러스터에서 다른 클러스터로 이름을 변경할 수 없으며, metrics.k8s.io API가 가용한 경우 언제든지 사용할 수 있어야 한다.

또한, Utilization 대신 AverageValuetarget 타입을, 그리고 target.averageUtilization 대신 target.averageValue로 설정하여 자원 메트릭을 퍼센트 대신 값으로 명시할 수 있다.

파드 메트릭과 오브젝트 메트릭 두 가지의 사용자 정의 메트릭 이 있다. 파드 메트릭과 오브젝트 메트릭. 이 메트릭은 클러스터에 특화된 이름을 가지고 있으며, 더 고급화된 클러스터 모니터링 설정이 필요하다.

이러한 대체 메트릭 타입중 첫 번째는 파드 메트릭 이다. 이 메트릭은 파드들을 설명하고, 파드들 간의 평균을 내며, 대상 값과 비교하여 레플리카 개수를 결정한다. 이것들은 AverageValuetarget만을 지원한다는 것을 제외하면, 자원 메트릭과 매우 유사하게 동작한다.

파드 메트릭은 이처럼 메트릭 블록을 사용하여 정의된다.

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

두 번째 대체 메트릭 타입은 오브젝트 메트릭 이다. 이 메트릭은 파드를 기술하는 대신에 동일한 네임스페이스 내에 다른 오브젝트를 표현한다. 이 메트릭은 반드시 오브젝트로부터 가져올 필요는 없다. 단지 오브젝트를 기술할 뿐이다. 오브젝트 메트릭은 ValueAverageValuetarget 타입을 지원한다. Value를 사용할 경우 대상은 API로부터 반환되는 메트릭과 직접 비교된다. AverageValue를 사용할 경우, 대상 값과 비교되기 이전에 사용자 정의 메트릭 API로부터 반환된 값은 파드의 개수로 나눠진다. 다음은 requests-per-second 메트릭을 YAML로 기술한 예제이다.

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

이러한 메트릭 블록을 여러 개 제공하면, HorizontalPodAutoscaler는 각 메트릭을 차례로 고려한다. HorizontalPodAutoscaler는 각 메트릭에 대해 제안된 레플리카 개수를 계산하고, 그중 가장 높은 레플리카 개수를 선정한다.

예를 들어, 네트워크 트래픽 메트릭을 수집하는 모니터링 시스템이 있는 경우, kubectl edit 명령어를 이용하여 다음과 같이 정의를 업데이트 할 수 있다.

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      current:
        value: 10k

이후, HorizontalPodAutoscaler는 각 파드가 요청 된 약 50%의 CPU 사용률을 소모하는지, 초당 1000 패킷을 처리하는지, 메인-루트 인그레스 뒤의 모든 파드들이 초당 10000 요청을 처리하는지 확인한다.

보다 구체적인 메트릭을 기초로한 오토스케일링

많은 메트릭 파이프라인들을 사용하면 이름 또는 labels 이라 불리는 추가적인 식별자로 메트릭을 설명할 수 있다. 그리고, 모든 비 자원 메트릭 타입(파드, 오브젝트 그리고 아래 기술된 외부 타입)에 대해, 메트릭 파이프라인으로 전달되는 추가 레이블 셀렉터를 지정할 수 있다. 예를 들면, verb 레이블로 http_requests 메트릭을 수집하는 경우, 다음과 같이 메트릭 블록을 지정하여 GET 요청에 대해 크기를 조정할 수 있다.

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

이 셀렉터는 쿠버네티스의 레이블 셀렉터와 동일한 문법이다. 모니터링 파이프라인은 네임과 셀렉터가 여러 시리즈와 일치하는 경우, 해당 여러 시리즈를 단일 값으로 축소하는 방법을 결정한다. 셀렉터는 부가적인 속성이며, 대상 오브젝트(Pods 타입의 대상 파드, Object 타입으로 기술된 오브젝트)가 아닌 메트릭을 선택할 수 없다.

쿠버네티스 오브젝트와 관련이 없는 메트릭을 기초로한 오토스케일링

쿠버네티스 위에서 동작하는 애플리케이션은, 쿠버네티스 클러스터의 어떤 오브젝트와도 관련이 없는 메트릭에 기반하여 오토스케일링을 할 수도 있다. 예로, 쿠버네티스 네임스페이스와 관련이 없는 서비스를 기초로한 메트릭을 들 수 있다. 쿠버네티스 버전 1.10 포함 이후 버전에서, 외부 메트릭 을 사용하여 이러한 유스케이스를 해결할 수 있다.

외부 메트릭 사용시, 먼저 모니터링 시스템에 대한 이해가 있어야 한다. 이 설치는 사용자 정의 메트릭과 유사하다. 외부 메트릭을 사용하면 모니터링 시스템의 사용 가능한 메트릭에 기반하여 클러스터를 오토스케일링 할 수 있다. 위의 예제처럼 nameselector를 갖는 metric 블록을 명시하고, Object 대신에 External 메트릭 타입을 사용한다. 만일 여러 개의 시계열이 metricSelector와 일치하면, HorizontalPodAutoscaler가 값의 합을 사용한다. 외부 메트릭들은 ValueAverageValue 대상 타입을 모두 지원하고, Object 타입을 사용할 때와 똑같이 동작한다.

예를 들면 애플리케이션이 호스팅 된 대기열 서비스에서 작업을 처리하는 경우, 다음과 같이 HorizontalPodAutoscaler 매니퍼스트에 30개의 미해결 태스크 당 한 개의 워커를 지정하도록 추가할 수 있다.

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

가능하다면, 외부 메트릭 대신 사용자 정의 메트릭 대상 타입을 사용하길 권장한다. 왜냐하면, 클러스터 관리자가 사용자 정의 메트릭 API를 보안관점에서 더 쉽게 보호할 수 있기 때문이다. 외부 메트릭 API는 잠재적으로 어떠한 메트릭에도 접근할 수 있기에, 클러스터 관리자는 API를 노출시킬때 신중해야 한다.

부록: Horizontal Pod Autoscaler 상태 조건

HorizontalPodAutoscaler의 autoscaling/v2beta2 형식을 사용하면, HorizontalPodAutoscaler에서 쿠버네티스가 설정한 상태 조건 을 확인할 수 있다. 이 상태 조건들은 HorizontalPodAutoscaler가 스케일을 할 수 있는지, 어떤 방식으로든 제한되어 있는지 여부를 나타낸다.

이 조건은 status.conditions에 나타난다. HorizontalPodAutoscaler에 영향을 주는 조건을 보기 위해 kubectl describe hpa를 사용할 수 있다.

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

이 HorizontalPodAutoscaler 경우, 건강 상태의 여러 조건들을 볼 수 있다. 첫 번째 AbleToScale는 HPA가 스케일을 가져오고 업데이트할 수 있는지, 백 오프 관련 조건으로 스케일링이 방지되는지 여부를 나타낸다. 두 번째 ScalingActive는 HPA가 활성화되어 있는지(즉 대상 레플리카 개수가 0이 아닌지), 원하는 스케일을 계산할 수 있는지 여부를 나타낸다. 만약 False 인 경우, 일반적으로 메트릭을 가져오는데 문제가 있다. 마지막으로, 마지막 조건인 ScalingLimited는 원하는 스케일 한도가 HorizontalPodAutoscaler의 최대/최소값으로 제한돼있음을 나타낸다. 이는 HorizontalPodAutoscaler에서 레플리카의 개수 제한을 최대/최소값으로 올리거나 낮추려는 것이다.

부록: 수량

HorizontalPodAutoscaler와 메트릭 API에서 모든 메트릭은 쿠버네티스에서 사용하는 수량 숫자 표기법을 사용한다. 예를 들면, 10500m 수량은 10진법 10.5으로 쓰인다. 메트릭 API들은 가능한 경우 접미사 없이 정수를 반환하며, 일반적으로 수량을 밀리단위로 반환한다. 10진수로 표현했을때, 11500m 또는 11.5 로 메트릭 값을 나타낼 수 있다.

부록: 다른 가능한 시나리오

명시적으로 오토스케일러 만들기

HorizontalPodAutoscaler를 생성하기 위해 kubectl autoscale 명령어를 사용하지 않고, 명시적으로 다음 파일을 사용하여 만들 수 있다.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

다음 명령어를 실행하여 오토스케일러를 생성할 것이다.

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

7 - 파드 내에서 쿠버네티스 API에 접근

이 페이지는 파드 내에서 쿠버네티스 API에 접근하는 방법을 보여준다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

파드 내에서 API에 접근

파드 내에서 API에 접근할 때, API 서버를 찾아 인증하는 것은 위에서 설명한 외부 클라이언트 사례와 약간 다르다.

파드에서 쿠버네티스 API를 사용하는 가장 쉬운 방법은 공식 클라이언트 라이브러리 중 하나를 사용하는 것이다. 이러한 라이브러리는 API 서버를 자동으로 감지하고 인증할 수 있다.

공식 클라이언트 라이브러리 사용

파드 내에서, 쿠버네티스 API에 연결하는 권장 방법은 다음과 같다.

각각의 경우, 파드의 서비스 어카운트 자격 증명은 API 서버와 안전하게 통신하는 데 사용된다.

REST API에 직접 접근

파드에서 실행되는 동안, 쿠버네티스 apiserver는 default 네임스페이스에서 kubernetes라는 서비스를 통해 접근할 수 있다. 따라서, 파드는 kubernetes.default.svc 호스트 이름을 사용하여 API 서버를 쿼리할 수 있다. 공식 클라이언트 라이브러리는 이를 자동으로 수행한다.

API 서버를 인증하는 권장 방법은 서비스 어카운트 자격 증명을 사용하는 것이다. 기본적으로, 파드는 서비스 어카운트와 연결되어 있으며, 해당 서비스 어카운트에 대한 자격 증명(토큰)은 해당 파드에 있는 각 컨테이너의 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/token 에 있다.

사용 가능한 경우, 인증서 번들은 각 컨테이너의 파일시스템 트리의 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt 에 배치되며, API 서버의 제공 인증서를 확인하는 데 사용해야 한다.

마지막으로, 네임스페이스가 지정된 API 작업에 사용되는 기본 네임스페이스는 각 컨테이너의 /var/run/secrets/kubernetes.io/serviceaccount/namespace 에 있는 파일에 배치된다.

kubectl 프록시 사용

공식 클라이언트 라이브러리 없이 API를 쿼리하려면, 파드에서 새 사이드카 컨테이너의 명령으로 kubectl proxy 를 실행할 수 있다. 이런 식으로, kubectl proxy 는 API를 인증하고 이를 파드의 localhost 인터페이스에 노출시켜서, 파드의 다른 컨테이너가 직접 사용할 수 있도록 한다.

프록시를 사용하지 않고 접근

인증 토큰을 API 서버에 직접 전달하여 kubectl 프록시 사용을 피할 수 있다. 내부 인증서는 연결을 보호한다.

# 내부 API 서버 호스트 이름을 가리킨다
APISERVER=https://kubernetes.default.svc

# 서비스어카운트(ServiceAccount) 토큰 경로
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# 이 파드의 네임스페이스를 읽는다
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# 서비스어카운트 베어러 토큰을 읽는다
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# 내부 인증 기관(CA)을 참조한다
CACERT=${SERVICEACCOUNT}/ca.crt

# TOKEN으로 API를 탐색한다
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

출력은 다음과 비슷하다.

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}