Posts [ArgoCD] 무중단 배포 전략과 함께 HPA 적용하기
Post
Cancel

[ArgoCD] 무중단 배포 전략과 함께 HPA 적용하기

개요

Kubernetes의 HPA(Horizontal Pod Autoscaler) 객체를 활용하여 무중단 배포 전략(Rolling Update, Canary)을 설정하고, 오토스케일링을 구현하는 방법을 정리했다.

HPA 개념

1.png

출처: Was ist (Kubernetes) Autoscaling? VPA vs. HPA [kreyman]

HPA(Horizontal Pod Autoscaler)는 특정 파드에 부하가 발생했을 때 동일한 성능의 파드를 추가로 생성해 부하를 분산하는 Kubernetes 객체이다. CPU 사용률, 메모리 사용률 등을 기준으로 파드 생성(스케일 아웃)을 판단하며, 부하가 줄어들면 일정 시간 후 파드 수를 줄이는(스케일 인) 방식으로 작동한다.

2.png

출처: [Kubernetes] 쿠버네티스 HPA 개념과 구성 (HorizontalPodAutoscaler, 오토스케일러) [티스토리]

HPA는 metrics-server에서 수집한 파드 메트릭을 바탕으로 추가할 파드 개수를 계산하여 오토스케일링을 수행한다.

metrics-server 설치

3.png

출처: Resource metrics pipeline [kubernetes.io]

HPA는 metrics-server가 수집한 메트릭 데이터를 활용해 오토 스케일링을 수행한다. metrics-server는 각 노드에서 실행되는 kubelet 에 있는 cAdvisor 를 통해 메트릭을 수집하고, Kubernetes API 서버에 전달한다. API 서버는 이 메트릭을 바탕으로 오토스케일링을 수행하거나, 사용자가 kubectl top 명령어로 메트릭을 조회할 수 있도록 한다.

cAdvisor

4.png

출처: Datadog [github]

cAdvisor는 Container Advisor의 약자로, 컨테이너의 리소스 사용량과 성능을 모니터링하는 데몬 프로그램이다. Kubernetes뿐만 아니라 Docker 환경에서도 사용할 수 있다. Kubernetes에서는 파드 안에서 컨테이너가 실행될 수 있도록 하는 kubeletcAdvisor가 함께 포함되어 있어 파드와 노드의 메트릭을 수집해 오토스케일링을 수행할 수 있도록 한다.

설치 방법

아래의 명령어로 metrics-server 를 설치한다.

1
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

이후, 다음 명령어로 설정을 수정한다.

1
kubectl edit deployments.apps -n kube-system metrics-server

spec.template.spec.containers.args 항목에 다음 옵션을 추가한다.

1
2
--kubelet-insecure-tls
--kubelet-preferred-address-types=InternalIP, ExternalIP, Hostname

정상적으로 설치되었는지 아래 명령어로 확인한다.

1
2
3
kubectl top nodes
#NAME              CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
#k8s-master-node   557m         27%    2957Mi          77%

yaml 파일 작성

무중단 배포 전략에 따라 HPA 설정 방식이 달라진다.

Rolling Update 전략에는 Deployment 객체를, CanaryBlue/Green 배포 전략에는 Rollout 객체를 사용하여 HPA를 설정한다. 아래는 Helm Chart 템플릿 파일을 작성하는 방법이다.

Rolling Update

디렉토리 구조

Rolling Update 전략에서는 Deployment 객체를 사용한다. 디렉토리 구조는 다음과 같다.

1
2
3
4
5
6
7
8
9
.
├── demo-hpa-no-rollout
│   ├── Chart.yaml
│   ├── templates
│   │   ├── deployment.yaml
│   │   ├── hpa.yaml
│   │   └── service.yaml
│   └── values.yaml
└── demo-hpa-no-rollout-application.yaml

deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
  name: -deployment
  labels:
    app: 
  namespace: 
spec:
# replicas:  # HPA 사용하려면 제거해야함
  selector:
    matchLabels:
      app: 
  template:
    metadata:
      annotations:
      labels:
        app: 
    spec:
      containers:
      - image: 
        name: 
        resources: # 반드시 있어야 함
        ports:
          - name: http
            protocol: TCP
            containerPort: 

hpa.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: -hpa
spec:
  minReplicas: 
  maxReplicas: 
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: -deployment
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 
        type: Utilization
  • spec.minReplicas: 최소 파드 수
  • spec.maxReplicas: 최대 파드 수
  • spec.scaleTargetRef
    • apiVersion: Deployment 에 해당하는 apps/v1 작성
    • kind: Deployment 작성
    • name: Deployment 객체의 metadata.name 에 해당하는 값 입력
  • spec.replicas: HPA 적용 시 제거해야 하며, 파드 개수는 HPA 가 관리한다.
    • 참고자료: https://argo-cd.readthedocs.io/en/release-1.8/user-guide/best_practices/#leaving-room-for-imperativeness
  • spec.template.spec.containers.resources: HPA 는 메트릭을 기준으로 작동하기 때문에 requestlimit 를 반드시 입력해야 한다.

service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Service
metadata:
  labels:
    app: 
  name: 
  namespace: 
spec:
  ports:
  - port: 
    protocol: TCP
    name: http
    targetPort: 
  selector:
    app: 

demo-hpa-no-rollout-application.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-hpa-no-rollout
  namespace: argocd
spec:
  project: default
  source:
    path: demo-hpa-no-rollout
    repoURL: https://github.com/[org]/[repository]
    targetRevision: HEAD
    helm:
      valueFiles:
        - values.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated: {}

values.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
app:
  name: "demo-hpa-no-rollout"
  image: "argoproj/rollouts-demo:blue"
  port: 80
  minReplicas: 1
  maxReplicas: 4
  averageUtilization: 30
  resources:
    requests:
      cpu: "200m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "128Mi"

podAnnotations: {}
podLabels:
  tier: backend

service:
  enabled: true
  port: 80

Canary 전략

Blue/Green 전략도 Canary와 설정하는 방법은 비슷하기 때문에 생략했다.

디렉토리 구조

Canary 전략에서는 Deployment 객체 대신 Rollout 객체를 사용한다. 디렉토리 구조는 다음과 같다.

1
2
3
4
5
6
7
8
9
.
├── demo-hpa-with-rollout
│   ├── Chart.yaml
│   ├── templates
│   │   ├── hpa.yaml
│   │   ├── rollout.yaml
│   │   └── service.yaml
│   └── values.yaml
└── demo-hpa-with-rollout-application.yaml

hpa.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: -hpa
spec:
  minReplicas: 
  maxReplicas: 
  scaleTargetRef:
    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    name: -rollout
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 
        type: Utilization
  • spec.scaleTargetRef
    • apiVersion: Rollout 에 해당하는 argoproj.io/v1alpha1 작성
    • kind: Rollout 작성
    • name: Rollout 객체의 metadata.name 에 해당하는 값 입력

rollout.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: -rollout
  labels:
    app: 
    chart: "-"
    release: 
    heritage: 
spec:
# replicas:  # HPA 사용하려면 제거해야함
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: 
      release: 
  strategy:
    canary:
      canaryService: -canary
      stableService: -stable
      steps:
        - setWeight: 20
        - pause: {duration: 2s}
        - setWeight: 40
        - pause: {duration: 2s}
        - setWeight: 60
        - pause: {duration: 2s}
        - setWeight: 80
        - pause: {duration: 2s}
  template:
    metadata:
      annotations:
      labels:
        app: 
        release: 
    spec:
      containers:
        - name: 
          image: 
          resources: # 반드시 입력
          ports:
            - name: http
              containerPort: 
              protocol: TCP
  • spec.replicas: 마찬가지로 HPA 를 적용하려면 해당 속성을 사용하지 않아야 한다.
  • spec.template.spec.containers.resources: 마찬가지로 request 와 limit 를 반드시 입력해주어야 한다.

service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: v1
kind: Service
metadata:
  labels:
    app: 
  name: -canary
  namespace: 
spec:
  ports:
  - port: 
    protocol: TCP
    name: http
    targetPort: 
  selector:
    app: 
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: 
  name: -stable
  namespace: 
spec:
  ports:
  - port: 
    protocol: TCP
    name: http
    targetPort: 
  type: NodePort
  selector:
    app: 

values.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
app:
  name: "demo-hpa-with-rollout"
  image: "argoproj/rollouts-demo:blue"
  port: 80
  minReplicas: 1
  maxReplicas: 4
  averageUtilization: 30
  resources:
    requests:
      cpu: "200m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "128Mi"

podAnnotations: {}
podLabels:
  tier: backend

service:
  enabled: true
  port: 80

demo-hpa-with-rollout-application.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-hpa-with-rollout
  namespace: argocd
spec:
  project: default
  source:
    path: demo-hpa-with-rollout
    repoURL: https://github.com/[org]/[repository]
    targetRevision: HEAD
    helm:
      valueFiles:
        - values.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated: {}

부하 테스트

Helm Chart를 이용해 애플리케이션을 배포하고, HPA가 작동하는지 확인하기 위해 부하를 발생시킨다. 서비스 도메인으로 다음 명령어를 실행한다.

1
2
3
4
5
kubectl run -i --tty load-generator \
--rm \
--image=busybox:1.28 \
--restart=Never \
-- /bin/sh -c "while sleep 0.01; do wget -q -O- http://[service-name].[namespace].svc; done"

부하가 발생하고 나서 kubectl top pod 명령어를 실행해 파드가 자동으로 생성된 것을 확인한다. (초기 파드 개수 1개, 최대 파드 개수 4개)

1
2
3
4
5
6
7
kubectl top pod
#NAME                                CPU(cores)   MEMORY(bytes)
#demo-hpa-rollout-7449969d75-7jdgl   151m         17Mi
#demo-hpa-rollout-7449969d75-db4vg   174m         19Mi
#demo-hpa-rollout-7449969d75-dnvt2   142m         11Mi
#demo-hpa-rollout-7449969d75-xvrns   143m         11Mi
#load-generator                      24m          1Mi
This post is licensed under CC BY 4.0 by the author.

[ArgoCD] RBAC 적용을 위한 AppProject와 무중단 배포를 위한 Argo Rollouts 함께 사용하기

[ArgoCD] 무중단 배포 canary 전략의 파드 개수에 따른 로드밸런싱