PrometheusでKubernetes上のnginxを監視する方法

By 清水 孝郎 - JULY 1, 2022

SHARE:

本文の内容は、2022年6月30日にJesus Ángel Samitierが投稿したブログ(https://sysdig.com/blog/monitor-nginx-prometheus/)を元に日本語に翻訳・再構成した内容となっております。

nginx はオープンソースのウェブサーバーで、リバースプロキシ、ロードバランサー、ウェブキャッシュとしてよく利用されます。高負荷の同時接続用に設計されており、高速で汎用性が高く、信頼性が高く、そして最も重要なのは、リソースの消費が非常に少ないことです。この記事では、Prometheusを使ってKubernetes上のnginxを監視する方法と、レイテンシーやサチュレーションなどに関するさまざまな問題をトラブルシューティングする方法について説明します。

構成要素

始める前に、このプロジェクトで使用するツールをまとめておきましょう。


基本から始める:nginx exporter

Kubernetes上でnginxをPrometheusで監視する場合、まず最初に必要なことはnginx exporterのインストールです。推奨は、nginxサーバのサイドカーとして、デプロイメントに追加するだけでインストールできるようにすることです。以下のような内容である必要があります:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-server
spec:
  selector:
    matchLabels: null
  app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '9113'
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: nginx.conf
        - name: nginx-exporter
          image: 'nginx/nginx-prometheus-exporter:0.10.0'
          args:
            - '-nginx.scrape-uri=http://localhost/nginx_status'
          resources:
            limits:
              memory: 128Mi
              cpu: 500m
          ports:
            - containerPort: 9113
      volumes:
        - configMap:
            defaultMode: 420
            name: nginx-config
          name: nginx-config


このようにすると、各 nginx サーバー Pod に nginx exporter コンテナが追加さ れることになります。レプリカを 3 つ作成したので、3 つの Pod が作成され、それぞれに 1 つの nginx サーバー コンテナと 1 つの nginx エクスポーター コンテナが含まれることになります。この新しい設定を適用すると、完了です! nginx サーバーから簡単にメトリクスを公開することができます。

Prometheus で nginx 全体のステータスを監視する

うまくいったかどうか確認したいですか?簡単です。Prometheus にアクセスして、以下の PromQL を試してみてください。

sum (nginx_up)

これで、3つのコンテナで nginx_up が1つになったと報告されます。メトリクスのことはまだ気にしないでください。


Prometheus で nginx 接続を監視する

アクティブな接続

以下のメトリクスを使って、nginx のアクティブな接続を見てみましょう。どれが読み書きを行っているかに注目することもできます。

  • nginx_connections_active
  • nginx_connections_reading
  • nginx_connections_writing

これらを使うだけで、こんな感じになります:


未処理の接続

では、nginx で処理されていないコネクションがどれだけあるかに着目してみましょう。受理されたコネクションから処理されたコネクションを取り除けばいいのです。nginx exporter は両方のメトリクスを提供してくれます。

  • nginx_connections_handled
  • nginx_connections_accepted

では、受け付けられたコネクションのうち、処理されないコネクションの割合を求めてみましょう:

rate(nginx_connections_accepted{kube_cluster_name=~$cluster}[$__interval]) - rate(nginx_connections_handled{kube_cluster_name=~$cluster}[$__interval]) or vector(0) / rate(nginx_connections_accepted{kube_cluster_name=~$cluster}[$__interval]) * 100

PromQL をもっと深く知りたいですか?Prometheusがどのようにデータを保存し、PromQLの関数や演算子をどのように使用するかについては、PromQLの入門ガイドをお読みください。



うまくいけば、この数字はゼロに近くなりますよ!

接続待ち

幸いなことに、これも簡単なクエリーです。 nginx_connections_waiting と入力するだけです。これは nginx exporter がこの情報を公開するために使用するメトリクスです。


もっとメトリクスが必要ですか?ログから取得しましょう!

PrometheusでKubernetesのnginxを監視するためにさらに情報が必要な場合、nginxのaccess.logを使ってもう少し情報を取得することができます。その方法を見てみましょう。

オープンソースのデータコレクターであるFluentd

Fluentdを設定することで、nginxのaccess.logから情報を拾って、Prometheusのメトリクスに変換することができます。これは、インスツルメンテッド・アプリケーションがあまり情報を公開しないような状況において、とても便利です。

Fluentd のインストールと設定方法

FluentdとそのPrometheusプラグインについては、すでにここで説明しましたので、その記事の指示に従うだけで、準備は完了します。

Fluentd を設定して、さらにいくつかのメトリクスをエクスポートするようにしましょう

これを行うには、 access.log フォーマットを少し調整する必要があります。デフォルトのログ形式を選択し、最後に $upstream_response_time を追加します。 このように、Fluentdはこの変数を持ち、それを使用していくつかの有用なメトリクスを作成します。

name: nginx-config
data:
  nginx.conf: |
    log_format custom_format '$remote_addr - $remote_user [$time_local] '
      '"$request" $status $body_bytes_sent '
      '"$http_referer" "$http_user_agent" '
      '$upstream_response_time';
    server {
      access_log /var/log/nginx/access.log custom_format;
      ...
    }

この設定は  nginx.conf 、通常は  ConfigMap に記述します。

次に、Fluentd が新しいログのフォーマットを読み込むように設定する必要があります。これは Fluentd の  fileConfig セクションに nginx 用の新しい config を作成することでできます。

<source>
    @type prometheus_tail_monitor
</source>
<source>
    @type tail
    <parse>
    @type regexp
    expression /^(?<timestamp>.+) (?<stream>stdout|stderr)( (.))? (?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] \"(?<method>\w+)(?:\s+(?<path>[^\"]*?)(?:\s+\S*)?)?\" (?<status_code>[^ ]*) (?<size>[^ ]*)(?:\s"(?<referer>[^\"]*)") "(?<agent>[^\"]*)" (?<urt>[^ ]*)$/
        time_format %d/%b/%Y:%H:%M:%S %z
        keep_time_key true
        types size:integer,reqtime:float,uct:float,uht:float,urt:float
    </parse>
    tag nginx
    path /var/log/containers/nginx*.log
    pos_file /tmp/fluent_nginx.pos
</source>
<filter nginx>
     @type prometheus
</filter>

この設定で、基本的には nginx の access.log 用の正規表現パーサーを作成しました。これは expression の設定です。

expression /^(?<timestamp>.+) (?<stream>stdout|stderr)( (.))? (?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] \"(?<method>\w+)(?:\s+(?<path>[^\"]*?)(?:\s+\S*)?)?\" (?<status_code>[^ ]*) (?<size>[^ ]*)(?:\s"(?<referer>[^\"]*)") "(?<agent>[^\"]*)" (?<urt>[^ ]*)$/

例えば、このログ行を見てみましょう:

2022-06-07T14:16:57.754883042Z stdout F 100.96.2.5 - - [07/Jun/2022:14:16:57 +0000] "GET /ok/500/5000000 HTTP/1.1" 200 5005436 "-" "python-requests/2.22.0" 0.091 

パーサーで、そのログ行を以下の部分に分解してみました:

  • timestamp: 2022-06-07T14:16:57.754883042Z
  • stream: stdout
  • remote: 100.96.2.5
  • host: -
  • user: -
  • time: 07/Jun/2022:14:16:57 +0000
  • method: GET
  • path: /ok/500/5000000
  • status_code: 200
  • size: 5005436
  • referer: -
  • agent: python-requests/2.22.0
  • urt: 0.091

これで、Fluentd が access.log を読むように設定されたので、パーサーから得られたこれらの変数を使用して、いくつかのメトリクスを作成することができます。

nginx 送信バイト数

 size 変数を使って  nginx_size_bytes_total メトリクスを作成することができます: nginx の総送信バイト数を表すカウンターです。

      <metric>
        name nginx_size_bytes_total
        type counter
        desc nginx bytes sent
        key size
      </metric>

エラーレート

このシンプルなメトリクスを作成してみましょう:

<metric>
        name nginx_request_status_code_total
        type counter
        desc nginx request status code
        <labels>
          method ${method}
          path ${path}
          status_code ${status_code}
        </labels>
</metric>

このメトリクスは、すべてのログ行をカウンターにしただけのものです。では、なぜそれが有用なのでしょうか?まあ、他の変数をラベルとして使うことができるので、すべての情報を分解するのに便利でしょう。このメトリクスを使って、全体のエラー率パーセンテージを取得してみましょう:

sum(rate(nginx_request_status_code_total{status_code=~"[4|5].."}[1h])) / sum(rate(nginx_request_status_code_total[1h])) * 100

また、この情報を method別に集計して得ることもできます:

sum by (method) (rate(nginx_request_status_code_total{status_code=~"[4|5].."}[1h])) / sum by (method) (rate(nginx_request_status_code_total[1h]))

あるいは、 path別でも:

sum by (path) (rate(nginx_request_status_code_total{status_code=~"[4|5].."}[1h])) / sum by (path) (rate(nginx_request_status_code_total[1h]))

レイテンシー

成功したリクエストのレイテンシーを監視できたら素晴らしいと思いませんか?そうです、あなたの誕生日かもしれません! なぜなら、それができるからです。 $upstream_response_time 変数を追加するように指示したのを覚えていますか?

この変数はアップストリームサーバーからレスポンスを受け取るのに費やされた時間を秒単位で保存します。Fluentdでヒストグラムのメトリクスを作成するには、このようにします:

<metric>
        name nginx_upstream_time_seconds_hist
        type histogram
        desc Histogram of the total time spent on receiving the response from the upstream server.
        key urt
        <labels>
          method ${method}
          path ${path}
          status_code ${status_code}
        </labels>
</metric>

さて、魔法のように、この PromQL クエリーを試すと、成功したすべてのリクエストのレイテンシーを、リクエストのパスによって集約して p95 で取得することができます。

histogram_quantile(0.95, sum(rate(nginx_upstream_time_seconds_hist_bucket{status_code !~ "[4|5].."}[1h])) by (le, path))

まとめると

今回は、Kubernetes上のnginxをPrometheusで監視する方法と、Fluentdを使ってnginxのaccess.logを読み、さらにメトリクスを作成する方法について学びました。また、Prometheusでnginxを監視しトラブルシュートするための興味深いメトリクスを学びました。