개요
Kubernetes의 HPA(Horizontal Pod Autoscaler) 객체를 활용하여 무중단 배포 전략(Rolling Update, Canary)을 설정하고, 오토스케일링을 구현하는 방법을 정리했다.
HPA 개념
출처: Was ist (Kubernetes) Autoscaling? VPA vs. HPA [kreyman]
HPA(Horizontal Pod Autoscaler)는 특정 파드에 부하가 발생했을 때 동일한 성능의 파드를 추가로 생성해 부하를 분산하는 Kubernetes 객체이다. CPU 사용률, 메모리 사용률 등을 기준으로 파드 생성(스케일 아웃)을 판단하며, 부하가 줄어들면 일정 시간 후 파드 수를 줄이는(스케일 인) 방식으로 작동한다.
출처: [Kubernetes] 쿠버네티스 HPA 개념과 구성 (HorizontalPodAutoscaler, 오토스케일러) [티스토리]
HPA는 metrics-server
에서 수집한 파드 메트릭을 바탕으로 추가할 파드 개수를 계산하여 오토스케일링을 수행한다.
metrics-server 설치
출처: Resource metrics pipeline [kubernetes.io]
HPA는 metrics-server
가 수집한 메트릭 데이터를 활용해 오토 스케일링을 수행한다. metrics-server
는 각 노드에서 실행되는 kubelet
에 있는 cAdvisor
를 통해 메트릭을 수집하고, Kubernetes API 서버에 전달한다. API 서버는 이 메트릭을 바탕으로 오토스케일링을 수행하거나, 사용자가 kubectl top
명령어로 메트릭을 조회할 수 있도록 한다.
cAdvisor
출처: Datadog [github]
cAdvisor는 Container Advisor의 약자로, 컨테이너의 리소스 사용량과 성능을 모니터링하는 데몬 프로그램이다. Kubernetes뿐만 아니라 Docker 환경에서도 사용할 수 있다. Kubernetes에서는 파드 안에서 컨테이너가 실행될 수 있도록 하는 kubelet
에 cAdvisor
가 함께 포함되어 있어 파드와 노드의 메트릭을 수집해 오토스케일링을 수행할 수 있도록 한다.
설치 방법
아래의 명령어로 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
객체를, Canary
및 Blue/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 는 메트릭을 기준으로 작동하기 때문에request
와limit
를 반드시 입력해야 한다.
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