kube-state-metrics에 대해서

ShinChul Bang
finda 기술 블로그
26 min readApr 23, 2020

쿠버네티스 클러스터의 다양한 매트릭 정보를 수집하여 HTTP 엔드포인트를 통해 제공해주는 서비스인 kube-state-metrics에 대해서 알아본다.

쿠버네티스를 사용하면서 운영 관점에서 다양한 메트릭 정보를 수집해 모니터링 하거나 특정 상황(예를 들어 Pod의 장애 발생 상황)에 별도의 채널로 알림을 발송해야 할 필요가 생길 수 있다.

이런 경우, 쿠버네티스 클러스터 워커노드로 사용중인 서버의 CPU, 메모리, 디스크 뿐만 아니라 쿠버네티스 클러스터 내부의 Pod가 사용중인 리소스 매트릭과 네트워크 I/O, Deployments 갯수, Pod 갯수 등의 다양한 정보를 수집해야 한다.

이런 방대한 데이터를 어떻게 수집해야할까?

위와 같은 다양한 매트릭 정보를 수집해서 매트릭 데이터를 /metrics 라는 엔드포인트로 제공해주는 녀석을 찾았다. 그 녀석이 바로 kubernetes에서 만든 [kube-state-metrics](<https://github.com/kubernetes/kube-state-metrics>)라는 서비스다.

kube-state-metrics에 대해서 kubernetes는 아래와 같이 설명한다.

kube-state-metrics is a simple service that listens to the Kubernetes API server and generates metrics about the state of the objects. (See examples in the Metrics section below.) It is not focused on the health of the individual Kubernetes components, but rather on the health of the various objects inside, such as deployments, nodes and pods.

kube-state-metrics is about generating metrics from Kubernetes API objects without modification. This ensures that features provided by kube-state-metrics have the same grade of stability as the Kubernetes API objects themselves. In turn, this means that kube-state-metrics in certain situations may not show the exact same values as kubectl, as kubectl applies certain heuristics to display comprehensible messages. kube-state-metrics exposes raw data unmodified from the Kubernetes API, this way users have all the data they require and perform heuristics as they see fit.

The metrics are exported on the HTTP endpoint /metrics on the listening port (default 8080). They are served as plaintext. They are designed to be consumed either by Prometheus itself or by a scraper that is compatible with scraping a Prometheus client endpoint. You can also open /metrics in a browser to see the raw metrics. Note that the metrics exposed on the /metrics endpoint reflect the current state of the Kubernetes cluster. When Kubernetes objects are deleted they are no longer visible on the /metrics endpoint.

이 서비스를 사용하면 오픈소스 모니터링 툴킷인 Prometheus를 이용하여 매트릭 데이터를 수집할 수 있다.

kube-state-metrics 매커니즘

따라서 간단하게 쿠버네티스 클러스터의 전반적인 상태를 모니터링 할 수 있는 대시보드를 구성할 수 있다.

Prometheus에 관련하여 알고싶다면 아래 포스트를 참고해보자.

kube-state-metrics 셋업 방법

그렇다면 직접 쿠버네티스 클러스터 내부에 셋업을 진행해보자.

먼저 쿠버네티스를 사용하는 만큼 Pod로 배포해보자.

각 워커 노드에 1개씩만 골고루 배포 하면 되지 않을까..? 라고 생각했지만, 이 서비스는 쿠버네티스 클러스터의 마스터노드에 존재하는 kubernetes API와 통신하여 매트릭 정보를 수집하기 때문에 클러스터 내에 단 1개의 Pod만 셋업하면 된다고 한다. (만약 클러스터가 여러개라면, 각 클러스터 마다 셋업을 해주어야 할 것 같다..)

kube-state-metrics의 GitHub에 들어가 README.md를 참조해보면,

간단하게 examples/standard 디렉터리 내의 manifest를 이용하여 배포하면 된다고 한다.

따라서 해당 GitHub 프로젝트를 clone하고 프로젝트 내부로 들어가서 아래 명령어를 치면 된다.

$ kubectl apply -f examples/standard

하지만 아래와 같이 쿠버네티스 버전 별로 kube-state-metrics와 호환성 문제가 있는 것 같으니 내가 사용하고 있는 쿠버네티스의 버전과 examples/standard 내의 deployment.yaml을 확인해서 호환성에 문제가 없는지 먼저 검토하도록 한다.

kube-state-metrics의 GitHub에서 위와 같은 표를 찾아볼 수 있다.

참고로 현재 이 포스트 작성 날짜 2020-04-23 기준으로 examples/standard 내의 manifest 내용들에서 kube-state-metrics의 버전은 1.9.5로 세팅되어 있다.

만약 내 쿠버네티스 버전이 1.15 이하라면 호환성 문제가 발생할 수 있으니 적절한 kube-state-metrics의 버전을 사용하기 위해 manifest의 내용을 수정하는 것이 좋다.

어려울 것 없이 간단하게 버전만 바꿔주면 된다.

예를 들어, examples/standard 디렉터리 내의 모든 manifest 안에는 공통적으로 아래와 같은 코드가 있는데,

여기에서 app.kubernetes.io/version: 의 값만 내 쿠버네티스 버전에 알맞은 버전으로 변경해주면 된다.

(나의 경우는 쿠버네티스 버전이 1.14여서 이와 가장 호환이 잘 맞는 kube-status-metrics 버전인 1.8.0으로 설정했다.)

metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.9.5 # 바로 이 부분이다.
name: kube-state-metrics

추가적으로 GKE(Google Kubernetes Engine)를 사용할 경우에는 kube-state-metrics 역할과 역할 바인딩이 작성되지 못하게 막는 엄격한 역할 권한(?)이 있다고 하니 아래 명령어를 통해 GCP ID에 클러스터 관리자 역할을 부여한 뒤 진행하는 것을 권장하고 있다.

$ kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format='value(config.account)')

아래와 같이 examples/standard의 내용을 검토하고, kubectl apply -f 명령어를 통해 쿠버네티스 클러스터에 셋업을 해보자.

manifest 검토

apiVersion: rbac.authorization.k8s.io/v1 
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0 # 나의 경우 1.8.0 버전을 적용
name: kube-state-metrics
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-state-metrics
subjects:
- kind: ServiceAccount
name: kube-state-metrics
namespace: kube-system
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
name: kube-state-metrics
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-state-metrics
subjects:
- kind: ServiceAccount
name: kube-state-metrics
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
name: kube-state-metrics
rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
- nodes
- pods
- services
- resourcequotas
- replicationcontrollers
- limitranges
- persistentvolumeclaims
- persistentvolumes
- namespaces
- endpoints
verbs:
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- replicasets
- ingresses
verbs:
- list
- watch
- apiGroups:
- apps
resources:
- statefulsets
- daemonsets
- deployments
- replicasets
verbs:
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- list
- watch
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- list
- watch
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- list
- watch
- apiGroups:
- storage.k8s.io
resources:
- storageclasses
- volumeattachments
verbs:
- list
- watch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- list
- watch
- apiGroups:
- networking.k8s.io
resources:
- networkpolicies
verbs:
- list
- watch
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- list
- watch
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
name: kube-state-metrics
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: kube-state-metrics
template:
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
spec:
containers:
- image: quay.io/coreos/kube-state-metrics:v1.8.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 5
name: kube-state-metrics
ports:
- containerPort: 8080
name: http-metrics
- containerPort: 8081
name: telemetry
readinessProbe:
httpGet:
path: /
port: 8081
initialDelaySeconds: 5
timeoutSeconds: 5
securityContext:
runAsUser: 65534
nodeSelector:
kubernetes.io/os: linux
serviceAccountName: kube-state-metrics

클러스터에 kube-status-metrics 배포

$ kubectl apply -f examples/standard

아래 명령어를 통해 잘 배포되었는지 클러스터 리소스를 확인해보자.

$ kubectl get pods -o wide -A 
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system kube-state-metrics-6dd46bfc45-b8gpz 1/1 Running 0 85m
...
# 위와 같이 kube-state-metrics 라는 이름의 Pod가 떠 있으면 성공이다.
$ kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-system kube-state-metrics ClusterIP None <none> 8080/TCP,8081/TCP 85m
...
# 위와 같이 kube-state-metrics라는 이름의 ClusterIP 타입의 Service가 떠 있으면 성공이다.

그리고 클러스터 워커노드에 SSH으로 접속해서 curl 명령어를 날려서 실제로 Pod가 매트릭 정보를 리턴해주는지 확인해보자.

$ curl http://<kube-state-metrics Pod의 IP>:8080/metrics 
# HELP kube_certificatesigningrequest_labels Kubernetes labels converted to Prometheus labels.
# TYPE kube_certificatesigningrequest_labels gauge
# HELP kube_certificatesigningrequest_created Unix creation timestamp
# TYPE kube_certificatesigningrequest_created gauge
# HELP kube_certificatesigningrequest_condition The number of each certificatesigningrequest condition
# TYPE kube_certificatesigningrequest_condition gauge
# HELP kube_certificatesigningrequest_cert_length Length of the issued cert
# TYPE kube_certificatesigningrequest_cert_length gauge
# HELP kube_configmap_info Information about configmap.
# TYPE kube_configmap_info gauge
kube_configmap_info{namespace="kube-system",configmap="coredns"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-rbac-cm"} 1
kube_configmap_info{namespace="kube-system",configmap="extension-apiserver-authentication"} 1
kube_configmap_info{namespace="kube-system",configmap="kube-proxy"} 1
kube_configmap_info{namespace="kube-system",configmap="eks-certificates-controller"} 1
kube_configmap_info{namespace="kube-system",configmap="ingress-controller-leader-alb"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-ssh-known-hosts-cm"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-tls-certs-cm"} 1
kube_configmap_info{namespace="kube-system",configmap="kube-proxy-config"} 1
kube_configmap_info{namespace="kube-system",configmap="aws-auth"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-cm"} 1
# HELP kube_configmap_created Unix creation timestamp
# TYPE kube_configmap_created gauge
...

매트릭 정보를 아주 잘 가져온다.

Pod의 IP를 조회하는 방법은 -o wide 옵션을 추가한 kubectl get pods -o wide -A 명령어를 통해 가능하다.

기본적으로 kube-status-metrics는 쿠버네티스에 ClusterIP 타입의 Service만 생성하게 manifest가 짜여져 있는데, 만약 매트릭 수집을 클러스터 내부에서 할 예정이라면 상관 없지만, 외부 서버에서 별도로 매트릭 정보를 수집하려면 NodePort나 Ingress를 사용하여 쿠버네티스의 외부 네트워킹이 구현되어야 한다.

나는 간단하게 NodePort를 사용하여 테스트를 해보았다. (물론 보안을 위한 방화벽 설정이나 네트워크 액세스 정책과 같은 부분을 잘 고려해야한다.)

아래와 같이 추가적으로 NodePort manifest를 작성하여 클러스터에 배포해보자.

apiVersion: v1 
kind: Service
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
name: kube-state-http-metrics-nodeport
namespace: kube-system
spec:
type: NodePort
selector:
app.kubernetes.io/name: kube-state-metrics
ports:
- protocol: TCP
port: 8080
targetPort: 8080
nodePort: 30001 # 외부에 30001번 포트로 Pod의 8081번 포트를 라우팅
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 1.8.0
name: kube-state-telemetry-nodeport
namespace: kube-system
spec:
type: NodePort
selector:
app.kubernetes.io/name: kube-state-metrics
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30002 # 외부에 30002번 포트로 Pod의 8081번 포트를 라우팅

그리고 이제 브라우저를 통해 http://<워커노드의 public ip>:30001/metrics 혹은 http://<워커노드의 public ip>:30002/metrics 에 접속해보자.

아래와 같은 결과가 보인다면 성공이다.

# HELP kube_certificatesigningrequest_labels Kubernetes labels converted to Prometheus labels. 
# TYPE kube_certificatesigningrequest_labels gauge
# HELP kube_certificatesigningrequest_created Unix creation timestamp
# TYPE kube_certificatesigningrequest_created gauge
# HELP kube_certificatesigningrequest_condition The number of each certificatesigningrequest condition
# TYPE kube_certificatesigningrequest_condition gauge
# HELP kube_certificatesigningrequest_cert_length Length of the issued cert
# TYPE kube_certificatesigningrequest_cert_length gauge
# HELP kube_configmap_info Information about configmap.
# TYPE kube_configmap_info gauge
kube_configmap_info{namespace="kube-system",configmap="coredns"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-rbac-cm"} 1
kube_configmap_info{namespace="kube-system",configmap="extension-apiserver-authentication"} 1
kube_configmap_info{namespace="kube-system",configmap="kube-proxy"} 1
kube_configmap_info{namespace="kube-system",configmap="eks-certificates-controller"} 1
kube_configmap_info{namespace="kube-system",configmap="ingress-controller-leader-alb"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-ssh-known-hosts-cm"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-tls-certs-cm"} 1
kube_configmap_info{namespace="kube-system",configmap="kube-proxy-config"} 1
kube_configmap_info{namespace="kube-system",configmap="aws-auth"} 1
kube_configmap_info{namespace="argocd",configmap="argocd-cm"} 1
# HELP kube_configmap_created Unix creation timestamp
# TYPE kube_configmap_created gauge
kube_configmap_created{namespace="kube-system",configmap="eks-certificates-controller"} 1.586326176e+09
kube_configmap_created{namespace="kube-system",configmap="coredns"} 1.586326179e+09
kube_configmap_created{namespace="argocd",configmap="argocd-rbac-cm"} 1.586330024e+09
kube_configmap_created{namespace="kube-system",configmap="extension-apiserver-authentication"} 1.586326171e+09
kube_configmap_created{namespace="kube-system",configmap="kube-proxy"} 1.586326179e+09
kube_configmap_created{namespace="argocd",configmap="argocd-cm"} 1.586330024e+09
kube_configmap_created{namespace="kube-system",configmap="ingress-controller-leader-alb"} 1.586326578e+09
kube_configmap_created{namespace="argocd",configmap="argocd-ssh-known-hosts-cm"} 1.586330024e+09
kube_configmap_created{namespace="argocd",configmap="argocd-tls-certs-cm"} 1.586330024e+09
kube_configmap_created{namespace="kube-system",configmap="kube-proxy-config"} 1.586326179e+09
kube_configmap_created{namespace="kube-system",configmap="aws-auth"} 1.586326543e+09
# HELP kube_configmap_metadata_resource_version Resource version representing a specific version of the configmap.
# TYPE kube_configmap_metadata_resource_version gauge
...

--

--