Prometheus 0.14.0 における高度なサービスディスカバリ

2015年6月1日筆者: Fabian Reinartz, Julius Volz

今週、Prometheus v0.14.0 をリリースしました。このバージョンでは、長らくお待ちいただいていた多くの機能追加と改善が行われています。

ユーザー側では、Prometheus が新しいサービスディスカバリメカニズムをサポートするようになりました。DNS-SRV レコードに加えて、標準で Consul をサポートし、ファイルベースのインターフェースにより独自のディスカバリメカニズムを接続できるようになりました。今後、Prometheus に他の一般的なサービスディスカバリメカニズムを追加していく予定です。

多くの小規模な修正や改善に加え、設定をランタイム中にリロードできるようになりました。Prometheus プロセスに SIGHUP を送信することで可能です。変更点の完全なリストについては、このリリースに関する変更履歴 をご確認ください。

このブログ記事では、組み込みのサービスディスカバリメカニズムを詳しく見ていき、いくつかの実践的な例を紹介します。追加のリソースとして、Prometheus の設定ドキュメント を参照してください。

Prometheus とターゲット

このブログ記事を正しく理解するためには、まず Prometheus がターゲットにどのようにラベルを付けるかを見ていく必要があります。

設定ファイルには、ターゲットラベルを設定できるさまざまな場所があります。これらは次の順序で適用され、後続のステージは先行するステージで設定されたラベルを上書きします。

  1. Prometheus インスタンスによってスクレイプされるすべてのターゲットに割り当てられるグローバルラベル。
  2. 各スクレイプ設定のデフォルト値として構成される job ラベル。
  3. スクレイプ設定内のターゲットグループごとに設定されるラベル。
  4. リラベリング による高度なラベル操作。

各ステージは、先行するステージからの衝突するラベルを上書きします。最終的に、単一のターゲットを記述するフラットなラベルセットが得られます。これらのラベルは、このターゲットからスクレイプされるすべての時系列にアタッチされます。

注意: 内部的には、ターゲットのアドレスさえも特別な __address__ ラベルに格納されます。これは、後で見るように、高度なラベル操作(リラベリング)中に役立ちます。__ で始まるラベルは、最終的な時系列には表示されません。

スクレイプ設定とリラベリング

ASCII プロトコルバッファ形式から YAML への移行に加え、Prometheus の設定における根本的な変更は、ジョブごとの設定からより汎用的なスクレイプ設定への変更です。単純なセットアップでは両者はほぼ同等ですが、スクレイプ設定はより高度なユースケースでより大きな柔軟性を提供します。

各スクレイプ設定は、job ラベルのデフォルト値として機能するジョブ名を定義します。その後、job ラベルは、ターゲットグループ全体または個々のターゲットに対して再定義できます。たとえば、各ジョブのターゲットを定義する 2 つのターゲットグループを定義できます。それらを同じパラメータでスクレイプするには、次のように設定できます。

scrape_configs:
- job_name: 'overwritten-default'

  scrape_interval: 10s
  scrape_timeout:  5s

  target_groups:
  - targets: ['10.1.200.130:5051', '10.1.200.134:5051']
    labels:
      job: 'job1'

  - targets: ['10.1.200.130:6220', '10.1.200.134:6221']
    labels:
      job: 'job2'

リラベリング と呼ばれるメカニズムにより、任意のラベルをターゲットごとに削除、作成、または変更できます。これにより、サービスディスカバリから取得したメタデータも考慮できる、きめ細かなラベリングが可能になります。リラベリングはラベル割り当ての最後のステージであり、以前に設定されたラベルをすべて上書きします。

リラベリングは次のように機能します。

  • ソースラベルのリストが定義されます。
  • 各ターゲットについて、それらのラベルの値が区切り文字で連結されます。
  • 正規表現が結果の文字列に対して一致されます。
  • それらのマッチに基づいた新しい値が別のラベルに割り当てられます。

各スクレイプ設定に対して複数のリラベリングルールを定義できます。2 つのラベルを 1 つにまとめる簡単なルールは次のようになります。

relabel_configs:
- source_labels: ['label_a', 'label_b']
  separator:     ';'
  regex:         '(.*);(.*)'
  replacement:   '${1}-${2}'
  target_label:  'label_c'

このルールは、ラベルセットを持つターゲットを次のように変換します。

{
  "job": "job1",
  "label_a": "foo",
  "label_b": "bar"
}

...ラベルセットを持つターゲットに変換します。

{
  "job": "job1",
  "label_a": "foo",
  "label_b": "bar",
  "label_c": "foo-bar"
}

その後、追加のリラベリングステップでソースラベルを削除することもできます。

リラベリングと、ターゲットのフィルタリングにどのように使用できるかについては、設定ドキュメントで読むことができます。

次のセクションでは、サービスディスカバリを使用する際にリラベリングをどのように活用できるかを見ていきます。

DNS-SRV レコードによるディスカバリ

最初から、Prometheus は DNS-SRV レコードによるターゲットディスカバリをサポートしていました。対応する設定は次のようでした。

job {
  name: "api-server"
  sd_name: "telemetry.eu-west.api.srv.example.org"
  metrics_path: "/metrics"
}

Prometheus 0.14.0 では、単一のスクレイプ設定で複数の SRV レコードを指定できるようになり、リラベリングフェーズで役立つサービスディスカバリ固有のメタ情報も提供されるようになりました。

DNS-SRV レコードをクエリすると、__meta_dns_name という名前のラベルが各ターゲットにアタッチされます。その値は、返された SRV レコード名に設定されます。telemetry.<zone>.<job>.srv.example.org のように構造化された SRV レコード名がある場合、それらの名前から関連ラベルを抽出できます。

scrape_configs:
- job_name: 'myjob'

  dns_sd_configs:
  - names:
    - 'telemetry.eu-west.api.srv.example.org'
    - 'telemetry.us-west.api.srv.example.org'
    - 'telemetry.eu-west.auth.srv.example.org'
    - 'telemetry.us-east.auth.srv.example.org'

  relabel_configs:
  - source_labels: ['__meta_dns_name']
    regex:         'telemetry\.(.+?)\..+?\.srv\.example\.org'
    target_label:  'zone'
    replacement:   '$1'
  - source_labels: ['__meta_dns_name']
    regex:         'telemetry\..+?\.(.+?)\.srv\.example\.org'
    target_label:  'job'
    replacement:   '$1'

これにより、SRV レコードのソースに基づいて、zone および job ラベルが各ターゲットにアタッチされます。

Consul によるディスカバリ

Consul を介したサービスディスカバリがネイティブでサポートされるようになりました。Consul エージェントへのアクセスパラメータと、ターゲットをクエリしたい Consul サービスの一覧を定義することで構成できます。

各 Consul ノードのタグは、構成可能な区切り文字で連結され、__meta_consul_tags ラベルを介して公開されます。その他のさまざまな Consul 固有のメタラベルも提供されます。

指定されたサービスの一覧のすべてのインスタンスをスクレイプすることは、単純な consul_sd_config とリラベリングルールで実現できます。

scrape_configs:
- job_name: 'overwritten-default'

  consul_sd_configs:
  - server:   '127.0.0.1:5361'
    services: ['auth', 'api', 'load-balancer', 'postgres']

  relabel_configs:
  - source_labels: ['__meta_consul_service']
    regex:         '(.*)'
    target_label:  'job'
    replacement:   '$1'
  - source_labels: ['__meta_consul_node']
    regex:         '(.*)'
    target_label:  'instance'
    replacement:   '$1'
  - source_labels: ['__meta_consul_tags']
    regex:         ',(production|canary),'
    target_label:  'group'
    replacement:   '$1'

これにより、ローカル Consul エージェントから指定されたサービスが検出されます。結果として、4 つのジョブ(authapiload-balancerpostgres)のメトリクスが得られます。ノードに production または canary の Consul タグがある場合、対応する group ラベルがターゲットに割り当てられます。各ターゲットの instance ラベルは、Consul によって提供されるノード名に設定されます。

Consul を介したサービスディスカバリのすべての設定パラメータの詳細なドキュメントは、Prometheus ウェブサイトで確認できます。

カスタムサービスディスカバリ

最後に、カスタムサービスディスカバリや、まだ標準でサポートされていないその他の一般的なメカニズムを統合するためのファイルベースのインターフェースを追加しました。

このメカニズムにより、Prometheus はターゲットグループを定義するディレクトリまたはファイルのセットを監視します。これらのファイルのいずれかが変更されるたびに、ファイルからターゲットグループのリストが読み取られ、スクレイプターゲットが抽出されます。任意のサービスディスカバリメカニズムから変更を取得し、ターゲット情報をターゲットグループのリストとして監視対象ファイルに書き込む小さなブリッジプログラムを作成するのが私たちの仕事です。

これらのファイルは YAML 形式でも

- targets: ['10.11.150.1:7870', '10.11.150.4:7870']
  labels:
    job: 'mysql'

- targets: ['10.11.122.11:6001', '10.11.122.15:6002']
  labels:
    job: 'postgres'

...または JSON 形式でも

[
  {
    "targets": ["10.11.150.1:7870", "10.11.150.4:7870"],
    "labels": {
      "job": "mysql"
    }
  },
  {
    "targets": ["10.11.122.11:6001", "10.11.122.15:6002"],
    "labels": {
      "job": "postgres"
    }
  }
]

Prometheus は、作業ディレクトリ内の tgroups/ ディレクトリにあるすべての .json ファイルを監視するように設定します。

scrape_configs:
- job_name: 'overwritten-default'

  file_sd_configs:
  - names: ['tgroups/*.json']

このディレクトリにファイルを書き込むプログラムが不足しています。この例では、さまざまなジョブのすべてのインスタンスが 1 つの非正規化された MySQL テーブルにあると仮定します。(ヒント: 通常、サービスディスカバリをこの方法で行うことはお勧めしません。)

30 秒ごとに、MySQL テーブルからすべてのインスタンスを読み込み、結果のターゲットグループを JSON ファイルに書き込みます。ターゲットまたはそのラベルが変更されたかどうかを状態として保持する必要がないことに注意してください。Prometheus は変更を自動的に検出し、スクレイプサイクルを中断することなくターゲットに適用します。

import os, time, json

from itertools import groupby
from MySQLdb import connect


def refresh(cur):
    # Fetch all rows.
    cur.execute("SELECT address, job, zone FROM instances")

    tgs = []
    # Group all instances by their job and zone values.
    for key, vals in groupby(cur.fetchall(), key=lambda r: (r[1], r[2])):
        tgs.append({
            'labels': dict(zip(['job', 'zone'], key)),
            'targets': [t[0] for t in vals],
        })

    # Persist the target groups to disk as JSON file.
    with open('tgroups/target_groups.json.new', 'w') as f:
        json.dump(tgs, f)
        f.flush()
        os.fsync(f.fileno())

    os.rename('tgroups/target_groups.json.new', 'tgroups/target_groups.json')


if __name__ == '__main__':
    while True:
        with connect('localhost', 'root', '', 'test') as cur:
            refresh(cur)
        time.sleep(30)

Prometheus は不正な形式の変更をファイルに適用しませんが、例のように名前変更によってアトミックにファイルを更新することがベストプラクティスと見なされます。また、大量のターゲットグループは論理的なグループ分けに基づいて複数のファイルに分割することが推奨されます。

結論

DNS-SRV レコードと Consul により、2 つの主要なサービスディスカバリ方法が Prometheus によってネイティブでサポートされるようになりました。リラベリングが、サービスディスカバリメカニズムが提供するメタデータを利用するための強力なアプローチであることがわかりました。

新しい 設定ドキュメント を確認して、Prometheus セットアップを新しいリリースにアップグレードし、基本的な HTTP 認証やリラベリングによるターゲットフィルタリングなどのその他の設定オプションについて調べてください。

既存の設定ファイルを新しい YAML フォーマットにアップグレードするための 移行ツール を提供しています。小規模な設定の場合は、新しいフォーマットに慣れるため、コメントを保持するために手動でのアップグレードをお勧めします。