kubernetes에 HorizontalPodAutoscaler가 동작하기 위해서는 기본적으로 metrics-server가 설치되어야 동작한다.
내부 동작 방식을 깊이 이해하기 위해서 구성 및 deep dive로 정리한다.helm을 이용해서 배포를 진행할 예정이나 template를 구성해서 배포하는 방식으로 진행할 예정이다.
Helm Repo 등록
아래와 같이 metrics-server의 repo를 동록한다.
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
https://github.com/kubernetes-sigs/metrics-server
https://artifacthub.io/packages/helm/metrics-server/metrics-server
metrics-server용 helm value 파일 생성
metrics-server-values.yaml
args:
- --kubelet-insecure-tls
- --v=4 # deep dive 확인을 위한 로그 출력 / 운영용으로 사용시 제거
--kubelet-insecure-tls
옵션이 빠진 상태에서 배포되면 아래와 같은 오류가 발생된다.
I1111 06:22:10.927848 1 tlsconfig.go:240] "Starting DynamicServingCertificateController"
E1111 06:22:10.928284 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.122.75:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.122.75 because it doesn't contain any IP SANs" node="node4"
E1111 06:22:10.951754 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.122.73:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.122.73 because it doesn't contain any IP SANs" node="node2"
E1111 06:22:10.952118 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.122.72:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.122.72 because it doesn't contain any IP SANs" node="node1"
E1111 06:22:10.953099 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.122.74:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.122.74 because it doesn't contain any IP SANs" node="node3"
metrics-server 배포용 template 파일 생성 및 배포
아래와 같이 배포용 template yaml 파일을 생성한다.
# helm template metrics-server metrics-server/metrics-server -f metrics-server-values.yaml --dependency-update --include-crds > metrics-server-deploy.yaml
or
# helm template metrics-server --dependency-update --include-crds \
--repo https://kubernetes-sigs.github.io/metrics-server/ \
--set fullnameOverride="metrics-server" \
--set-json 'args=["--kubelet-insecure-tls","--v=4"]' > metrics-server-deploy.yaml
배포용 yaml을 생성 시, 가능하면 --dependency-update
, --include-crds
2가지 옵션을 추가해 주면 좋다
생성된 metrics-server-deploy.yaml
을 kubectl로 배포한다.
# kubectl apply -f metrics-server-deploy.yaml --server-side
주의 사항으로 kubectl로 배포 시 --server-side
옵션을 붙여서 배포한다. (이유는 구글링을..)
metrics-server 배포 파일 분석
metrics-server를 분석해보면 아래와 같이 4가지 부분으로 구성되어 있다.
- metrics-server가 동작(메트릭 수집 등등)을 할 있도록 ServiceAccount 및 관련 구성 (ServiceAccount -> ClusterRole / ClusterRoleBinding / RoleBinding)
- metrics-server를 호출하는 Service
- metrics-server pod가 배포되는 Deployment
- APIService를 등록하고 이를 Service와 연결
metrics-server의 API 등록 현황
아래와 같이 apiservices 등록 현황을 확인할 수 있다.
# kubectl get apiservices
NAME SERVICE AVAILABLE AGE
.....
v1beta1.metrics.k8s.io default/metrics-server True 82s
...
배포용 파일 설정 내용
# Source: metrics-server/templates/apiservice.yaml
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1beta1.metrics.k8s.io
labels:
...
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: metrics-server
namespace: default
port: 443
version: v1beta1
versionPriority: 100
호출 해보면 아래와 같이 확인이 가능하다.
# kubectl get --raw / | grep metrics | grep v1beta1
"/apis/metrics.k8s.io/v1beta1",
HorizontalPodAutoscaler 테스트를 위한 테스트 배포
아래 deployment yaml과 hpa yaml 파일을 배포 진행한다.
nginx-deploy-dpm.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy-dpm
labels:
app: nginx-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- name: nginx-deploy-pod
image: nginx:1.14.2
resources:
requests:
cpu: 100m
memory: 200Mi
ports:
- name: http
containerPort: 80
# kubectl apply -f nginx-deploy-dpm.yaml
nginx-deploy-hpa.yaml
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-deploy-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-deploy-dpm
minReplicas: 2
maxReplicas: 100
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 30
# kubectl apply -f nginx-deploy-hpa.yaml
HorizontalPodAutoscaler 현황 파악
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-deploy-hpa Deployment/nginx-deploy-dpm 0%/30% 2 100 2 35s
위와 같이 TARGETS가 unknown이 아니라면 정상 동작하는 것이다.
metrics-server로 수집된 데이터 확인
metrics-server로 수집된 데이터는 "/apis/metrics.k8s.io/v1beta1
" API를 통해서 확인이 가능하다.
# kubectl get --raw /apis/metrics.k8s.io/v1beta1 | jq
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "metrics.k8s.io/v1beta1",
"resources": [
{
"name": "nodes",
"singularName": "",
"namespaced": false,
"kind": "NodeMetrics",
"verbs": [
"get",
"list"
]
},
{
"name": "pods",
"singularName": "",
"namespaced": true,
"kind": "PodMetrics",
"verbs": [
"get",
"list"
]
}
]
}
위와 같이 값이 호출될다고 할때, 현재 2가지로 수집되고 있다.
- nodes 메트릭
- pods 메트릭
nodes 값에서는 namespaced가 "false", pods 값에서는 namespaced가 "true"로 확인된다.
이때 호출하는 URL는 아래와 같이 만들어진다.
nodes (disabled namespaced) : /apis/metrics.k8s.io/v1beta1/nodes/[node name]
pods (enabled namespaed) : /apis/metrics.k8s.io/v1beta1/namespaces/[namespace]/pods/[pod name]
POD 메트릭 정보를 아래와 같이 확인이 가능하다.
### POD 정보 ###
# kubectl get pod
NAME READY STATUS RESTARTS AGE
metrics-server-55cc5bcdb8-qfkhv 1/1 Running 0 85m
nginx-deploy-dpm-7c7cb598b5-hpdnz 1/1 Running 0 8m15s
nginx-deploy-dpm-7c7cb598b5-ltdmf 1/1 Running 0 8m36s
### 메트릭 정보 확인 ###
# kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/default/pods/nginx-deploy-dpm-7c7cb598b5-hpdnz | jq
{
"kind": "PodMetrics",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"name": "nginx-deploy-dpm-7c7cb598b5-hpdnz",
"namespace": "default",
"creationTimestamp": "2024-11-11T07:59:23Z",
"labels": {
"app": "nginx-deploy",
"pod-template-hash": "7c7cb598b5"
}
},
"timestamp": "2024-11-11T07:58:53Z",
"window": "15.282s",
"containers": [
{
"name": "nginx-deploy-pod",
"usage": {
"cpu": "0",
"memory": "1948Ki"
}
}
]
}
참고로 /apis/metrics.k8s.io/v1beta1/
API는 데이터를 찾는 방식이 낮게 리스팅을 이쁘게 해준다.
prometheus-adapter로 custom metrics를 구성할 경우, 위의 방식으로 메트릭 API URL을 찾아야 한다.
HorizontalPodAutoscaler 동작 Workflow
HorizontalPodAutoscaler는 kube-controller-manager
내부에 존재하는 function으로 metrics-server에서 수집한 데이터를 기반으로 동작한다.
추가로 custom metrics와 external metrics도 추가로 지원한다.
kube-controller-manager
가 등록된 hpa(horizontalpodautoscalers)의 동작을 하기 위한 메트릭 정보를 /apis/metrics.k8s.io/v1beta1/
요청하여 데이터를 가져온다.
위에서 설명했다 싶이, apiservices에는 "/apis/metrics.k8s.io/v1beta1/
" 호출 주소는 metrics-server services를 호출하게 설정되어 있고, 결론적으로 호출한 주소의 데이터는 metrics-server가 제공한다.
ube-controller-manager
는 수집된 데이터를 기반으로 hpa를 동작시킨다.

metrics-server의 로그를 활성화한 경우 아래와 같이 metric 데이터를 가져가는 것을 확인할 수 있다
# kubectl get pod
NAME READY STATUS RESTARTS AGE
metrics-server-55cc5bcdb8-qfkhv 1/1 Running 0 117m
nginx-deploy-dpm-7c7cb598b5-hpdnz 1/1 Running 0 40m
nginx-deploy-dpm-7c7cb598b5-ltdmf 1/1 Running 0 41m
# kubectl logs -f metrics-server-55cc5bcdb8-qfkhv | grep kube-controller-manager
I1111 07:48:56.276413 1 httplog.go:132] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=app%3Dnginx-deploy" latency="2.904626ms" userAgent="kube-controller-manager/v1.29.10 (linux/amd64) kubernetes/f0c1ea8/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="ece51ae9-0efc-4c12-86a6-a4ae43bd6402" srcIP="10.233.120.0:21388" resp=200
I1111 07:49:11.311232 1 httplog.go:132] "HTTP" verb="LIST" URI="/apis/metrics.k8s.io/v1beta1/namespaces/default/pods?labelSelector=app%3Dnginx-deploy" latency="1.838739ms" userAgent="kube-controller-manager/v1.29.10 (linux/amd64) kubernetes/f0c1ea8/system:serviceaccount:kube-system:horizontal-pod-autoscaler" audit-ID="dd4cee06-d8a1-4631-91ce-53c9e2487deb" srcIP="10.233.120.0:21388" resp=200