본문 바로가기

Develop/DevOps

[Prometheus] ServiceMonitor 정리 및 AlertManger 관련

반응형

 

이번에 작업을 하면서 ServiceMonitor에 대해서 알게되었는데. 이게 정확히 어떤방식으로 동작하는지 정리를 하면 좋을것같아서 글을 작성합니다.

 

왜 사용함?

Prometheus에서 일반적으로는 static-configs에 옵션으로 수집할 대상(target)을 지정해주는데. 지정 대상이 한두개면 작업할만하지만, 지정 대상이 수백개 이상이 되어버리면 하나하나 지정해주기가 매우 불편한 상황이 옴. 따라서 더 유연하고 선언적인 방식으로 모니터링 타켓을 관리하기 위해 Prometheus Operator중 하나인 ServiceMonitor를 사용하게됨.

어떻게 사용함?

모니터링 대상 App에 /metrics 엔드포인트 노출

당연하지만 해당 포트에는 Exporter와 같이 메트릭을 노출하는 애플리케이션이 실행중이여야함

apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    app: my-app
  ports:
    - name: http
      port: 80
      targetPort: 8080

ServiceMonitor 정의

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
  labels:
    release: prometheus  # Prometheus CR와 연결하는 라벨
spec:
  selector:
    matchLabels:
      app: my-app        # 수집 대상 Service를 식별하는 라벨
  endpoints:
    - port: http         # Service에서 수집할 포트 이름
      path: /metrics     # 기본 경로는 /metrics
      interval: 15s      # 수집 주기

Prometheus가 해당 ServiceMonitor를 인식하도록 설정

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: k8s
spec:
  serviceMonitorSelector:
    matchLabels:
      release: prometheus  # ServiceMonitor와 같은 라벨이어야 함

 

Alertmanager에 대한 소소한 정리

Prometheus에서 Rule을 정의하고 이걸 알람을 발생시켜서 Slack에 보낸다고했을때 아래와같은 구조로 이뤄진다.

 

Prometheus에서 Alertmanager를 연결 진행. Prometheus Operator의 Prometheus CR을 사용하면 아래 변수를 설정하면된다. alertmanager를 띄워놓은 namespace그리고 연결하는 port를 지정해주면 된다.

...
      alerting = {
        alertmanagers = [
          {
            namespace = var.prometheus_stack_namespace
            name      = "alertmanager-operated"
            port      = "web"
          }
        ]
      }
 ...

 

그런다음 Alertmanager에서 Slack으로 메세지를 전송하기 위해서는 아래와같은 요소가 필요함

terraform으로는 아래와같이 작업함

resource "kubernetes_service_account" "alertmanager" {
  metadata {
    name      = "alertmanager-main"
    namespace = var.prometheus_stack_namespace
  }
}

resource "kubernetes_secret_v1" "slack_api_url" {
  metadata {
    name      = "slack-api-url"
    namespace = var.prometheus_stack_namespace
  }
  data = {
    # service-status web hook url
    url = ""
  }
}

# https://prometheus-operator.dev/docs/developer/alerting/#using-alertmanagerconfig-resources
resource "kubernetes_manifest" "alertmanager_config" {
  manifest = {
    apiVersion = "monitoring.coreos.com/v1alpha1"
    kind       = "AlertmanagerConfig"
    metadata = {
      name      = "alertmanager-config"
      namespace = var.prometheus_stack_namespace
    }
    spec = {
      route = {
        repeatInterval = "12h"
        receiver       = "slack"
      }
      # https://prometheus-operator.dev/docs/api-reference/api/#monitoring.coreos.com/v1alpha1.Receiver
      receivers = [
        {
          name = "slack"
          slackConfigs = [
            {
              apiURL = {
                key  = "url"
                name = kubernetes_secret_v1.slack_api_url.metadata[0].name
              }
              sendResolved = true
              # message format from https://grafana.com/blog/2020/02/25/step-by-step-guide-to-setting-up-prometheus-alertmanager-with-slack-pagerduty-and-gmail/?utm_source=chatgpt.com
              title    = <<-EOT
                [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
                {{- if gt (len .CommonLabels) (len .GroupLabels) -}}
                  {{" "}}(
                  {{- with .CommonLabels.Remove .GroupLabels.Names }}
                    {{- range $index, $label := .SortedPairs -}}
                      {{ if $index }}, {{ end }}
                      {{- $label.Name }}="{{ $label.Value -}}"
                    {{- end }}
                  {{- end -}}
                  )
                {{- end }}
                EOT
              text     = <<-EOT
                {{ range .Alerts -}}
                *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }}
                *Cluster:* Moreh-QA
                *Description:* {{ .Annotations.description }}
                *Details:*
                  {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}`
                  {{ end }}
                {{ end }}
                EOT
              mrkdwnIn = ["text", "title"]
            }
          ]
        }
      ]
    }
  }
}

resource "kubernetes_manifest" "alertmanager_main" {
  manifest = {
    apiVersion = "monitoring.coreos.com/v1"
    kind       = "Alertmanager"
    metadata = {
      name      = "main"
      namespace = var.prometheus_stack_namespace
    }
    spec = {
      replicas           = 1
      serviceAccountName = kubernetes_service_account.alertmanager.metadata[0].name
      alertmanagerConfiguration = {
        name = kubernetes_manifest.alertmanager_config.manifest.metadata.name
      }
    }
  }
  wait {
    condition {
      type   = "Available"
      status = "True"
    }
  }
}
반응형