PrometheusとGrafanaを使ったマイクロサービスの監視

Navendu Pottekkat

Navendu Pottekkat

June 8, 2023

Ecosystem

マイクロサービスシステムを堅牢にするためには、継続的な監視が不可欠です。適切な監視がないと、マイクロサービスはすぐに過負荷になり、エラーやパフォーマンスの低下を引き起こす可能性があります。

継続的な監視を通じて、開発者はサービスに問題が発生した際にすぐに検出し、重大な損害を防ぐための対策を講じることができます。また、サービスのパフォーマンスに関する洞察を提供し、情報に基づいた意思決定を可能にします。

この記事では、PrometheusGrafana という2つの人気ツールを使用して、マイクロサービスアプリケーションに監視を設定する方法を紹介します。

このチュートリアルのソースコードとDocker Composeファイルは、pottekkat/monitoring-101 で入手できます。

Prometheusの基本

Prometheusは、オープンソースの監視およびアラートツールです。マイクロサービスからHTTPリクエストを送信してメトリクス(測定値)を「プル」し、その結果を時系列データベースに保存します。

Prometheusが提供するクライアントライブラリを使用して、サービスを計装することができます。これにより、サービスからカスタムメトリクスを作成して収集することが可能になります。

Prometheusには、Prometheus形式ではないメトリクスをプルするためのエクスポーターもあります。エクスポーターは仲介役として機能し、エクスポートされたデータをPrometheusが読み取れる形式に変換します。

1-prometheus-architecture.png

Prometheusは、収集したデータを操作するための強力なクエリ言語であるPromQLを提供しています。PromQLを使用して、データをフィルタリング、集計、変換するための複雑なクエリを作成できます。

メトリクスをプルするだけでなく、Prometheusは設定された閾値を超えた場合にアラートをトリガーすることもできます。アラートメカニズムは高度に設定可能で、Slackやメールなどに通知を送信できます。

Prometheusには、収集したメトリクスを簡単に視覚化するためのGUIも備わっています。また、Grafanaのような高度な視覚化ツールとも統合できます。

prometheus-dashboard.png

メトリクスの種類

Prometheusは、4つの主要なメトリクスタイプを提供しています:

  1. Counter: 単調増加するカウンターを表します。値は増加するか、再起動時にゼロにリセットされます。リクエスト数などのメトリクスを表すために使用できます。
  2. Gauge: 上下する数値を表します。メモリ使用量や1秒あたりのリクエスト数などの値を表すために使用できます。
  3. Histogram: データを設定可能なバケットにサンプリングします。リクエストの持続時間やレスポンスサイズなどの値を表すために使用できます。
  4. Summary: Histogramと同様に、スライディングタイムウィンドウで設定可能な値を計算します。

これらのメトリクスタイプとその使用方法について詳しくは、公式ドキュメントを参照してください。

サンプルアプリケーション

このサンプルアプリケーションは、APIゲートウェイ、Goアプリ、Pythonアプリで構成されています。

2-sample-app-flowchart.png

このアプリケーションは、選択した言語で「Hello <name>!」を返します。Apache APISIXがAPIゲートウェイとして機能し、トラフィックをサービスに転送します。

以下の図は、システムの動作を示しています。

3-sample-app-sequence.png

  1. ユーザーがAPISIXにGETリクエストを送信します。APISIXはアプリケーションのエントリーポイントです。
  2. APISIXがリクエストをGoサービスに転送します。
  3. GoサービスがPythonサービスにGETリクエストを送信し、指定された言語で「Hello」を取得します。
  4. Pythonサービスが「Hello」の翻訳を返します。
  5. Goサービスがクエリで提供された名前を使用して必要なレスポンスを作成し、APISIXに送信します。
  6. APISIXがレスポンスをユーザーに返します。

Prometheusを設定してメトリクスを収集する

アプリケーション内のすべてのサービスからメトリクスを計装してエクスポートし、Prometheusで収集します。まず、APIゲートウェイであるApache APISIXから始めます。

APISIXからメトリクスをエクスポートする

Apache APISIXは、オープンソースのクラウドネイティブAPIゲートウェイです。

APISIXについて詳しく知る必要はなく、提供されているDocker Composeファイルを使用してすべてを設定できます。APISIXについて詳しく知りたい場合は、api7.ai/apisixを参照してください。

APISIXは、Prometheus形式でメトリクスを簡単にエクスポートするprometheusプラグインを提供しています。このプラグインは、APISIXの設定ファイルで設定できます:

apisix:
  enable_admin: false # スタンドアロンモードでAPISIXを実行
  config_center: yaml # etcdではなくYAMLファイルを使用して設定を保存
plugin_attr:
  prometheus:
    export_uri: /prometheus/metrics # prometheusプラグインを有効にし、メトリクスをこのURIにエクスポート
    enable_export_server: false # デフォルトのデータプレーンポートでメトリクスをエクスポート

次に、すべてのルートでプラグインを有効にするために、グローバルルールとして設定します:

routes:
  # /hello/* へのリクエストをgo-appに転送
  - uri: /hello/*
    upstream:
      type: roundrobin
      nodes:
        "go-app:8080": 1
    plugins:
      # go-appにリクエストを転送する前にプレフィックス "/hello" を削除
      proxy-rewrite:
        regex_uri:
          - "/hello/(.*)"
          - "/$1"
  # 指定されたURIにPrometheusメトリクスをエクスポート
  - uri: /prometheus/metrics
    plugins:
      public-api:
# すべてのルートでPrometheusプラグインをグローバルに有効化
global_rules:
  - id: 1
    plugins:
      prometheus:
        prefer_name: true
#END

これにより、Apache APISIXの/prometheus/metricsエンドポイントにメトリクスがエクスポートされます。

利用可能なメトリクスについて詳しくは、ドキュメントを参照してください。

Goサービスからメトリクスを計装してエクスポートする

Prometheusには、Goアプリケーションを計装するための公式のGoクライアントライブラリがあります。

デフォルトでは、PrometheusはデフォルトのGoメトリクスを公開します。また、アプリケーション固有のメトリクスを作成することもできます。

このサービスでは、デフォルトのメトリクスを公開し、リクエスト数を追跡するための独自のカウンターメトリクスを作成します:

package main

import (
  "encoding/json"
  "fmt"
  "io/ioutil"
  "log"
  "net/http"
  "os"

  // Prometheusパッケージ
  "github.com/prometheus/client_golang/prometheus"
  "github.com/prometheus/client_golang/prometheus/promauto"
  "github.com/prometheus/client_golang/prometheus/promhttp"
)

// Responseはpython-appから取得したメッセージを格納
type Response struct {
  Message string `json:"message"`
}

// デフォルトの言語と名前
var (
  lang = "en"
  name = "John"
)

// 新しいカスタムPrometheusカウンターメトリクスを作成
var pingCounter = promauto.NewCounter(
  prometheus.CounterOpts{
    Name: "go_app_request_count",
    Help: "go-appが処理したリクエスト数",
  },
)

// HelloHandlerはgo-appへのリクエストを処理
func HelloHandler(w http.ResponseWriter, r *http.Request) {
  lang = r.URL.String()
  name = r.URL.Query()["name"][0]
  
  fmt.Println("Request for", lang, "with name", name)
  pingCounter.Inc()
  
  pUrl := os.Getenv("PYTHON_APP_URL")
  if len(pUrl) == 0 {
    pUrl = "localhost"
  }

  // python-appを呼び出して翻訳を取得
  resp, err := http.Get("http://" + pUrl + ":8000" + lang)
  if err != nil {
    log.Fatalln(err)
  }

  body, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    log.Fatalln(err)
  }

  resp.Body.Close()

  var m Response
  json.Unmarshal(body, &m)

  // 指定された言語で「Hello name!」を返す
  fmt.Fprintf(w, "%s %s!", m.Message, name)
}

func main() {
  // Prometheusメトリクスを公開
  http.Handle("/metrics", promhttp.Handler())
  http.HandleFunc("/", HelloHandler)
  
  http.ListenAndServe(":8080", nil)
}

これにより、/metricsエンドポイントにメトリクスが公開されます。Goクライアントライブラリについて詳しくは、GitHubリポジトリを参照してください。

Pythonサービスからメトリクスを計装してエクスポートする

Prometheusには、公式のPythonクライアントライブラリもあります。特定のユースケースに合わせたサードパーティのライブラリもあります。

このサービスでは、FastAPIを使用しており、prometheus_fastapi_instrumentatorライブラリを使用して計装します:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI()

hello = {"en": "Hello", "fr": "Bonjour", "es": "Hola", "ml": "ഹലോ"}

# デフォルトのPythonメトリクスを/metricsエンドポイントに公開
Instrumentator().instrument(app).expose(app)

@app.get("/{lang}")
async def get_hello(lang):
    return {"message": hello[lang]}

カスタムメトリクスの作成について詳しくは、ドキュメントを参照してください。

Prometheusの設定

これで、Prometheusでこれらのメトリクスをスクレイピングして収集できます。

各サービスからメトリクスを収集するようにPrometheusを設定できます。デフォルトでは、Prometheusは/metricsパスでメトリクスをチェックします:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
  
  - job_name: "go-app"
    static_configs:
      - targets: ["go-app:8080"]

  - job_name: "python-app"
    static_configs:
      - targets: ["python-app:8000"]

  - job_name: "apisix"
    static_configs:
      - targets: ["apisix:9080"]
    metrics_path: "/prometheus/metrics"

これで完了です!Prometheusダッシュボード(デフォルトではポート9090)を開き、ナビゲーションバーから「Status」をクリックし、「Targets」を選択すると、サービスからスクレイピングされたメトリクスのステータスを確認できます。

prometheus-target-status.png

Prometheusでメトリクスをクエリして視覚化する

これで、Prometheusダッシュボードを使用してクエリや複雑な式を実行できます。

prometheus-query.png

Prometheusのクエリについて詳しくは、公式ドキュメントを参照してください。

Grafanaを使用してPrometheusをクエリする

Grafanaは、Prometheusと連携してメトリクスを収集、クエリ、視覚化するための包括的なツールを提供するオープンソースのデータ視覚化プラットフォームです。

Prometheusはメトリクスの収集とクエリに優れていますが、意味のある視覚化を作成するためのツールが不足しています。Grafanaはこの制限を克服し、収集されたメトリクスを視覚化に変換します。

Grafanaは、Prometheus以外にも多くのデータソースと互換性があります。

Grafanaをデプロイしたら、Web UI(デフォルトではポート3000)を開きます。

まず、Prometheusをデータソースとして追加する必要があります。これを行うには、/datasourcesまたは「Configuration」と「Data sources」に移動します。「Add data source」をクリックし、Prometheusを選択します。Prometheusがデプロイされている場所を指定し、接続を保存してテストします。

grafana-add-prometheus.png

事前構築済みのGrafanaダッシュボードを使用する

Grafanaは、事前構築済みのGrafanaダッシュボードを含む公開ダッシュボードリポジトリをホストしています。これらのダッシュボードを使用して、関連するメトリクスを迅速に視覚化できます。

このチュートリアルでは、Prometheus Goクライアントライブラリによって公開されたプロセスステータスを処理して視覚化するGo Processesダッシュボードを使用します。

このテンプレートをインポートするには、まずダッシュボードリポジトリからそのID(6671)をコピーします。Grafana UIで、「Dashboards」に移動し、「Import」を選択します。コピーしたIDを貼り付け、「Load」をクリックします。

grafana-dashboard.png

他の事前構築済みのダッシュボードを探索したり、独自のダッシュボードを作成したりすることもできます。これについて詳しくは、ドキュメントを参照してください。

次のステップ

このチュートリアルは以上です!

この記事は、サービスに監視を設定する方法の紹介に過ぎません。以下のリソースからPrometheusとGrafanaについてさらに学ぶことをお勧めします:

このチュートリアルの完全なコードとDocker Composeファイルは、pottekkat/monitoring-101で入手できます。

Tags: