Terraform으로 Prometheus 규칙 관리

Terraform으로 Prometheus 규칙 관리

Knowledge Graph의 Prometheus 규칙을 사용하면 메트릭 데이터에 대해 평가되는 사용자 지정 recording 및 alerting 규칙을 정의할 수 있어요. Recording 규칙은 자주 사용되거나 계산 비용이 높은 표현식을 미리 계산하고 결과를 새 시계열로 저장해요. Alerting 규칙은 조건이 충족되면 알림을 트리거하는 조건을 정의해요.

grafana_asserts_prom_rule_file 리소스를 사용하면 이러한 규칙을 코드로 관리하여 버전 관리, 검토 프로세스, 환경 간 일관된 배포를 가능하게 해요.

출처: 문서

본문

시작하기 전에

Terraform으로 Prometheus 규칙을 관리하려면 다음이 필요해요:

  • Knowledge Graph가 활성화된 Grafana Cloud 스택
  • Knowledge Graph용으로 구성된 Terraform
  • PromQL 및 Prometheus 규칙 개념에 대한 이해

기본 recording 규칙 만들기

Recording 규칙을 사용하면 PromQL 표현식을 미리 계산하고 결과를 새 메트릭으로 저장할 수 있어요. 자주 실행하는 비용이 높은 쿼리에 유용해요.

prom-rules.tf라는 파일을 만들고 다음 내용을 추가하세요:

# Basic recording rule for request rate
resource "grafana_asserts_prom_rule_file" "request_rates" {
  provider = grafana.asserts

  name   = "request-rates"
  active = true

  group {
    name     = "request_rate_rules"
    interval = "30s"

    rule {
      record = "job:http_requests_total:rate5m"
      expr   = "sum(rate(http_requests_total[5m])) by (job)"

      labels = {
        aggregation = "job"
        source      = "custom"
      }
    }
  }
}

alerting 규칙 만들기

Alerting 규칙은 충족 시 알림을 트리거하는 조건을 정의해요. record 대신 alert 필드를 사용해 alerting 규칙을 정의하세요.

# Alerting rules for service health
resource "grafana_asserts_prom_rule_file" "service_alerts" {
  provider = grafana.asserts

  name   = "service-health-alerts"
  active = true

  group {
    name     = "service_health"
    interval = "1m"

    rule {
      alert    = "HighErrorRate"
      expr     = "sum(rate(http_requests_total{code=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) > 0.05"
      duration = "5m"

      labels = {
        severity = "critical"
        team     = "platform"
      }

      annotations = {
        summary     = "High error rate detected"
        description = "Error rate is above 5% for the last 5 minutes"
        runbook_url = "https://docs.example.com/runbooks/high-error-rate"
      }
    }

    rule {
      alert    = "ServiceDown"
      expr     = "up == 0"
      duration = "2m"

      labels = {
        severity = "critical"
      }

      annotations = {
        summary     = "Service is down"
        description = "{{ $labels.job }} has been down for more than 2 minutes"
      }
    }
  }
}

여러 규칙 그룹 만들기

관련 규칙을 고유한 평가 간격을 가진 그룹으로 구성하세요:

# Multiple rule groups for comprehensive monitoring
resource "grafana_asserts_prom_rule_file" "comprehensive_rules" {
  provider = grafana.asserts

  name   = "comprehensive-monitoring"
  active = true

  # Latency recording rules
  group {
    name     = "latency_recording"
    interval = "30s"

    rule {
      record = "job:http_request_duration_seconds:p99"
      expr   = "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))"

      labels = {
        quantile = "0.99"
      }
    }

    rule {
      record = "job:http_request_duration_seconds:p95"
      expr   = "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))"

      labels = {
        quantile = "0.95"
      }
    }

    rule {
      record = "job:http_request_duration_seconds:p50"
      expr   = "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))"

      labels = {
        quantile = "0.50"
      }
    }
  }

  # Latency alerting rules
  group {
    name     = "latency_alerts"
    interval = "1m"

    rule {
      alert    = "HighP99Latency"
      expr     = "job:http_request_duration_seconds:p99 > 1"
      duration = "5m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "High P99 latency detected"
        description = "P99 latency for {{ $labels.job }} is above 1 second"
      }
    }

    rule {
      alert    = "CriticalLatency"
      expr     = "job:http_request_duration_seconds:p99 > 5"
      duration = "2m"

      labels = {
        severity = "critical"
      }

      annotations = {
        summary     = "Critical latency detected"
        description = "P99 latency for {{ $labels.job }} is above 5 seconds"
      }
    }
  }

  # Throughput rules
  group {
    name     = "throughput_rules"
    interval = "1m"

    rule {
      record = "job:http_requests:rate1m"
      expr   = "sum(rate(http_requests_total[1m])) by (job)"
    }

    rule {
      alert    = "LowThroughput"
      expr     = "job:http_requests:rate1m < 10"
      duration = "5m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "Low throughput detected"
        description = "Request rate for {{ $labels.job }} is below 10 requests per minute"
      }
    }
  }
}

리소스 사용률 규칙 만들기

인프라 전반의 리소스 사용률을 모니터링하는 규칙을 정의하세요:

# Resource utilization monitoring rules
resource "grafana_asserts_prom_rule_file" "resource_utilization" {
  provider = grafana.asserts

  name   = "resource-utilization"
  active = true

  group {
    name     = "cpu_rules"
    interval = "30s"

    rule {
      record = "instance:cpu_utilization:avg5m"
      expr   = "1 - avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) by (instance)"
    }

    rule {
      alert    = "HighCPUUtilization"
      expr     = "instance:cpu_utilization:avg5m > 0.85"
      duration = "10m"

      labels = {
        severity = "warning"
        resource = "cpu"
      }

      annotations = {
        summary     = "High CPU utilization"
        description = "CPU utilization on {{ $labels.instance }} is above 85%"
      }
    }

    rule {
      alert    = "CriticalCPUUtilization"
      expr     = "instance:cpu_utilization:avg5m > 0.95"
      duration = "5m"

      labels = {
        severity = "critical"
        resource = "cpu"
      }

      annotations = {
        summary     = "Critical CPU utilization"
        description = "CPU utilization on {{ $labels.instance }} is above 95%"
      }
    }
  }

  group {
    name     = "memory_rules"
    interval = "30s"

    rule {
      record = "instance:memory_utilization:ratio"
      expr   = "1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)"
    }

    rule {
      alert    = "HighMemoryUtilization"
      expr     = "instance:memory_utilization:ratio > 0.85"
      duration = "10m"

      labels = {
        severity = "warning"
        resource = "memory"
      }

      annotations = {
        summary     = "High memory utilization"
        description = "Memory utilization on {{ $labels.instance }} is above 85%"
      }
    }
  }

  group {
    name     = "disk_rules"
    interval = "1m"

    rule {
      record = "instance:disk_utilization:ratio"
      expr   = "1 - (node_filesystem_avail_bytes{mountpoint=\"/\"} / node_filesystem_size_bytes{mountpoint=\"/\"})"
    }

    rule {
      alert    = "DiskSpaceLow"
      expr     = "instance:disk_utilization:ratio > 0.80"
      duration = "15m"

      labels = {
        severity = "warning"
        resource = "disk"
      }

      annotations = {
        summary     = "Disk space running low"
        description = "Disk utilization on {{ $labels.instance }} is above 80%"
      }
    }
  }
}

Kubernetes 특화 규칙 만들기

Kubernetes 워크로드 모니터링을 위한 규칙을 정의하세요:

# Kubernetes workload monitoring rules
resource "grafana_asserts_prom_rule_file" "kubernetes_rules" {
  provider = grafana.asserts

  name   = "kubernetes-workloads"
  active = true

  group {
    name     = "kubernetes_pod_rules"
    interval = "30s"

    rule {
      record = "namespace:pod_restarts:rate1h"
      expr   = "sum(increase(kube_pod_container_status_restarts_total[1h])) by (namespace)"
    }

    rule {
      alert    = "PodCrashLooping"
      expr     = "rate(kube_pod_container_status_restarts_total[15m]) * 60 * 15 > 3"
      duration = "5m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "Pod is crash looping"
        description = "Pod {{ $labels.namespace }}/{{ $labels.pod }} is restarting frequently"
      }
    }

    rule {
      alert    = "PodNotReady"
      expr     = "kube_pod_status_ready{condition=\"true\"} == 0"
      duration = "10m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "Pod not ready"
        description = "Pod {{ $labels.namespace }}/{{ $labels.pod }} has been in a non-ready state for more than 10 minutes"
      }
    }
  }

  group {
    name     = "kubernetes_deployment_rules"
    interval = "1m"

    rule {
      record = "deployment:replicas_unavailable:count"
      expr   = "kube_deployment_status_replicas_unavailable"
    }

    rule {
      alert    = "DeploymentReplicasMismatch"
      expr     = "kube_deployment_spec_replicas != kube_deployment_status_replicas_available"
      duration = "10m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "Deployment replicas mismatch"
        description = "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} has not matched the expected number of replicas for more than 10 minutes"
      }
    }
  }

  group {
    name     = "kubernetes_node_rules"
    interval = "1m"

    rule {
      alert    = "NodeNotReady"
      expr     = "kube_node_status_condition{condition=\"Ready\",status=\"true\"} == 0"
      duration = "5m"

      labels = {
        severity = "critical"
      }

      annotations = {
        summary     = "Kubernetes node not ready"
        description = "Node {{ $labels.node }} has been unready for more than 5 minutes"
      }
    }

    rule {
      alert    = "NodeMemoryPressure"
      expr     = "kube_node_status_condition{condition=\"MemoryPressure\",status=\"true\"} == 1"
      duration = "5m"

      labels = {
        severity = "warning"
      }

      annotations = {
        summary     = "Node under memory pressure"
        description = "Node {{ $labels.node }} is under memory pressure"
      }
    }
  }
}

조건부 규칙 비활성화 사용

disable_in_groups 필드를 사용해 특정 그룹의 특정 규칙을 비활성화하세요:

# Rules with conditional disabling
resource "grafana_asserts_prom_rule_file" "conditional_rules" {
  provider = grafana.asserts

  name   = "conditional-alerting"
  active = true

  group {
    name     = "production_alerts"
    interval = "1m"

    rule {
      alert    = "HighErrorRate"
      expr     = "sum(rate(http_requests_total{code=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) > 0.01"
      duration = "5m"

      labels = {
        severity    = "critical"
        environment = "production"
      }

      annotations = {
        summary     = "High error rate in production"
        description = "Error rate is above 1% for the last 5 minutes"
      }

      # Disable this rule in staging group
      disable_in_groups = ["staging_alerts"]
    }
  }

  group {
    name     = "staging_alerts"
    interval = "1m"

    rule {
      alert    = "HighErrorRate"
      expr     = "sum(rate(http_requests_total{code=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) > 0.10"
      duration = "10m"

      labels = {
        severity    = "warning"
        environment = "staging"
      }

      annotations = {
        summary     = "High error rate in staging"
        description = "Error rate is above 10% for the last 10 minutes"
      }
    }
  }
}

비활성 규칙 파일 관리

규칙 파일 전체를 삭제하지 않고 일시적으로 비활성화하세요:

# Inactive rules file (not evaluated)
resource "grafana_asserts_prom_rule_file" "experimental_rules" {
  provider = grafana.asserts

  name   = "experimental-rules"
  active = false  # Rules are not evaluated

  group {
    name     = "experimental_alerts"
    interval = "1m"

    rule {
      alert    = "ExperimentalAlert"
      expr     = "some_experimental_metric > 100"
      duration = "5m"

      labels = {
        severity = "info"
      }

      annotations = {
        summary = "Experimental alert triggered"
      }
    }
  }
}

리소스 참조

grafana_asserts_prom_rule_file

Knowledge Graph API를 통해 Prometheus recording 및 alerting 규칙을 관리해요. 이 리소스를 사용하면 메트릭 데이터에 대해 평가되는 사용자 지정 Prometheus 규칙을 만들고 관리할 수 있어요.

인자(Arguments)

이름 유형 필수 설명
name string Yes Prometheus 규칙 파일의 이름. 이 필드는 불변(immutable)이며 변경 시 재생성을 강제해요.
active bool No 규칙 파일이 활성 상태인지 여부. 비활성 규칙은 평가되지 않아요. 기본값은 true.
group list(object) Yes Prometheus 규칙 그룹 목록. 자세한 내용은 group 블록을 참고하세요.

Group 블록

각 group 블록은 공유된 평가 간격을 가진 관련 규칙 집합을 포함해요:

이름 유형 필수 설명
name string Yes 규칙 그룹의 이름(예: latency_monitoring).
interval string No 이 그룹의 평가 간격(예: 30s, 1m). 지정하지 않으면 전역 평가 간격을 사용해요.
rule list(object) Yes 이 그룹의 Prometheus 규칙 목록. 자세한 내용은 rule 블록을 참고하세요.

Rule 블록

각 rule 블록은 recording 또는 alerting 규칙을 정의해요. record 또는 alert 중 하나를 지정해야 하며, 둘 다는 안 돼요:

이름 유형 필수 설명
record string Conditional recording 규칙의 출력 시계열 이름. alert를 지정하지 않으면 필수예요.
alert string Conditional alerting 규칙의 알림 이름. record를 지정하지 않으면 필수예요.
expr string Yes 평가할 PromQL 표현식.
duration string No alert가 발화(firing)되기 전에 조건이 유지되어야 하는 시간(예: 5m). alerting 규칙에만 해당. Prometheus의 for에 매핑돼요.
labels map(string) No 결과 시계열 또는 알림에 첨부할 라벨.
annotations map(string) No 알림에 추가할 주석(예: summary, description). alerting 규칙에만 적용 가능.
disable_in_groups set(string) No 이 규칙을 비활성화할 그룹 이름 목록. 조건부 규칙 활성화에 유용해요.

예시

resource "grafana_asserts_prom_rule_file" "example" {
  provider = grafana.asserts

  name   = "example-rules"
  active = true

  group {
    name     = "example_group"
    interval = "1m"

    # Recording rule
    rule {
      record = "job:http_requests:rate5m"
      expr   = "sum(rate(http_requests_total[5m])) by (job)"
    }

    # Alerting rule
    rule {
      alert    = "HighErrorRate"
      expr     = "job:http_errors:rate5m > 0.05"
      duration = "5m"

      labels = {
        severity = "critical"
      }

      annotations = {
        summary     = "High error rate detected"
        description = "Error rate for {{ $labels.job }} is above 5%"
      }
    }
  }
}

모범 사례

Terraform으로 Prometheus 규칙을 관리할 때 다음 모범 사례를 고려하세요.

규칙 구성

  • 관련 규칙을 같은 규칙 그룹으로 묶으세요.
  • 목적을 나타내는 설명적인 이름을 규칙에 사용하세요.
  • 평가 요구 사항이 다른 경우 recording 규칙과 alerting 규칙을 다른 그룹으로 분리하세요.
  • level:metric:operation 같은 기록된 메트릭의 일관된 명명 규칙을 사용하세요.

Recording 규칙

  • 자주 실행되는 비용이 높은 쿼리에 recording 규칙을 사용하세요.
  • 여러 대시보드나 알림에서 사용되는 집계를 미리 계산하세요.
  • 데이터가 변경되는 빈도에 따라 적절한 평가 간격을 선택하세요.
  • job:metric:aggregation 같은 Prometheus 명명 규칙을 따르세요.

Alerting 규칙

  • 알림 플래핑(flapping)을 피하려면 적절한 duration 값을 설정하세요.
  • 라우팅을 위해 severity, team 같은 의미 있는 라벨을 포함하세요.
  • summary, description, runbook_url을 포함한 포괄적인 주석을 제공하세요.
  • 컨텍스트를 제공하려면 {{ $labels.job }} 같은 주석의 템플릿 변수를 사용하세요.

성능 고려 사항

  • 타임아웃될 수 있는 지나치게 복잡한 PromQL 표현식을 피하세요.
  • 적절한 평가 간격을 사용하세요. 필요 이상으로 빠르게 평가하지 마세요.
  • Terraform에 추가하기 전에 Grafana에서 PromQL 쿼리를 테스트하세요.
  • 새 시계열을 만드는 recording 규칙의 카디널리티 영향에 주의하세요.

테스트 및 검증

  • 프로덕션에 배포하기 전에 프로덕션 외 환경에서 규칙을 테스트하세요.
  • 규칙을 평가하지 않고 스테이징하려면 active = false 플래그를 사용하세요.
  • 배포 후 새 규칙의 성능 영향을 모니터링하세요.
  • 사용하지 않는 규칙을 주기적으로 검토하고 제거하세요.

검증

Terraform 구성을 적용한 후 다음을 확인하세요:

  • 규칙 파일이 Knowledge Graph 규칙 구성에 생성되고 보이는지.
  • Recording 규칙이 예상 시계열을 생성하는지.
  • Alerting 규칙이 올바르게 평가되는지.
  • 알림 임계값이 예상대로 알림을 트리거하는지.
  • 규칙 평가 시간이 허용 가능한 범위 내에 있는지.
  • 규칙 평가 로그에 오류가 나타나지 않는지.

문제 해결

규칙이 평가되지 않음

규칙이 평가되지 않는 경우:

  • 규칙 파일에 active = true가 설정되어 있는지 확인하세요.
  • 규칙 파일명이 명명 검증 규칙(영숫자, 하이픈, 밑줄)을 따르는지 확인하세요.
  • Grafana에서 테스트해 PromQL 표현식이 유효한지 확인하세요.

Recording 규칙이 데이터를 생성하지 않음

recording 규칙이 데이터를 생성하지 않는 경우:

  • 소스 메트릭이 존재하고 데이터가 있는지 확인하세요.
  • PromQL 표현식이 결과를 반환하는지 확인하세요.
  • 평가 간격이 적절한지 확인하세요.
  • 오류가 있는지 규칙 평가 로그를 검토하세요.

알림이 발화되지 않음

alerting 규칙이 발화되지 않는 경우:

  • 표현식을 수동으로 쿼리해 조건이 실제로 충족되었는지 확인하세요.
  • duration 기간이 경과했는지 확인하세요.
  • 알림 시스템이 알림을 받도록 올바르게 구성되었는지 확인하세요.
  • 구문 오류가 있는지 주석 템플릿을 검토하세요.

규칙 재생성 시 가져오기 오류

규칙을 재생성할 때 오류가 발생하면:

  • name 필드는 불변이며 변경 시 재생성을 강제해요.
  • 같은 이름의 충돌하는 규칙 파일이 없는지 확인하세요.
  • 필요하면 terraform import를 사용해 기존 리소스를 가져오세요.

관련 문서

더 알아보기 (Learn more)