[monitoring] Prometheus 설치 가이드(+exporter)

안녕하세요? 정리하는 개발자 워니즈입니다. 이번시간에는 Prometheus에 관해서 정리를 해보려고 합니다. 필자가 이전 회사에서는 EKS를 운용하다 보니, helm을 통해서 프로메테우스를 설치했었습니다. helm은 한번에 익스포터부터 모든것을 설치하는 것으로 알고 있습니다. 그러다보니 내부적인 설정이을 건드린것이 전혀 없었습니다.

이번에는 익스포터의 역할과 설치하는 과정에 대해서 정리를 하려고 합니다.

1. Node-exporter 설치하기

쿠버네티스 클러스터를 구축한 뒤로, 각 worker 노드에 대해서 메트릭(cpu, memory, disk)을 수집하기 위해서는 해당 worekr 노드에 대한 정보를 수집할 수 있는 api를 제공해주어야 프로메테우스가 풀 방식으로 가져갑니다. 여기서 api를 제공해주기 위해 설치하는 것이 Node-exporter입니다. 각 Node들에 대해서 메트릭을 exporting하는 어플리케이션을 설치한다고 보면 됩니다.

각 worekr node마다 하나씩 설치하게 되어있기 때문에, daemonset으로 올리게 됩니다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
  labels:
    app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true
      hostIPC: true
      hostPID: true
      containers:
        - name: node-exporter
          image: prom/node-exporter:v1.0.1
          imagePullPolicy: IfNotPresent
          args:
            - --path.procfs=/host/proc
            - --path.sysfs=/host/sys
          resources:
            requests:
              cpu: 10m
              memory: 100Mi
            limits:
              cpu: 100m
              memory: 100Mi
          ports:
            - name: scrape
              containerPort: 9100
              hostPort: 9100
          volumeMounts:
            - mountPath: /host/proc
              name: proc
              readOnly: true
            - mountPath: /host/sys
              name: sys
              readOnly: true
      volumes:
        - name: proc
          hostPath:
            path: /proc
            type: ""
        - name: sys
          hostPath:
            path: /sys
            type: ""

여기서 중요한 것은, worker에 데몬셋으로 올리긴하지만, 실제로는 host의 file system과 network를 공용해서 사용하기 떄문에 worker에 native하게 설치하는 것과 동일한 효과라고 보면 될 것입니다. 설치를 완료하고나서 실제로 9100/metircs라고 호출을 하면 다음과 같은 값들이 출력 됩니다.

...
node_load5 0.4
# HELP node_memory_Active_anon_bytes Memory information field Active_anon_bytes.
# TYPE node_memory_Active_anon_bytes gauge
node_memory_Active_anon_bytes 1.596526592e+09
# HELP node_memory_Active_bytes Memory information field Active_bytes.
# TYPE node_memory_Active_bytes gauge
node_memory_Active_bytes 3.758211072e+09
# HELP node_memory_Active_file_bytes Memory information field Active_file_bytes.
# TYPE node_memory_Active_file_bytes gauge
node_memory_Active_file_bytes 2.16168448e+09
# HELP node_memory_AnonHugePages_bytes Memory information field AnonHugePages_bytes.
# TYPE node_memory_AnonHugePages_bytes gauge
node_memory_AnonHugePages_bytes 0
...

메모리에 대한 정보를 보여주는 것이고, 실제로 worker에 대해서 다양한 값들을 api를 통해서 가져갈 수 있도록 plain text 형식으로 제공해줍니다.

$ kubectl get pod -n monitoring
pod/node-exporter-47xj5                   1/1     Running   0          46h
pod/node-exporter-5cbvh                   1/1     Running   0          46h
pod/node-exporter-6sc87                   1/1     Running   0          46h
pod/node-exporter-7s45x                   1/1     Running   0          46h
pod/node-exporter-dx6sf                   1/1     Running   0          46h
pod/node-exporter-gx6g7                   1/1     Running   0          46h
pod/node-exporter-kxh7z                   1/1     Running   0          46h
pod/node-exporter-wcfdb                   1/1     Running   0          46h
pod/node-exporter-wpzgk                   1/1     Running   0          46h
pod/node-exporter-z9zlx                   1/1     Running   0          46h

모니터링 namespace에 보이는 것처럼, 데몬셋으로 pod들이 올라온것을 확인할 수 있습니다.

2. Kube-state-metric 설치

kube-state-metirc은 kubernetes 클러스터 내부의 Pod가 사용중인 리소스들에 대해서 수집을 진행합니다. Kubernetes-api를 통해서 실제 데이터를 서버로부터 요청하여 가져오기 때문에 cluster안에는 deployment를 통해서 하나의 component만을 배포합니다.

apiVersion: v1 
kind: Service 
metadata: 
  labels: 
    app.kubernetes.io/name: kube-state-metrics 
    app.kubernetes.io/version: 2.1.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: 31000 # 외부에 31000번 포트로 Pod의 8080번 포트를 라우팅 
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/name: kube-state-metrics
    app.kubernetes.io/version: 2.1.0
  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: 2.1.0
  name: kube-state-metrics
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - secrets
  - nodes
  - pods
  - services
  - resourcequotas
  - replicationcontrollers
  - limitranges
  - persistentvolumeclaims
  - persistentvolumes
  - namespaces
  - endpoints
  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
  - ingresses
  verbs:
  - list
  - watch
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/name: kube-state-metrics
    app.kubernetes.io/version: 2.1.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: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: kube-state-metrics
    app.kubernetes.io/version: 2.1.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: 2.1.0
    spec:
      containers:
      - image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.1.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

위의 내용에 대해서는 궁금한게 좀 있는데, 이부분은 좀더 서칭을 해봐야될 것 같다는 생각을 했습니다. 우선 1개의 container가 배포가 됩니다. 1개의 kube-metric pod가 배포가 되며, NodePort를 통해서 외부로 요청을 했을경우, 호출이 되도록 하였습니다. 각각의 역할에 대해서 현재로서는 정확하게 판단이 안되지만, 어쩃든 kubernetes cluster의 api-server를 통해서 cadvisor로부터 metirc들을 수집하는거까지 간단한게 정리하면 될 것 같습니다.

설치를 완료하게 되면, 보이는 것과 같이 1개의 deploy에 의해서 1개의 pod가 생성되는 것을 볼 수 있습니다 .

kube-state-metrics-995496786-czggl        1/1     Running   0          20m

2. Prometheus 설치하기

위의 내용에서는 클러스터에 exporter 2가지를 설치해 봤습니다. 정상적으로 설치 하게 되면, 우선 절반은 완료가 된것입니다. 이제 해당 exporter로 부터 데이터를 주기적으로 가져가는 어플리케이션인 Prometheus를 설치해보겠습니다.

프로메테우스는 SoundCloud사에서 만든 오픈소스 모니터링 툴입니다. go 언어로 만들어졌으며, kubernetes 환경에서 모니터링 하기 원하는 리소스로부터 metric을 수집하고 해당 메트릭을 이용해서 모니터링하는 기능을 제공합니다.

프로메테우스에는 여러가지 특징이 있는데 가장큰 특징은 다음과 같습니다

  • Pull 방식

    exporter에 프로메테우스가 직접 접속하여 수집한 메트릭을 가져오는 방식입니다. 아무래도 pull방식으로 수집을 하다보니, 부하가 생길 염려가 없습니다. Push방식으로 보내게 되면 받을수 없는 상황(부하 발생)임에도 불구하고 더큰 부하를 줄 수 도 있습니다.

2-1. 환경 구성

필자 같은 경우, 추후에 2개의 프로메테우스 서버를 타노스로 묶어주기 위해서(가용성 유지) 2개의 linux instance를 준비했습니다.

서버 사양 : [STAND] 4vCPU, 16GB Mem [g2]
서버 OS : CentOS 7.8 (64-bit)
서버 스토리지 : 50 GB /dev/xvda

2대의 서버에는 모두 사전작업으로 도커가 기본적으로 설치 되어있고, 추가적으로 도커 컴포오즈도 역시 설치가 되어있어야 합니다.

2-2. 프로메테우스 설치 폴더 구성

docker/
└── prometheus
    ├── docker-compose.yml
    ├── prometheus.yml
    └── prometheus_data
        ├── 01F9B272CJJSBTN6K6EXPNTB4M
        ├── lock
        ├── queries.active
        └── wal

설치는 위와 같이 진행했습니다. 특정 폴더 (필자는 /data 하위로 작성)에 docker라틑 폴더를 생성합니다. 그리고 하위로 프로메테우스 폴더를 생성하고 해당 위치에는 다음과 같은 파일 및 폴더를 위치시킵니다.

  • docker-compose.yml : docker를 올리기 위한 구성 파일
  • prometheus.yml : 프로메테우스 설정 파일
  • prometheus_data : 프로메테우스 데이터 저장소

2-3. 프로메테우스 설정 파일

global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  external_labels:
    cluster: prometheus-1
scrape_configs:
  - job_name: 'prometheus'
    static_configs:
    - targets: ['127.0.0.1:9090']

  - job_name: 'node_exporter'
    static_configs:
      - targets: [{워커아이피}:{exporter 포트}]
  • global
    • Scrape_interval : 몇 초에 한번씩 metricdㅡㄹ 수집할 것인지에 관한 옵션입니다. 해당 값을 설정하지 않는다면 default로 1m 값이 설정됩니다.
    • scrape_timeout: metric을 scrape하는데 time out을 얼마나 둘 것인지에 관한 옵션입니다. default는 10s 입니다.
    • evaluation_interval: 몇 초에 한번씩 규칙을 평가할 것인지 확인하는 옵션입니다. default 값은 1m 입니다.
  • scrape_configs
    • job_name이 하나의 key값이 되어, exporter로부터 metric을 수집합니다.

샘플 설정 파일

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: prometheus-server-conf
  namespace: monitoring
data:
  prometheus.yaml: |-
    global:
      scrape_interval: 15s
      scrape_timeout: 10s
      evaluation_interval: 15s
    rule_files:
      - "/etc/prometheus-rules/*.rules"
    alerting:
      alertmanagers:
      - scheme: http
        static_configs:
        - targets:
          - "alertmanager-http.monitoring.svc:9093"

    scrape_configs:
      - job_name: 'kubernetes-nodes'
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
          - role: node
        relabel_configs:
          - source_labels: [__address__]
            regex: '(.*):10250'
            replacement: '${1}:10255'
            target_label: __address__

      - job_name: 'kubernetes-service-endpoints'
        kubernetes_sd_configs:
          - role: endpoints
        relabel_configs:
          - source_labels: [__meta_kubernetes_pod_node_name]
            target_label: instance
          - source_labels: [__meta_kubernetes_pod_name]
            action: replace
            target_label: kubernetes_pod_name
          - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
            action: keep
            regex: true
          - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
            action: replace
            target_label: __scheme__
            regex: (https?)
          - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
            action: replace
            target_label: __metrics_path__
            regex: (.+)
          - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
            action: replace
            target_label: __address__
            regex: (.+)(?::\d+);(\d+)
            replacement: $1:$2
          - action: labelmap
            regex: __meta_kubernetes_service_label_(.+)
          - source_labels: [__meta_kubernetes_namespace]
            action: replace
            target_label: kubernetes_namespace
          - source_labels: [__meta_kubernetes_service_name]
            action: replace
            target_label: kubernetes_name

      - job_name: 'kubernetes-services'
        metrics_path: /probe
        params:
          module: [http_2xx]
        kubernetes_sd_configs:
          - role: service
        relabel_configs:
          - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
            action: keep
            regex: true
          - source_labels: [__address__]
            target_label: __param_target
          - target_label: __address__
            replacement: blackbox
          - source_labels: [__param_target]
            target_label: instance
          - action: labelmap
            regex: __meta_kubernetes_service_label_(.+)
          - source_labels: [__meta_kubernetes_namespace]
            target_label: kubernetes_namespace
          - source_labels: [__meta_kubernetes_service_name]
            target_label: kubernetes_name

      - job_name: 'kubernetes-pods'
        kubernetes_sd_configs:
          - role: pod
        relabel_configs:
          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
            action: keep
            regex: true
          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
            action: replace
            target_label: __metrics_path__
            regex: (.+)
          - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
            action: replace
            regex: ([^:]+)(?::\d+)?;(\d+)
            replacement: $1:$2
            target_label: __address__
          - action: labelmap
            regex: __meta_kubernetes_pod_label_(.+)
          - source_labels: [__meta_kubernetes_namespace]
            action: replace
            target_label: kubernetes_namespace
          - source_labels: [__meta_kubernetes_pod_name]
            action: replace
            target_label: kubernetes_pod_name
          - source_labels: [__meta_kubernetes_pod_container_port_number]
            action: keep
            regex: 9\d{3}

      - job_name: 'kubernetes-cadvisor'
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        kubernetes_sd_configs:
          - role: node
        relabel_configs:
          - action: labelmap
          - action: labelmap
            regex: __meta_kubernetes_node_label_(.+)
          - target_label: __address__
            replacement: kubernetes.default.svc:443
          - source_labels: [__meta_kubernetes_node_name]
            regex: (.+)
            target_label: __metrics_path__
            replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor

      - job_name: 'kube-state-metrics'
        static_configs:
          - targets: ['kube-state-metrics-http.monitoring:8080']

상위으 설정파일은 좀더 세밀하게 조정을 하는 내용입니다.

2-4. docker-compose 파일

version: '3.7'

services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - /data/docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - /data/docker/prometheus/prometheus_data:/prometheus
    ports:
      - 9090:9090
    restart: always

사실 docker-compose 파일에 대해서는 큰 내용은 없습니다. volume 구성으로 작성된 프로메테우스 설정파일과 데이터 저장폴더를 지정해주는 내용입니다.

3. Prometheus 실행

이제 준비가 다 됐습니다. 실제로 docker-compose를 올려보도록 하겠습니다.

$ docker-compose up -d
Creating network "prometheus_default" with the default driver
Creating prometheus ... done

9090포트를 사용하기로 하였으므로, 설치한 인스턴스의 아이피에 9090포트로 접속을 해봅니다.

/targets로 접속을 하게 되면, 현재 job config에 의해서 targeting 되는 내용들이 노출됩니다.

prometheus_1

설치가 완료되었습니다. 이렇게 해서 2대의 prometheus를 동일하게 셋팅을 할 예정입니다.

4. 마치며..

프로메테우스는 docker를 통해서 손쉽게 올릴 수 있었습니다. 그리고 현재까지는 아주 심플하게 올릴 수 있었지만, 나중에는 좀 더 심화된 설정에 대해서 정리를 좀 해보려고 합니다. 다음시간에는 프로메테우스에 대한 HA구성을 어떤식으로 하면되는지 현재 설치된 프로메테우스에 사이드카로 타노스를 사용할 예정입니다.

5. 출처

https://ooeunz.tistory.com/139?category=893464

Install Prometheus with docker

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다