본문 바로가기
Kubernetes/PKOS

[4주차] 쿠버네티스 모니터링

by isn`t 2023. 4. 2.

*본 게시글의 내용은 가시다님의 노션 페이지와 스터디 자료인 '24단계 실습으로 정복하는 쿠버네티스 도서' 를 기반으로 작성하였습니다.

Metric server와 cAdvisor

cAdvisor

구글이 만든 오픈소스 컨테이너 모니터링/시각화 솔루션이다. 컨테이너별 실시간 리소스 사용량을 컨테이너 런타임으로부터 전달받아 대시보드에 보여주며, 처리결과를 kubelet으로도 넘겨준다. 독립적인 이미지로도 지원하고 있으며 쿠버네티스 환경이 아니더라도 사용할 수는 있지만 쿠버네티스 환경에서는 kubelet에 포함되어 데몬 형태로 동작하며 kubelet과 매우 긴밀히 연계한다. cAdvisor 컨테이너를 실행할 때 도커 데몬의 정보를 가져올 수 있는 호스트의 path를 cAdvisor 컨테이너 볼륨으로 마운트하기 때문에, 컨테이너로 실행중인 cAdvisor에서 컨테이너의 상태정보 뿐만 아니라 컨테이너 런타임과 호스트의 상태까지도 확인이 가능하다.

 sudo docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  gcr.io/google-containers/cadvisor:v0.33.0

Metric Server

각 워커노드의 kubelet으로부터 수집한 메트릭을 aggregation하는 adds-on이다. HPA, VPA를 실행하기 위해서는 파드 내 컨테이너 리소스 사용량을 확인할 수 있어야 하는데 kubernetes API 서버는 이들 파드오토스케일링과 kubectl top 커맨드가 동작할 수 있도록 metric API를 제공하며, metric-server는 클러스터 내에서 메트릭을 수집하고 API 서버에서 수집된 결과를 쿼리할 수 있도록 한다.

/metrics/..., /stats/... 로 접근 가능하다.

[root@kops-ec2 ~]# kubectl top pod -A
NAMESPACE     NAME                                            CPU(cores)   MEMORY(bytes)   
kube-system   aws-cloud-controller-manager-w8r2d              2m           20Mi            
kube-system   aws-load-balancer-controller-7f9c6fb4d5-mncpv   2m           22Mi            
kube-system   aws-node-hrlpp                                  3m           37Mi            
kube-system   aws-node-s7qjd                                  3m           37Mi            
kube-system   aws-node-zgns6                                  3m           37Mi            
kube-system   cert-manager-9d894d6f-7r4mv                     1m           20Mi            
kube-system   cert-manager-cainjector-8659b5599b-8nlhx        1m           16Mi            
kube-system   cert-manager-webhook-854dfb7d9-v6sl4            1m           10Mi            
kube-system   coredns-68cd66b8cc-6vgrx                        2m           13Mi            
kube-system   coredns-68cd66b8cc-sk9lg                        2m           13Mi            
kube-system   coredns-autoscaler-66fbc7dd48-zltn8             1m           6Mi             
kube-system   ebs-csi-controller-6b9447f987-hdc7n             3m           53Mi            
kube-system   ebs-csi-node-82qnn                              1m           22Mi            
kube-system   ebs-csi-node-fr25k                              1m           22Mi            
kube-system   ebs-csi-node-tq7vx                              1m           24Mi            
kube-system   etcd-manager-events-i-04898af95a757b9e4         6m           30Mi            
kube-system   etcd-manager-main-i-04898af95a757b9e4           16m          58Mi            
kube-system   external-dns-5bc59bc9b5-sh2xd                   1m           19Mi            
kube-system   kops-controller-4mcnq                           2m           15Mi            
kube-system   kube-apiserver-i-04898af95a757b9e4              45m          329Mi           
kube-system   kube-controller-manager-i-04898af95a757b9e4     12m          54Mi            
kube-system   kube-proxy-i-0088ec4fb0a3b5dac                  1m           11Mi            
kube-system   kube-proxy-i-04898af95a757b9e4                  1m           11Mi            
kube-system   kube-proxy-i-0d570b2ccb528371c                  1m           11Mi            
kube-system   kube-scheduler-i-04898af95a757b9e4              3m           20Mi            
kube-system   metrics-server-5f65d889cd-df92s                 3m           15Mi            
kube-system   metrics-server-5f65d889cd-ffkmw                 3m           15Mi            
kube-system   node-local-dns-krw9c                            2m           10Mi            
kube-system   node-local-dns-mz7ps                            2m           10Mi            
kube-system   node-local-dns-t72wd                            2m           10Mi

Prometheus

SoundCloud에서 사내용으로 개발했다가 외부에 공개한 시스템 모니터링과 알림을 위한 오픈소스 도구이다. CNCF 산하의 프로젝트로 쿠버네티스 기반의 컨테이너 오케스트레이션 환경에서 사용되며, 다양한 시스템으로부터 메트릭을 수집하고 이를 사용자가 설정한 규칙에 따라 경고하거나, 시각화 도구와 연계하여 지표를 시각화 할 수 있다. 또한 메트릭을 검색하고 분석할 수 있도록 쿼리 언어(PromQL)를 지원한다.

데이터 자체는 시계열 데이터베이스(TSDB)로 저장되지만 key/value 기반으로 메트릭을 확인할 수 있으며, Pull 방식으로 동작하는 모니터링 시스템이기 때문에 프로메테우스 에이전트 자체적으로 메트릭을 가지고 있고 새로운 메트릭 데이터가 추가되기에 용이하다.

배포

실습 과정에서 프로메테우스는 helm 차트로 배포하였으므로 필요한 value 값들만 설정해 주었다.

prometheus:
  ingress:
    enabled: true
    ingressClassName: alb
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $ACM_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/group.name: "monitoring"
    hosts:
      - prometheus.$CLUSTER_NAME
    paths:
      - /*
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

annotations에는 LoadBalancerController가 AWS ALB를 설정할 떄 필요한 값들이 정의된다. 프로메테우스도 웹 대시보드를 통해 제어할 수 있으며 이 페이지에 접근하기 위해 인그레스로 연결한다.

prometheusSpec 필드에는 프로메테우스 서버에 설정될 값들이 정의된다. retention을 설정하여 메트릭 데이터 보관 기간을 제한하거나, retentionSize를 설정하여 사이즈 기준으로 메트릭 삭제 기준을 세울 수도 있다. 이외에 helm chart로 프로메테우스를 배포할때 필요한 입력값들에 대한 설명은 prometheus-community Github 에서 확인할 수 있다.

node exporter

exporters는 pull 방식인 prometheus가 메트릭을 가져오는 target을 의미하므로, node-extporter는 node 레벨에서 수집한 메트릭을 프로메테우스가 가져갈 수 있도록 노출된 클라이언트 역할을 한다.

(ilikebigmac:N/A) [root@kops-ec2 ~]# kubectl get node -owide
toring kube-prometheus-stack-prometheus-node-exporter
NAME                  STATUS   ROLES           AGE   VERSION    INTERNAL-IP     EXTERNAL-IP      OS-IMAGE             KERNEL-VERSION    CONTAINER-RUNTIME
i-0ac07f456ba862eac   Ready    control-plane   14m   v1.24.12   172.30.34.85    13.125.243.253   Ubuntu 20.04.5 LTS   5.15.0-1031-aws   containerd://1.6.18
i-0b74a2bae9c3f89ef   Ready    node            11m   v1.24.12   172.30.48.239   3.38.107.137     Ubuntu 20.04.5 LTS   5.15.0-1031-aws   containerd://1.6.18
i-0bc7017f0a83af344   Ready    node            12m   v1.24.12   172.30.78.201   52.79.233.143    Ubuntu 20.04.5 LTS   5.15.0-1031-aws   containerd://1.6.18
(ilikebigmac:N/A) [root@kops-ec2 ~]# kubectl get svc,ep -n monitoring kube-prometheus-stack-prometheus-node-exporter
NAME                                                     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/kube-prometheus-stack-prometheus-node-exporter   ClusterIP   100.69.101.0   <none>        9100/TCP   39s

NAME                                                       ENDPOINTS                                                 AGE
endpoints/kube-prometheus-stack-prometheus-node-exporter   172.30.34.85:9100,172.30.48.239:9100,172.30.78.201:9100   39

Endpoints 탭을 보면 3개 IP의 9100 포트가 node-exporter 서비스와 연결되어 있음을 확인할 수 있다. 이 IP들이 Node의 IP이다.

Prometheus Job

프로메테우스의 Job은 프로메테우스가 모니터링해야하는 대상을 정의하는 개념이다. Job은 수집될 메트릭 집합과 수집 대상을 정의한다. 예를 들어 node-exporter나 kube-state-metrics 등의 모니터링 에이전트를 설치하고, 해당 에이전트에서 제공하는 메트릭을 프로메테우스가 수집해야 할 때, node-exporter와 kube-state-metrics를 각각의 job으로 정의하고, 해당 job에서 수집할 메트릭을 지정한다. 이 값은 Prometheus 웹 대시보드에서 Status -> Configuration 에서 확인할 수 있다.

예) node-exporter job 정의

- job_name: serviceMonitor/monitoring/kube-prometheus-stack-prometheus-node-exporter/0
  honor_timestamps: true
  scrape_interval: 15s
  scrape_timeout: 10s
  metrics_path: /metrics
  scheme: http
  follow_redirects: true
  enable_http2: true
  relabel_configs:
  - source_labels: [job]
    separator: ;
    regex: (.*)
    target_label: __tmp_prometheus_job_name
    replacement: $1
    action: replace
  - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_instance, __meta_kubernetes_service_labelpresent_app_kubernetes_io_instance]
    separator: ;
    regex: (kube-prometheus-stack);true
    replacement: $1
    action: keep
  - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name, __meta_kubernetes_service_labelpresent_app_kubernetes_io_name]
    separator: ;
    regex: (prometheus-node-exporter);true
    replacement: $1
    action: keep
  - source_labels: [__meta_kubernetes_endpoint_port_name]
    separator: ;
    regex: http-metrics
    replacement: $1
    action: keep
  - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
    separator: ;
    regex: Node;(.*)
    target_label: node
    replacement: ${1}
    action: replace
  - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
    separator: ;
    regex: Pod;(.*)
    target_label: pod
    replacement: ${1}
    action: replace
  - source_labels: [__meta_kubernetes_namespace]
    separator: ;
    regex: (.*)
    target_label: namespace
    replacement: $1
    action: replace
  - source_labels: [__meta_kubernetes_service_name]
    separator: ;
    regex: (.*)
    target_label: service
    replacement: $1
    action: replace
  - source_labels: [__meta_kubernetes_pod_name]
    separator: ;
    regex: (.*)
    target_label: pod
    replacement: $1
    action: replace
  - source_labels: [__meta_kubernetes_pod_container_name]
    separator: ;
    regex: (.*)
    target_label: container
    replacement: $1
    action: replace
  - source_labels: [__meta_kubernetes_pod_phase]
    separator: ;
    regex: (Failed|Succeeded)
    replacement: $1
    action: drop
  - source_labels: [__meta_kubernetes_service_name]
    separator: ;
    regex: (.*)
    target_label: job
    replacement: ${1}
    action: replace
  - source_labels: [__meta_kubernetes_service_label_jobLabel]
    separator: ;
    regex: (.+)
    target_label: job
    replacement: ${1}
    action: replace
  - separator: ;
    regex: (.*)
    target_label: endpoint
    replacement: http-metrics
    action: replace
  - source_labels: [__address__]
    separator: ;
    regex: (.*)
    modulus: 1
    target_label: __tmp_hash
    replacement: $1
    action: hashmod
  - source_labels: [__tmp_hash]
    separator: ;
    regex: "0"
    replacement: $1
    action: keep
  kubernetes_sd_configs:
  - role: endpoints
    kubeconfig_file: ""
    follow_redirects: true
    enable_http2: true
    namespaces:
      own_namespace: false
      names:
      - monitoring

kwatch

kwatch는 kubernetes 클러스터 모니터링 도구 중 하나로 클러스터 내 모든 변경사항과, 클러스터에서 실행중인 애플리케이션의 성능 측정 및 상태와 부하를 확인할 수 있는 오픈소스 웹 기반 도구이다.

프로메테우스와는 기능적인 측면에서 차이가 있는데, 가장 큰 차이점은 '실시간'의 여부이다. 앞서 프로메테우스는 메트릭을 수집하고 시계열 데이터베이스에 저장하였지만, kwatch는 이를 실시간으로 보여주며 쿼리 기능도 제공한다.

프로메테우스는 메트릭을 수집하고 분석하는데 중점을 두고 있으며, kwatch는 실시간 모니터링에 중점을 두고 있기 때문에 경쟁 관계보다는 상호 보완의 관계로 볼 수 있을 것 같다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: kwatch
  namespace: kwatch
data:
  config.yaml: |
    alert:
      slack:
        webhook: '<slack web hook url>'
        #title:
        #text:
    pvcMonitor:
      enabled: true
      interval: 5
      threshold: 70

이와 같이 슬랙의 web hook url을 지정해주면 kwatch가 감지한 이슈를 실시간으로 슬랙에 전달하고 발생한 이벤트와 로그도 출력시킬 수 있다.

  • 잘못된 파드가 배포되어 정상적으로 올라오지 않은 경우

AlertManager

프로메테우스는 애플리케이션에서 숮비한 메트릭을 분석하고 이를 기반으로 임계치를 설정하여 Alert을 생성하는 기능을 제공하는데, 이를 위해서 AlertManager라는 컴포넌트가 제공된다.

AlertManager는 프로메테우스에서 발생한 Alert을 다양한 경로로 보낸다. 이 때 AlertManager는 Label 기반의 라우팅을 지원한다. Alert을 특정 팀이나 개인 단위로 전달하거나, 애플리케이션에 따라 다른 Alert 처리 규칙을 적용할 수도 있다.

alertmanager:
  config:
    global:
      resolve_timeout: 5m
      slack_api_url: '<slack web hook url>'
    route:
      group_by: ['job']  # namespace
      group_wait: 10s
      group_interval: 1m
      repeat_interval: 5m
      receiver: 'slack-notifications'
      routes:
      - receiver: 'slack-notifications'
        matchers:
          - alertname =~ "InfoInhibitor|Watchdog"
    receivers:
    - name: 'slack-notifications'
      slack_configs:
      - channel: '<slack channel>'
        send_resolved: true
        title: '[{{.Status | toUpper}}] {{ .CommonLabels.alertname }}'
        text: |
          *Description:* {{ .CommonAnnotations.description }}

이와 같이 kwatch에서 했던 방법과 유사하게 slack 채널로 Alert을 전달할 수 있다. 샘플 메세지를 날려보면 아래와 같은 결과를 얻을 수 있다.

curl -X POST --data-urlencode "payload={\"channel\": \"#webhook2\", \"username\": \"pkosbot\", \"text\": \"$KOPS_CLUSTER_NAME 다음주 종강! - 봇 제공\"}" $WEBHOOK