Prometheus を CDK でインストールして Recording rules で集計したデータを New Relic に Remote write することでデータ量を節約する

awskubernetesnewrelicprometheus

Prometheus の Recording rules は PromQL で既存のメトリクスから新たなメトリクスを作ることができる機能。これによって集計したデータを送ることで生のデータを送るのと比べて New Relic へのデータ送信量を節約することができるが、newrelic-bundle に含まれる newrelic-prometheus-configurator の Prometheus は Agent mode で動くので Recording rules に対応していない

CDKでEKSクラスタにnewrelic-bundleをインストールしてモニタリングする - sambaiz-net

そこで Helm で自前の Prometheus をインストールして Remote write する。

cluster.addHelmChart('PrometheusOperatorChart', {
  release: 'kube-prometheus-stack',
  chart: 'kube-prometheus-stack',
  repository: 'https://prometheus-community.github.io/helm-charts',
  version: '59.1.0',
  namespace: 'prometheus',
  createNamespace: true,
  values: {
    defaultRules: {
      create: false,
    },
    prometheus: {
      enabled: true,
      prometheusSpec: {
        storageSpec: {
          volumeClaimTemplate: {
            spec: {
              resources: {
                requests: {
                  storage: '30Gi',
                },
              },
            },
          },
        },
        retention: '7d',
        retentionSize: '25GB',
        remoteWrite: [{
          url: 'https://metric-api.newrelic.com/prometheus/v1/write?prometheus_server=prometheus',
          authorization: {
            type: 'Bearer',
            credentials: {
              key: secretKey,
              name: secretName,
            }
          },
          writeRelabelConfigs: [
            {
              sourceLabels: ['aaaa'],
              separator: ';',
              regex: 'bbbb',
              action: 'keep',
            },
          ],
        }],
      },
    },
    nodeExporter: {
      enabled: false,
    },
    alertmanager: {
      enabled: false,
    },
    grafana: {
      enabled: false,
    },
  },
})

これをインストールすると Prometheus などの CRD を介して Prometheus server が立ち上がる。Operator を用いない Chart もあるが、 extraScrapeConfigs が文字列なのでインデントが正しくないとエラーになって設定しづらかったりするのと、Operator には各種 CRD によってアプリケーション側での必要に応じて Prometheus server を立ち上げたりスクレイピングの設定を行いやすいという利点がある。

const extraScrapeConfigs = 
`- job_name: newrelic-pods
  honor_timestamps: true
  scrape_interval: 30s
  scrape_timeout: 10s
  ...`

デフォルトで Alertmanager なども立ち上がるが、今回は使わないので無効化している。

Service|PodMonitor でスクレイピングする targets の設定ができ、 PrometheusRule で Recording rules を設定できる。メトリクス名は level:metric:operations の形式にする。

podMonitorSelectorNilUsesHelmValues がデフォルトの true のとき、 service|podMonitorSelector が matchLabels: release: になるため、このラベルを付けないと configuration に読み込まれない。configuration に読み込まれているが targets に出ない場合 selector などの値が正しいかを確認する。podMetricsEndpoints の port は名前しか渡すことができず、番号は __meta_kubernetes_pod_container_port_number に対応する targetPort で渡す必要がある。

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: testapp-monitor
  namespace: prometheus
  labels:
    release: kube-prometheus-stack
spec:
  selector:
    matchLabels:
      app: testapp
  namespaceSelector:
    matchNames:
    - testapp
  podMetricsEndpoints:
  - port: monitor
    path: /metrics
    interval: 5m
    scrapeTimeout: 10s
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: example
  namespace: prometheus
  labels:
    release: kube-prometheus-stack
spec:
  groups:
  - name: example
    interval: 5m
    rules:
    - record: code:prometheus_http_requests_total:sum
      expr: sum by (code) (prometheus_http_requests_total)
      labels:
        aaaa: bbbb

Prometheus server にポートフォワードして PromQL で Recording rules のメトリクスを取得してみる。

$ kubectl -n prometheus port-forward svc/prometheus-server 9090:80
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090

$ curl -G 'http://localhost:9090/api/v1/query' --data-urlencode 'query=code:prometheus_http_requests_total:sum' | jq
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {
          "__name__": "code:prometheus_http_requests_total:sum",
          "aaaa": "bbbb",
          "code": "200"
        },
        "value": [
          1716339421.309,
          "182"
        ]
      },
      {
        "metric": {
          "__name__": "code:prometheus_http_requests_total:sum",
          "aaaa": "bbbb",
          "code": "302"
        },
        "value": [
          1716339421.309,
          "2"
        ]
      }
    ]
  }
}

New Relic にも送られていることが確認できる。

FROM Metric 
SELECT latest(`code:prometheus_http_requests_total:sum`) 
WHERE prometheus_server = 'prometheus' FACET code