레이블이 prometheus인 게시물을 표시합니다. 모든 게시물 표시
레이블이 prometheus인 게시물을 표시합니다. 모든 게시물 표시

prometheus stall

특정 클러스터1 prometheus 조회가 간헐적으로 stall(멈춤,지연)되는 현상이 발생했다.
버스트(동시에 여러 접속 시도)시에 주로 발생한다.
같은 구성의 k8s 클러스터2 prometheus 는 지연없이 조회 된다.

참고로 클러스터는 2개의 노드에 각각 prometheus pod 1개씩 떠있고, 데이터(메트릭)이 유입되면 각각 똑같은 데이터를 저장하게 된다.

버스트요청 - stress test 로 10개 쿼리 동시 요청 x n 번(round)시 반응
문제가 되는 클러스터1 - prometheus 일부 요청들 지연 응답 또는 연결 취소 발생
문제가 없는 클러스터2 - prometheus 모든 요청 정상 응답

체크 - 쿼리가 무거워서?
무거운 쿼리 1동시 요청은 괜찮았지만 vector(1) 8동시 요청을 하면 23%확률로 스톨된다.

체크 - 클러스터들간의 데이터량(pod) 80 vs 40) 차이나서?
클러스터 양쪽 메트릭을 사이즈 차이가 크지 않다.

체크 - 요청 부하(GC / 스로틀 / 메모리 / 디스크 등)?
파드/노드 지표 12h 그래프를 봐도 cpu, 메모리가 튀지는 않는다.

체크 - 존/랙 업링크등의 문제가 있나?
같은 존/랙 떠있을것으로 추정되는 노드의 grafana 연결은 지연이 없다.

체크 - 진입 노드 외부 구간
문제가 있는 노드1를 통해서 grafana 조회시 지연이 없다.

체크 - LB(VIP)1 경유시 
스톨 발생

체크 - 새로운 LB2 를 생성하고 서비스는 기존 prometheus 로 연결
스톨 발생

체크 - LB 미경유 nodeport 로 바로 조회
스톨 발생

체크 - cilium 패킷 drop 이 발생하나?
cilium 측정(cilium hubbl ui 서비스 portfowrading)시 패킷 드랍 없음

[원인]
클러스터1,2 모두 --web.max-connections 512 (디폴트)로 되어 있다.
문제가 되는 클러스터1 이 클러스터2 200 개 정도에 비해 2배정도 많은 400 개 정도다.

현재 prometheus 연결 수는 config-reload container 를 통해 다음 명령으로 확인
kubectl -context ysoftman1-context -n ysoftman-monitoring-stack \
 exec ysoftman-monitoring-stack-prometheus-1 -c config-reloader -- \
 sh -c 'awk "\$2 ~ /:2382\$/ && \$4==\"01\" {c++} END{print c+0}" /proc/net/tcp /proc/net/tcp6'

또는 다음 prometheus 매트릭/쿼리로 확인할 수 있다.
net_conntrack_listener_conn_accepted_total{listener_name="http"} - net_conntrack_listener_conn_closed_total

클러스터1,2 둘다 batch 로 promql 조회를 하고 있는데 이게 클러스트의 pod 의 비례해서 상대적으로 많은 pod 를 가진 클러스터의 트래픽이 2배가 됐다.
512까지 여유가 없는 상태에서 버스트 요청시 클러스터2 보다 상대적으로 accept 가 빨리 찬다.

1. prometheus 가 들고 있는(accept 된) 연결이 512 에 도달 -> prometheus LimitListener 가 accept 호출 자체를 연결 하나가 닫혀 슬롯이 반환될 때까지 멈춘다. 
2. 그 동안 들어오는 새 연결은 커널이 알아서 3-way handshake 를 완료하고 accept 대기열에 쌓아둔다. 클라이언트 입장에선 connect 성공(tcp= 15ms), 요청까지 전송
3. 하지만 prometheus가 accept 을 안 하니 클라이언트는 first-byte 무한 대기한다. 서버가 끊는 게 아니라 클라이언트가 중단하게 된다.
4. 기존 연결이 닫혀 슬롯이 나면 대기열에서 순서대로 accept 재개되고 밀렸던 연결들이 한꺼번에 처리 된다. 1~7초 동기 방출되는 prometheus 쿼리 자체는 15ms 로 금방 처리된다.

이미 accept 돼 있던 연결(scrape 로 다른 클러스터에 긁어 가고 있는 경우 keep-alive 재사용)은 이 제한과 무관하게 계속 동작한다.
그래서 서버 히스토그램 기존 트래픽은 내내 정상으로 보였고, 신규 연결만 지연으로 관측됐다.

[해결방법]
max-connections 을 늘리자. 디폴트 512는 너무 작고 2048 정도로 늘려도 무리가 없다.
helm chart 로 설정
kube-prometheus-stack:
 prometheusSpec:
  prometheus:
      web:
        maxConnections: 2048

(수동 적용시) argocd syncPolicy.automated (selfHeal 포함) 를 제거해서 auto-sync 끈다.
kubectl -n ysoftman-argocd patch application ysoftman-monitoring-stack \
 --type merge -p '{"spec":{"syncPolicy":{"automated":null}}}'

(수동 적용시) prometheus cr 변경하면 operator 감지해서 적용
kubectl -n ysoftman-monitoring-stack patch prometheus ysoftman-monitoring-stack-prometheus \
 -type merge -p '{"spec":{"web":{"maxConnections":2048}}}'

pod 에 적용 모습
spec:
  containers:
  - args:
    - --web.max-connections=2048

--web.max-connections 는 커맨드라인 플래그라 reload operator가 StatefulSet 템플릿을 바꾸면서 롤링 재시작 된다.(replicas: 2 로 쓰고 있어서 한 대씩 앞 pod 이 ready 된 후 다음 pod 진행)
StatefulSet volumeClaimTemplate 기반이라 pod 삭제와 무관하게 pvc/pv(local-path-retain)는 그대로 남고, 새 pod 가 같은 pvc 에 다시 붙는다.
local-path 특성상 pv 가 노드에 고정돼 있어 pod 도 같은 노드로 다시 스케줄된다.
pod1대 재시작 중 나머지 replica 가 조회/수집을 계속 받아서 전체적으로 메트릭 유실은 없다.

max-connections 을 늘린 후로 스톨이 발생하지 않는다.ㅎ

prometheus timeout

grafana 에서 7일, 10일 등으로 긴 기간으로 보면 prometheus pod 가 메모리가 늘어나다가 prometheus container 가 재시작하는 현상이 발생했다.
pod describe 해보면 health, ready 에서 context deadlin exceed 가 보인다.
grafana 의 큰 데이터(날짜 길이 길게하는등) 요청에 시간이 많이 걸리고 그때 readiness / liveness 가 실패하고 pod 는 container 가 죽었다고 판단해서 재시작하는것으로 보인다.
참고로 prometheus 리소스가 별로도 설정되어 있지 않아 메모리도 늘어나도 된다.
(메모리 부족 pod describe 에서 OOM kill 같은 메시지가 보였을거다.)

# http://ysoftman-prometheus.test.abc:9090/-/ready 정상 응답일때
Prometheus Server is Ready.

# http://ysoftman-prometheus.test.abc:9090/-/health 정상 응답일때
Prometheus Server is Healthy.

# prometheus pod는 statefulset(sts) 로 관리된다.
# prometheus operator 로 운영되고 있어 prometheus statefulset(sts)를 바로 수정해도 변경되지 않는다.

# prometheus(CRD) 이름 파악
kubectl get prometheus

# 다음과 같이 prometheus (crd) > spec > containers > {readinessProbe, livenessProbe} > timeoutSeconds 를 늘려주자
kubectl edit prometheus {prometheus이름} -n {prometheus네임스페이스}
spec:
  containers:
  - livenessProbe:
      failureThreshold: 6
      httpGet:
        path: /-/healthy
        port: 9090  # 요건 보통 http-web 으로 portName이 설정되어 있어, http-web 으로 설정해도 된다.
        scheme: HTTP
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 30
    name: prometheus
    readinessProbe:
      failureThreshold: 3
      httpGet:
        path: /-/ready
        port: 9090
        scheme: HTTP
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 30

# 이제 sts 도 변경되고 prometheus pod 가 위 설정으로 자동 재시작되어 반영된다.
# {livenessProbe, readinessProbe} timeoutSeconds 를 늘려주시 시간이 오래걸리는 grafana 요청에도 pod 가 죽지 않는다.

#####

# 런타임 정보
http://{프로메테우스}/api/v1/status/runtimeinfo

# tsdb 정보
http://{프로메테우스}/api/v1/status/tsdb

# prometheus > status 메뉴 > command-line flags 에서 동시 요청 처리수 --query.max-concurrency=20 (디폴트) 확인
http://{프로메테우스}/flags
# prometheus_engine_queries_concurrent_max 메트릭으로도 확인 가능
# prometheus_engine_queries 현재 요청 수 메트릭(/ready 응답이 느려지는 순간에는 조회가 안되니 그래프로 봐야 알 수 있다.)

# prometheus ready 응답 느려지는 원인 파악을 위한 테스트

#####

# 쿼리 로그 남기기
# prometheus 쿼리를 로그파일로 남기기려면 global > query_log_file
# /dev/stdout 나 실제 파일 경로 명시
# 파일의 경우 log rotation 이 되지 않기 때문에 파일에 커지니 주의해야 한다.

# helm chart 에서는 prometheusspec > queryLogFile

# prometheus operator 를 사용 중이다.
# 테스트를 위해 임시로 parameters > values 에 추가한다.
kube-prometheus-stack:
  prometheus:
    prometheusSpec:
      # queryLogFile: /prometheus/query.log # 파일로 남길 경우
      queryLogFile: /dev/stdout

# 이제 prometheus(crd) 에 설정되고 prometheus container stdout 에 쿼리와 수행시간이 포함된 로그가 출력된다.
# prometheus_engine_query_log_enabled 메트릭으로 사용 상태 확인
# 3.5초 이상 걸린 쿼리 필털링
# json 이 아닌 로그를 걸러내기 위해 -R(--raw-input), fromjson? 사용
kubectl logs -f ysoftman-prometheus-0 -n ysoftman-ns -c prometheus | jq -R 'fromjson? | select(.stats.timings.execTotalTime >= 3.5)'

# 쿼리 확인 되면 queryLogFile 부분은 삭제해서 쿼리 파일 기록은 멈추자.
# 참고로 container 가 재시작 되지는 않아 파일로 기록했다면 파일이 남아 있어 필요하면 수동 삭제한다.

k8s metrics-server pod resource issue

# k9s,ktop 등의 프로그램에서 에서 pod 리소스(cpu,mem) 사용량이 실제보다 2배로 보인다.
# kubectl(k) top pods 로 봐도 같다.
# 1개의 container 로 운영되는 pod 인데 이름이 없는 container 가 리소스를 똑같이 잡고 있어 pod 리소스에는 2배로 보인다.
kubectl top pods
kubectl top pods --containers

# 실제 metrics api 로 pod 정보를 요청해 보면 2개의 container 가 있고 하나는 이름이 없다.
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/{네임스페이스}/pods/{파드} | jq .

# 네임스페이스 구분 없이 모든 pod 리소스가 위와 같이 2배로 보인다.
# metrics-server 를 재시작 해봤지만 변화가 없다.
kubectl rollout restart deployment metrics-server -n kube-system

# 현재 k8s 버전에 비해 metrics-server 버전이 낮은것 같다.
# k8s v1.26.4
# metrics-server k8s.gcr.io/metrics-server/metrics-server:v0.4.2
# 현재 최신 버전 0.7.2 으로 다시 설치해 보자.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# metrics-server pod 에서 다음과 같은 tls 에러가 발생해
tls: failed to verify certificate: x509

# 다음 옵션을 추가하면 자동 재시작되고 정상 동작 한다.
kubectl edit deployment metrics-server -n kube-system
args:
  - --kubelet-insecure-tls

# metrics-server 가 최신버전으로 변경됐지만 문제는 여전하다.
# metrics-server 를 다음으로 삭제했는데 kubectl top 등이 동작한다.
kubectl delete -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# api 를 확인해보니 prometheus-adapter 서비스에서 제공하고 있다.
kubectl get apiservice v1beta1.metrics.k8s.io

# prometheus-adapter helm chart 를 보면 v1beta1.custom.metrics.k8s.io api 외 (kubectl top 에서 사용하는) /apis/metrics.k8s.io/v1beta1 (v1beta1.metrics.k8s.io) 를 서빙할 수 있다고 한다.
# 실제 prometheus-adapter configmap 을 보면 resourceRules 로 설정이 되어 있었다.
# 설명을 보면 https://github.com/helm/charts/tree/master/stable/metrics-server 의 기능을 제공한다고 하는데 chart 버전을 보면 deprecatd 된 0.3.6 을 참고하는것 같다.

# prometheus-adapter 가 선점하고 있는 v1beta1.metrics.k8s.io 서비스는 삭제한다.
# 참고로 apiservice 는 먼저 등록한쪽이 사용되고 나중에 등록된건 무시된다고 한다.
kubectl delete apiservice v1beta1.metrics.k8s.io

# 참고로 prometheus-adapter chart 는 자동 argocd 에서 자동 싱크되어 있어 v1beta1.metrics.k8s.io 를 선점하려고 해서 잠시 비활성화해야 했다.

# 다시 최신 metrics-server 를 설치하고 v1beta1.metrics.k8s.io 서비스를 확인하면 metrics-server 로 동작한다.
kubectl get apiservice v1beta1.metrics.k8s.io

# 이제 kubectl top pods --containers 에도 중복된 녀석이 보이지 않는다.

# 다시 prometheus-adapter 로 v1beta1.metrics.k8s.io 서빙하도록 설치하자.
# 원래 prometheus-adapter chart 에는 container_cpu_usage_seconds_total{container!=""} 로 빈이름의 container 는 제외 하는데 우리 클러스터에 적용시 이 조건이 누락되어 있있고 조건을 추가하니 container 리소스가 중복 취합 되지 않는다.

argo-cd sync errror

# prometheus, grafana 버전업을 위해 다음과 같이 prometheus(operator) Chart.yaml > dependencies 버전업했다.
apiVersion: v2
name: ysoftman-prometheus
version: 0.0.1
description: Chart for ysoftman-prometheus
dependencies:
  - name: "kube-prometheus-stack"
    version: "65.3.2"
    repository: "https://prometheus-community.github.io/helm-charts"
  - name: "prometheus-adapter"
    version: "4.11.0"
    repository: "https://prometheus-community.github.io/helm-charts"

# Chart.lock 새로 생성
helm dependencies build

# 이제 git develop 브랜치에 커밋
# argocd 에서 해당 앱(prometheus operator)이 자동 싱크가 활성화 돼 있어 자동 싱크를 수행하는 중 다음과 같은 에러가 발생했다.

Failed to compare desired state to live state: failed to calculate diff: error calculating structured merge diff: error building typed value from config resource: .spec.scrapeConfigSelector: field not declared in schema

# 해결하기
# argocd 해당 application > detail > sync policy > automated 비활성화 상태에서 수동으로 싱크한다.
# 다시 automated 를 활성화하면 자동싱크시 에러가 발생하지 않는다.

# 버전 확인
http://ysoftman-prometheus.aaa.bbb/ > prometheus_build_info 메트릭으로 조회
http://ysoftman-grafana.aaa.bbb/api/health

# 기타 values spec 변경 사항 확인
Kind: Prometheus > spec
Kind: Alertmanger > spec

prometheus etcd-client-cert

# https://prometheus-community.github.io/helm-charts/ 으로 prometheus 설치시
# prometheus pod 가 실행할때 etcd-client-cert secret 을 참조하도록 설정했다.
# values.yaml  
kube-prometheus-stack:
  prometheus:
    prometheusSpec:
      replicas: 2
      secrets:
        - etcd-client-cert

# etcd-client-cert secret 생성
# k8s master 마스터 서버 접속해 아래 위치에서 3개의 파일을 가져온다.
ssh ysoftman@ysoftman-master-1.server
sudo -i
cp -v /etc/kubernetes/pki/etcd/etcd-ca.crt /home/ysoftman/
cp -v /etc/kubernetes/pki/apiserver-etcd-client.crt /home/ysoftman/
cp -v /etc/kubernetes/pki/apiserver-etcd-client.key /home/ysoftamn/
exit; exit;

# 로컬로 3개의 파일을 복사해 온다.
rsync ysoftman@ysoftman-master-1.server:/home/ysoftman/etcd-ca.crt .
rsync ysoftman@ysoftman-master-1.server:/home/ysoftman/apiserver-etcd-client.crt .
rsync ysoftman@ysoftman-master-1.server:/home/ysoftman/apiserver-etcd-client.key .

# 이 파일로 secret 을 생성한다.
kubectl create secret generic etcd-client-cert -n prometheus \
--from-literal=etcd-ca="$(cat etcd-ca.crt)" \
--from-literal=etcd-client="$(cat apiserver-etcd-client.crt)" \
--from-literal=etcd-client-key="$(cat apiserver-etcd-client.key)"

backup grafana dashboard

# 다음과 같이 prometheus 를 설치하면
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install ysoftman-release prometheus-community/kube-prometheus-stack

# grafana 설치하면 기본으로 포함된 dashboard 들이 있다.
# sidecar.dashboards.folder: /tmp/dashboards
# grafana container > /tmp/dashboards 에는 기본 제공되는 dashboard json 파일들이 위치한다.

# 그런데 사용자가 생성한(import)한 경우 /var/lib/grafana/grafana.db 에 추가된다.

# grafana pod 의 grafana.db 를 로컬에 복사
kubectl cp ysoftman-grafana-123:/var/lib/grafana/grafana.db ./grafana.db

# sqlite 로 확인해보자
# 테이블 조회
sqlite3 ./grafana.db '.tables'

# import 했던 dashboard 를 확인할 수 있다.
sqlite3 ./grafana.db 'select * from dashboard' | grep -i 20211010

#####

# grafana in k8s 인 grafana 재시작시 db 파일 삭제로 어려움이 있다.
# 대신 api 를 이용해서 백업해보자.
# api 사용을 위해 서비스 계정 생성 및 토큰 생성한다.
grafana > administration > service accounts > add service account
이후 add service account token (no expiration) > 토큰 복사해두기

# backup / restore grafana dashboard script

prometheus "found duplicate series" error

# pod 기준으로 network 트래픽 쿼리를 다음과 같이 실행하면
avg_over_time(container_network_transmit_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m]) + on(pod) group_left avg_over_time(container_network_receive_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m])

# 특정 pod series 가 중복되어 하나로 그룹핑 되지 않아 다음과 같은 에러를 발생한다.
Error executing query: found duplicate series for the match group {pod="ysoftman-123"} on the right hand-side of the operation:

# ysoftman-123 pod 의 id 가 다르게 3개가 나와서 문제였다.
# 해당 series 는 데이터는 무의미한것으로 없어도 된다.

# 해결방법1
# prometheus 의 admin api가 활성화(--web.enable-admin-api) 되어 있다면 다음과 같이 삭제할 수 있다.
# 바로 삭제되지는 않고 다음 compaction 시 적용된다.
curl -X POST -g 'http://localhost:8090/api/v1/admin/tsdb/delete_series?match[]=container_network_transmit_bytes_total{pod=~"ysoftman-.*"}[1w]'
# 바로 삭제를 위해선 다음을 api 한번더 호출해 준다. 
curl -X POST -g 'http://localhost:8090/api/v1/admin/tsdb/clean_tombstones'

# 해결방법2
# on(pod, id) 로 pod, id 로 그룹핑되도록 한다.
avg_over_time(container_network_transmit_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m]) + on(pod, id) group_left avg_over_time(container_network_receive_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m])

kubectl top node error

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

# node 를 조회하면 다음 에러가 발생한다.(kubectl top pods 는 정상동작)
kubectl top node
error: metrics not available yet

# kube-system > metrics-server deployment 가 제대로 동작하고 있지 않았다.
kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   0/1     1            0           26h

# 이슈1 - 이미지 다운로드 실패
# 원인은 registry.k8s.io 에 대해서 방화벽 설정으로 이미지를 받아오지 못해서였다.
Pulling image "registry.k8s.io/metrics-server/metrics-server:v0.6.3"

# 우선 로컬에 이미지를 다운로드 받자.
docker pull registry.k8s.io/metrics-server/metrics-server:v0.6.3

# 이미지를 tar 로 만든다.
docker save -o metrics-server-v0.6.3.tar registry.k8s.io/metrics-server/metrics-server:v0.6.3

# 워커 노드에 tar 파일 전송
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker1/home/ysoftman/
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker2/home/ysoftman/
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker3/home/ysoftman/

# 노드의 tar 이미지 파일을 로딩한다.
ssh ysoftman@worker1 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"
ssh ysoftman@worker2 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"
ssh ysoftman@worker3 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"

# metrics-server deployment 명세에 다음이 설정되어 있기 때문에
# imagePullPolicy: IfNotPresent
# pod 가 running 된다.

# 이슈2 - tls 비활성화
# metrics 로그를 보면 아래와같이 노드(kubelet)의 메트릭 수집시 실패한다고 나온다.
scraper.go:140] "Failed to scrape node" err="Get \"https://10.10.10.100:10250/metrics/resource\": read tcp 10.10.1.10:40752->10.10.10.100:10250: read: connection reset by peer" node="ysoftman-cpu1"

# metrics-server deployment> containers args 에 다음을 추가하면 위 에러는 발행하지 않고 정상 동작(scraping node ... scrap finished)한다.
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls  # 추가옵션
- --v=6 # 추가옵션 (Scraping node "node=xxxx" 노드 수집 성공 메시지 확인용)

# 이슈3 - apiservice 적용 안되는 이슈
# 아직도 error:metrics not available yet 이 발생한다.
# 찾아보니 kubectl 에서 top 커맨드 사용시 발생하는 에러 메시지였다.

# k8s api 로 node 메트릭을 다음과 같이 실행하면 결과가 나온다.
NODE_NAME="ysoftman-cpu1"
kubectl get --raw /api/v1/nodes/$NODE_NAME/proxy/metrics/resource | grep -i -E "node_cpu|node_mem"

# metrics api 에서 pods 응답은 나온다.
kubectl get --raw /apis/metrics.k8s.io/v1beta1/pods | jq 

# 하지만 nodes 응답을 보면 items: [] 로 빈값으로 나온다.
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | jq 

# metrics-server pod log 는 다음과 같이 노드로부터 정보를 받아 저장했다고 나온다.
round_trippers.go:553] GET https://10.10.10.10:10250/metrics/resource 200 OK in 2 millisecond
server.go:139] "Storing metrics"
server.go:144] "Scraping cycle complete"

# metrics-server 를 삭제했는데, k top nodes/pods 에러가 발행하지 않는다.
kubectl delete -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# top 명령을 받고 있는 별도의 pod 가 있는것으로 의심된다.
# v1beta1.metrics.k8s.io 를 사용하는 api 서비를 조회하면
kubectl get apiservices | grep -i v1beta1.metrics.k8s.io

# 다음과 같이 prometheus-adapter 서비스가 나온다.
v1beta1.metrics.k8s.io     prometheus-adapter/prometheus-adapter

# kubectl top nodes 동작하는 클러스터는 다음과 같다.
v1beta1.metrics.k8s.io     kube-system/metrics-server

# metrics-server (kind:APIService) 부분이 반영이 안되는게 문제였다.
# prometheus-adapter > v1beta1.metrics.k8s.io 를 사용하고 있고, 이를 argo-cd 에서 항상 sync 하고 있어 삭제해도 다시 생성된다.

helm dependencies prometheus alert 비활성화 하기

# Chart.yaml 에서 prometheus helm-charts 를 가져와 사용중이였는데,
# CPUThrottlingHigh 관련 알림이 너무 많아 알람을 비활성화 해보자
dependencies:
  - name: "kube-prometheus-stack"
    version: "45.9.1"
    repository: "https://prometheus-community.github.io/helm-charts"

# 우선 CPUThrottlingHigh 는 다음 템플릿 조건으로 추가할지 판단 한다.

# 그리고 prometheus values.yaml 에 default > disabled: {} 을 확인했다.

# 이제 내가 생성한 Chart values.yaml 에서
# dependencies(sub chart)의 values 를 다음과 형식으로 명시하면 된다.
kube-prometheus-stack:
  defaultRules:
    disabled: 
      CPUThrottlingHigh: true
  # 추가로 prometheus 설정은 다음과 같이 수정할 수 있다.
  prometheus:
    prometheusSpec:
      replicas: 2
      retention: 30d  # 저장기간(디폴트 10d)

Prometheus ServiceMonitor

# 사용자가 만든 api 가 prometheus 데이터 포맷(시계열 데이터...)으로 응답할때
# 이 응답을 주기적으로 prometheus 에서 수집하기 위해 monitoring.coreos.com 의 ServiceMonitor 리소스를 생성한다.

# 사용자 api 서버에 대한 Service 리소스
apiVersion: v1
kind: Service
metadata:
  labels:
    app: ysoftman-server
    release: prometheus-monitor
  name: ysoftman-server
  namespace: ysoftman-server
spec:
  ports:
    - name: metrics
      port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: ysoftman-server

---
# ServiceMonitor 리소스 생성
# 위 Service 에 대해 주기적으로 요청해 prometheus 로 수집한다.
# ServiceMonitor 가 정상적으로 등록되면 prometheus에서 메트릭이 수집되고, tagets 에 추가한 api 를 확인할 수 있다.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: prometheus-monitor-ysoftman-exporter
    # prometheus 오브젝트의 다음 값이 'release: prometheus-monitor' 로 되어 있고
    # matchLabels 에 명시해야 이 ServiceMonitor 가 prometheus target 으로 추가된다.
    # kubectl get prometheus -n prometheus-monitor prometheus-monitor -o yaml | yq '.spec.serviceMonitorSelector'
    release: prometheus-monitor
  name: prometheus-monitor-ysoftman-exporter
  namespace: prometheus-monitor
spec:
  namespaceSelector:
    # 모든 namespace 에서 대상(수집,모니터링)할 service 찾는다.
    # any: true
    # namespace 가 ysoftman-server 인곳에서 대상(수집,모니터링)할 service 를 찾는다.
    matchNames:
      - ysoftman-server
  selector:
    matchLabels:
      # 다음 label(key,value)가 명시된 Service 를 선택한다.
      app: ysoftman-server
      release: prometheus-monitor
  endpoints:
      # Service 요청할 path
    - path: /metrics
      # Service port name 명시
      port: metrics

DCGM GPU utilization

# GKE(Goole Kubernetes Engin) k8s 에서 Nvidia GPU utilization 메트릭(prometheus용)을 수집하기 위해
# Data Center GPU Manager(DCGM) exporter(https://github.com/NVIDIA/gpu-monitoring-tools) 를 사용한다.
# prometheus 에서 DCGM_FI_DEV_GPU_UTIL 메트릭으로 조회가 가능한데
# 다음과 같이 pod 값에 실제 gpu 를 사용하는 pod 가 아닌 dcgm-exporter-xxx 로만 수집된다.
DCGM_FI_DEV_GPU_UTIL{Hostname="dcgm-exporter-xxx",UUID="GPU-xxx",device="nvidia1",endpoint="metrics",gpu="1",instance="my-app1",job="dcgm-exporter",modelName="Tesla P40",namespace="monitoring",pod="dcgm-exporter-xxx",service="dcgm-exporter"}

# 참고로 k8s v1.13 부터 /var/lib/kubelet/pod-resources 소켓파일로 pod 이름등의 정보를 제공한다.

# 관련한 이슈가 있었고 dcgm-exporter daemonset 에 아래와 같은 환경변수를 적용하면 된다고 한다.
env:
  - name: "DCGM_EXPORTER_KUBERNETES"
    value: "true"
  - name: "DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE"
    value: "device-name"

# daemonset 적용 후 dcgm-exporter container 에 하나에 접속해 환경변수를 확인해 보자
root@dcgm-exporter-xxxx:/# printenv | grep DCGM_EXPORTER
DCGM_EXPORTER_KUBERNETES=true
DCGM_EXPORTER_KUBERNETES_GPU_ID_TYPE=device-name

prometheus query

# PromQL(Prometheus Query Language) 사용 예시
# 버전 조회
prometheus_build_info

# my_requests 메트릭 모두 조회
my_requests

# my_requests 중 abc label 값이 lemon 인 경우 조회
my_requests{abc="lemon"}

# my_requests 중 abc label 값이 lemon 이 아닌 경우 조회
my_requests{abc!="lemon"}

# my_requests 중 abc label 값이 lemon로 시작하는 경우 조회
# =~ 로 regex match 한다.
my_requests{abc=~"^lemon.*"}

# 특정 시간에서의 조회
my_requests{abc=~"^lemon.*"} @유닉스타임

# 30분 전의 값 조회
my_requests{abc=~"^lemon.*"} offset 30m

# [1m] : 최근 1분 동안 수집된 n 개의 값들(range vector)
my_requests{abc=~"^lemon.*"}[1m]

# 처음과 끝의 차이로 초당 평균 변화율(rate)을 계산한다.
# 처음과 시작값 외 중간의 값들은 사용되지 않기 때문에
# range vector 를 너무 크게하면 정확한 값이 도출되지 않는다.
# abc=lemon 요청이 1분동안 60번의 요청이 있었다면 rate 로 1tps가 된다.
rate(my_requests{abc="lemon"}[1m])

# tps 같은 변화율이 아닌 실제 카운트 값을 취할때는 increase 를 사용하면 된다.
# abc=lemon 요청이 1분동안 60번의 요청이 있었다면 increase 는 60(개)가 된다.
increase(my_requests{abc="lemon"}[1m])

# rate 로 나오는 n 개의 값을 더한다.
sum(rate(my_requests{abc="lemon"}[1m]))

# rate 로 나오는 n 개의 값 평균을 계산한다.
avg(rate(my_requests{abc="lemon"}[1m]))

# rate 로 나오는 n 개의 값중 최소값
min(rate(my_requests{abc="lemon"}[1m]))

# rate 로 나오는 n 개의 값중 최대값
max(rate(my_requests{abc="lemon"}[1m]))

# [1d:1h] : 최근 2일 동안 1시간 간격의 값들
# 하루 중 1분단위로 rate 합산 결과들 중 최대 값
max_over_time(sum(rate(my_requests{abc="lemon"}[1m]))[1d:1m])

# 하루 중 1분단위로 rate 합산 결과들 중 평균 값
avg_over_time(sum(rate(my_requests{abc="lemon"}[1m]))[1d:1m])

# 하루 중 1분단위로 rate 합산 결과들 중 중간 값
quantile_over_time(0.5, sum(rate(my_requests{abc="lemon"}[1m]))[1d:1m])

# container_cpu_usage_seconds_total(cpu점유시간) 등의 메트릭은
# 시간이 지날수록 계속 증가되는 값들로 rate()로 
# 시작과 끝 값을 통해 변화율을 계산하면 cpu사용율을 파악할 수 있다.
# 1m 간 수집된 n 개의 range vector 값들로 cpu증감율(사용율)을 알아낸다.
rate(container_cpu_usage_seconds_total{namespace=~"ysoftman.*"}[1m])

# pod 이 같은 것들을 하나로 더해서 취합
sum by (pod) (rate(container_cpu_usage_seconds_total{namespace=~"ysoftman.*"}[1m]))

# pod 이 같은 것들 중 가장 큰 값만 취합
max by (pod) (rate(container_cpu_usage_seconds_total{namespace=~"ysoftman.*"}[1m]))

# pod,container 별 CPU 사용량 백분율
sum(rate(container_cpu_usage_seconds_total{name!~".*prometheus.*", image!="", container!="POD"}[5m])) by (pod, container) / 
sum(container_spec_cpu_quota{name!~".*prometheus.*", image!="", container!="POD"} / container_spec_cpu_period{name!~".*prometheus.*", image!="", container!="POD"}) by (pod, container) * 100

# worker 노드의 pod 최대 개수 파악
# on(양쪽 메트릭에 존재하는 레이블로 결과들을 구분할 수 있는 레이블)
# group_left (왼쪽 매트릭 레이블 기준으로 병합, 병합 결과에 포함할 레이블 명시할 수 있다.)
# sum (결과) by (node) 결과중 node 레이블 같은 것 까리 합치기
sum(kube_node_status_allocatable{resource="pods", unit="integer"} * on(node) group_left() kube_node_role{role="worker"}) by (node)

# worker 노드의 running pod 개수 파악
sum((kube_pod_info * on(pod, namespace) group_right(node) kube_pod_status_phase{phase="Running"}) * on (node) group_left() kube_node_role{role="worker"}) by (node)

#####

# promethus > alerts 메뉴에 등록된 rule 확인을 할 수 있다.
# 알람은 다음 3가지 상태가 있다.
- inactive: 알람 (rule)조건이 해당하지 않는 경우(정상)
- pending: 조건에 맞아서 발송 대기중, rule 명세의 for 필드에 설정된 기간동안 검사하며 이 기간 동안 조건이 해제되면 inactive, 계속 조건이 맞으면 firing 상태로 변경된다.
- firing: firing 상태가 되면 alert-manager 에게 알람 내용 전송한다.

# 자주 사용되는 prometheus alert 을 모아둔곳(왠만한건 다있음~ㅎ)

Prev