Elasticsearchと可観測性:PrometheusとOpenMetricsスタンダードでメトリックを活用する

このブログ記事で取り上げる内容は次の通りです。

  • オープンスタンダードが重視される理由
  • Prometheus expositionフォーマット
  • Elasticが考える"可観測性"
  • ElasticsearchにPrometheusメトリックを取り込む3つの方法
  • Prometheus Redis exporterが出力したメトリックを 収集・可視化する方法

オープンスタンダード

opensource.com に、" What are Open Standards?(オープンスタンダードとは何か?)"と題された良い記事があります。 この記事には 素晴らしい指摘がたくさんありますが、 長年運用に携わってきた筆者の心に次の4点がとりわけ響きます。

  1. 可用性:オープンスタンダードはすべての人が読むことができ、 また実装できる。
  2. エンドユーザーの選択肢を最大化している。
  3. 差別しない(ベンダーの中立性):オープンスタンダードと その管理組織においては、あるユーザーを 別のユーザーより優遇する、といったことがない。
  4. "意図的な秘密"がない:そもそも「規格」は、相互運用可能な実装に必要とされる 一切の詳細情報を非公開にすべきではない。

オープンスタンダードが優れている理由について説得力ある議論を紹介したところで、 次に触れておきたいのは Prometheus expositionフォーマット OpenMetricsのベースに採用された背景です。リチャード・ハートマン氏は PromCon 2018とKubeCon + CloudNativeCon North America 2018で Prometheus expositionフォーマットの影響を受けた オープンスタンダードを作成する理由について以下のようにまとめています。

  • 多くのデータフォーマットは独自専有であるか、実装しにくい、 またはその両方にあてはまる
  • Prometheusはクラウドネイティブなメトリックモニタリングの分野で デファクトスタンダードとなっている
  • データexpositionの手軽さから、 互換性のあるメトリックエンドポイントは爆発的に増加している
  • Prometheusのexpositionフォーマットは豊富な運用経験に基づいて作成されているものの 設計に関わった開発者の数は少ない
  • 一部のプロジェクトやベンダーは、ある種"競合"するプロダクトの派生物を 採用するか否かで意見が割れる状況にある

Prometheus expositionフォーマット

expositionフォーマットについては Prometheus Github repoに詳しく書かれています。まず、具体的な例で見てみましょう。 たとえば Oliver006's Redis exporterというexporterは ポート9121のメトリックを/metricsエンドポイントでパブリッシュしています。 ここに Redisの"instantaneous ops per second"というメトリックに関する情報だけを抜粋してみました。 3つの行を 読み取ることができます:

  1. Helpテキスト
  2. メトリックのタイプ(この場合はゲージメトリック)
  3. 測定対象のRedisサーバー(localhost port 6379)と、 現在の読み取り内容(9 ops/秒)

roscigno-prometheus-exposition-format.png

Elasticにおける可観測性

Elasticブログに「 Elastic Stackによる可観測性」という記事があります。実際にぜひご一読いただきたいのですが、ここでは私が特に気に入っている部分を 引用します:

「観測可能な」システムの設計および構築の目標は、 本番環境で実行したときに担当のオペレーターが、 好ましくない振る舞い(サービスのダウンタイム、 エラー、反応の遅延など)を検知し、根本原因を特定するための 効果的な方法(詳細なイベントログ、リソース使用に関する細かい情報、 アプリケーションのトレースなど)で実用的な情報を得られるようにすることです。

私はこの主張を全面的に支持します。提供中のサービスを 管理、修復、そして実行する上で私たちが ログやメトリック、追跡情報に求めるものをすべて説明する一文です。 Prometheusは広く採用され、 活発なコミュニティを持ち、監視の世界で重要な一角を 占める存在になりました。 OpenMetricsスタンダードが実質的な、あるいは認識上の 障害を取り除くことでますますその価値は高まっており、 "運用第一で設計された"メトリックフォーマットという共通認識が形成されています。

私の知る限り、ロギングのユースケースでElastic Stackを 使いこなしている技術者は相当な数に上ります。 ただ、もしElastic StackがメトリックやAPMでも 大きな力を発揮することをまだご存知ないという場合はぜひ メトリック APM/分散トレーシングについてもチェックしていただければと思います。

ElasticがElastic StackとPrometheusスタイルのメトリック出力技術との 深い統合に関心を寄せる理由は、主に次の点です。

  • ElasticsearchでメトリックをログやAPMと組み合わせ、 Kibanaで相関付けできる。(Elastic Stackでログとメトリックを組み合わせる方法については、NS1の 導入事例をご参照ください )
  • 現在ネイティブにクラスタリングをサポートしていない Prometheusサーバーで収集されたメトリックの長期保存ストレージとして Elasticsearchを使用できる。
  • 地理的に分散したPrometheusインスタンスのメトリックを グローバルに表示できる

さてここからは、実際に統合を実現するための アプローチをご紹介します。

exporterのサンプル

今回取り上げるデモ環境はGoogle Kubernetes Engine(GKE)に あり、筆者のアプリケーション、Metricbeat、Prometheus exporterは すべてKubernetesで実行されています。 以下は、 Redis exporterをRedis画像とともにサイドカーとしてデプロイする Oliver006のマニフェストの抜粋です。 このexporterはご覧のとおり ポート9121でパブリッシュしていますが、これはPrometheus Redis exporter向けに デフォルトでアサインされるポート番号と なっています。

...
  - name: redis-exporter
    image: oliver006/redis_exporter:latest
    resources:
      requests:
        cpu:100m
        memory:100Mi
    ports:
    - containerPort:9121
...

GitHubに公開されているソース全文はこちら

Metricbeat Prometheusモジュールでメトリックをスクレイピングする

MetricbeatはElasticのメトリック向け軽量シッパーです。 Metricbeatのシッピングに使える Prometheusモジュールは、次の3つの方法でメトリックを 収集します。

  1. ポート9090でPrometheusサーバーに接続し、 PrometheusフェデレーションAPIを使用して収集済みのメトリックをプル (Prometheusが収集するメトリックを入手)する
  2. /metricsエンドポイントを使用し、ポート9090でPrometheusサーバーに 接続する(Prometheusの自己監視)
  3. 個々のPrometheus exporterに接続し、expositionフォーマットを パースする

問題は3つのアプローチからどれを選ぶかですが、その答えは Prometheusサーバーの快適度によります。

  • 既にメトリックのスクレイピング用に設定したPrometheusサーバーがあり、 統合の目的でそのメトリックに直接クエリをかけたい場合は (1)と(2)の方法がおすすめです。
  • 逆にPrometheusサーバーがないとか、複数のツールでexporterを並列に スクレイピングすることに抵抗がないという場合、 (3)を検討することができます。

注:Metricbeatの上述の機能の一部は、Metricbeatバージョン7.0で ベータリリースとなっています。 7.0ベータをダウンロード していただくか、 https://www.docker.elastic.co/からコンテナーリンクをコピーして本番前環境で実行されることをおすすめしています。

PrometheusフェデレーションAPI

通常、フェデレーションはスケールの実施やデータセットをまとめる作業、あるいはデータをコピーして(災害復旧用などに)別の場所で使えるようにするといった目的で使用されます。 Prometheusサーバーは/federationエンドポイントを提供します。ElasticのフェデレーションAPIは上述のすべての目的を満たせるよう、このエンドポイントに接続してPrometheusが収集したメトリックをコピーします。

...
  - module: prometheus
    period:10s
    hosts: ["prometheus-service.monitoring.svc.cluster.local:9090"]
    metrics_path: '/federate'
    query:
      'match[]': '{__name__!=""}'
...

GitHubに公開されているソース全文はこちら

上の例は、"空白でない名前をもつものすべて"をクエリする設定になっています。 常にすべての情報を把握したい状況ばかりではありません。そこでPrometheusドキュメントは、より制約的な一致条件を記述する方法について情報を記載しています。 またこのデモサーバーが収集するのはごく少数のpodとkube-state-metricsだけなので、この例では10秒ごとにPometheusサーバーへ接続しています。実際のユースケースでは、インターバルを変更することが可能です。

Prometheusの自己監視

各種exporterと同様、Prometheusも/metricsエンドポイントを提供しており、 ユーザーはPrometheusサーバーのメトリックを収集することができます。 これは次のようにして設定できます:

...
  - module: prometheus
    period:10s
    hosts: ["prometheus-service.monitoring.svc.cluster.local:9090"]
    metrics_path: /metrics
...

GitHubに公開されているソース全文はこちら

Prometheus exporterスクレイピング

下記はMetricbeat DaemonSet をデプロイし、 Metricbeatに kubernetes.labels.app == redisの状況とpodのポート9121のメトリックを自動探知させる マニフェストから抜粋したYAMLです。 Redis exporterコンテナーを9121に 設定したcontainerPortをリコールします。

...
- condition.equals:
    kubernetes.annotations.prometheus.io/scrape: "true"
  config:
    - module: prometheus
      period:10s
      # Redis pods
      hosts: ["${data.host}:9121"]
      metrics_path: /metrics
...

Metricbeatのデプロイが完了すると、 kubernetes.labels.app == redisの条件を満たすすべてのpodに Prometheusモジュールが適用され、ポート9121でexporterのサイドカーから メトリックが収集されます。

しかし、k8sの世界を動かす"主役"はメタデータです。 その点を考慮し、 Beatsの自動探知機能をもう少し工夫してみましょう。 先ほどのYAMLを 次のように書き換えてみます。

...
- condition.equals:
    kubernetes.annotations.prometheus.io/scrape: "true"
  config:
    - module: prometheus
      period:10s
      hosts: ["${data.host}:${data.kubernetes.annotations.prometheus.io/port}"]
      metrics_path: /metrics
...

GitHubに公開されているソース全文はこちら

これでRedis podのexporterを 参照する代わりに、 kubernetes.annotations.prometheus.io/scrapeがtrueに設定された注釈があるすべてのpodのexporterを参照する設定になりました。 またこれでPrometheusに自動探知を設定する方法 もお分かりいただけたはずです。 さて、一般的なMetricbeatの 自動探知は"elastic.co"名前空間の 注釈により駆動しますが、Prometheus exporterから来る情報を 扱うには、Prometheusに関連する標準的なk8sの注釈を受け取る設定 にする必要があります。 上述のホストリストを見ると次のようになっています。

hosts: ["${data.host}:${data.kubernetes.annotations.prometheus.io/port}"]

もうポート9121はハードコードされていないことがわかります。 9121はRedis exporterのポートだったためです。 prometheus.io/port という注釈は、exporterのポート番号に設定されています。 念のため、 guestbook.yamlで注釈を設定している部分も 見ておきましょう。

...
kind:Deployment
metadata:
  name: redis-master
spec:
  replicas:1
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port:"9121"
      labels:
        app: redis
...

GitHubに公開されているソース全文はこちら

さて、メタデータがk8sの世界を動かしていることは既にご説明しました。 70年代のヒットソング、「ピープル・メイク・ザ・ワールド・ゴー・ラウンド」についての説明は... 残念ですが省くことにします。

可視化でインサイトを得る

Elastic Stackにデータを投入するのは素晴らしいことですが、投入したデータは操作しなければなりません。 下の動画は、Prometheusでスクレイピングし(、さらにElastic Stackにインポートし)たRedisメトリックと、Metricbeatでkube-state-metricsから直接収集したKubernetesイベントを使って便利なビジュアライゼーションを構築する方法をご紹介しています。

動画の詳細な内容や手順は 、 example repoに掲載されています。

再び、"可観測性"について

上でご紹介した動画は、Oliver006's Redis exporterによってエクスポーズされた 主要なRedisメトリック("instantaneous ops per second")をKibanaで 可視化するというものでした。 この次に目指したいのが、「ログを収集して ダッシュボードを作成し、複数のアプリケーションのログとメトリックを 組み合わせる」という段階です。

Kubernetes環境でログを収集する具体的な手順に ついては、 elastic/examples GitHub repoにある例がおすすめです。 わずか数分で Filebeat、Metricbeat、Packetbeatでデータを収集し、 Elasticsearchにパブリッシュできます。 また異なる複数のBeatを使用して シッピングしたデータのダッシュボード例もあります。 可視化を組み合わせて使いやすくなるよう工夫するなど、 Prometheusデータもぜひ自由な発想で可視化してみてください。監視に関してご質問などがおありの場合は、 ディスカッションフォーラムもお役立てください。