Elastic StackをPrometheusとFluentdと組み合わせてKubernetesを監視する

Kubernetesは、コンテナ化したアプリケーションのデプロイ、スケーリング、および管理を行うための、オープンソースのコンテナオーケストレーションシステムですが、昨今はこの分野におけるデファクトスタンダードの地位を確立したかに見えます。Kubernetesによってもたらされた、モノリシックなアプリケーションからマイクロサービスへのシフトは、動的な環境を当たり前のものとして、より素早い展開を可能にした反面、アプリケーションとそれを支えるインフラストラクチャーの監視をより複雑にしています。幸いなことに、Elasticのテクノロジーは、古くはELK Stackと呼ばれた時代から、最近はElastic Observabilityとして、ITインフラやアプリケーションの監視ソリューションとして、幅広く利用されて来ました。Elastic Observablityを利用することで、Kubernetes環境のオブザーバビリティを確立することも可能ですが、一方で、既に利用しているオープンソースベースの監視ツールを使いたいというユーザーの声も多数あります。

そこで本ブログでは、PrometheusとFluentdをElastic Stackと組み合わせて使用して、Kubernetesを監視する方法についてご紹介します。

PrometheusとFluentdを使う

Prometheusは、皆さんご存知の通り、メトリクスのツールとして、特に昨今のKubernetes環境のメトリックモニタリングにおいては、支配的な地位を築いており、非常に人気の高いオープンソースプロジェクトです。Prometheusはプルモデルを採用しており、エンドポイントからメトリックをスクレイプして、Prometheusサーバに投入します。ただし、ストレージに関して言えば、Prometheusのローカル ストレージは単一ノードによって制限されているため、Prometheusにはスケーラビリティと耐久性においていくつかの制限があります。この制約を取り除くためには、Prometheus自体のクラスター化されたストレージを構成するか、リモートストレージシステムとの統合を可能にするPrometheusインターフェースを使用する必要があります。 また、Fluentdは、古くから知られたオープンソースのログ収集ツールで、こちらも根強い人気を誇っています。Flutendは、様々なログソースと収集したログを保管するストレージ層の間に存在するもので、Elastic Stackで例えるとLogstashに相当するものです。そのため、Fluentdにも長期保管ストレージシステムが必要です。ここに、Elasticsearchをログ、メトリックの長期保管ストレージとして、Kibanaを可視化ツールとして組み合わせる理由があります。実際に、Elasticsearch、Fluentd、そしてKibanaを使ったログ監視のソリューションは、EFK Stackとも呼ばれています。

モニタリングアーキテクチャ

本ブログでは、Cloud-Voting-Appというシンプルな、マルチコンテナアプリケーションをKubernetesクラスター上にデプロイし、そのアプリケーションを含めたKubernetes環境を監視します。Prometheusを使ってメトリックを、Fluentdを使ってログを収集し、Elasticsearchに投入し、Kibanaを使って監視する手順を説明していきます。大まかなアーキテクチャは以下の図のようになります。また、ここでご紹介するコードは、GitHubレポに掲載していますので、完全な手順はそちらを参照してください。

CNCF monitoring

それでは、各手順を見ていきましょう。

FluentdをDaemonSetとしてデプロイする

FluentdのインスタンスはKubernetesノード1つにつき1つデプロイします。Fluentdの公式GitHubレポには、Elasticsearchへログを投入するための構成サンプルが提供されていますが、今回の手順ではKubernetes環境下で各コンテナのログを適切にパースするためのFluentdの設定をConfigMapとして追加しています。 DaemonSetのManifestは、cncf-projects/fluentd-daemonset-elasticsearch-rbac.yamlに定義されていますが、基本的には、ElasticsearchのクラスターURLとクレデンシャルを追加するだけでOKです。Flutendの設定方法についてさらに知りたい場合は、Fluentdの公式ドキュメントを参照ください。また、Kirill Goltsman によるブログ「Cluster-level Logging in Kubernetes with Fluentd」も非常に参考になります。

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  ...
spec:
    ...
    spec:
      containers:
      - name: fluentd
        image: quay.io/fluent/fluentd-kubernetes-daemonset
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch-logging"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name:  FLUENT_ELASTICSEARCH_SSL_VERIFY
            value: "true"
          - name:  FLUENT_ELASTICSEARCH_SSL_VERSION
            value: "TLSv1_2"
          - name: FLUENT_ELASTICSEARCH_USER
            value: "elastic"
          - name: FLUENT_ELASTICSEARCH_PASSWORD
            value: "changeme"
        ...

fluentdはデフォルトで、Elasticsearchにlogstash-YYYY.mm.ddというインデックスを作成します。TextフィールドとKeywordフィールドが無駄に混在することを避けるために、事前にIndex Templateを作成しておきます。

PUT _template/logstash
{
  "index_patterns": [
    "logstash-*"
  ],
  "mappings": {
    "dynamic_templates": [
      {
        "message_field": {
          "path_match": "message",
          "mapping": {
            "norms": false,
            "type": "text"
          },
          "match_mapping_type": "string"
        }
      },
      {
        "another_message_field": {
          "path_match": "MESSAGE",
          "mapping": {
            "norms": false,
            "type": "text"
          },
          "match_mapping_type": "string"
        }
      },
      {
        "strings_as_keyword": {
          "mapping": {
            "ignore_above": 1024,
            "type": "keyword"
          },
          "match_mapping_type": "string"
        }
      }
    ],
    "properties": {
      "@timestamp": {
        "type": "date"
      }
    }
  }
}

Prometheusをデプロイする

Prometheusは、リモートストレージシステムと統合することで、標準化された形式でリモートURLに取り込むサンプルを書き込むことが可能です。詳細については、Prometheusの公式ドキュメントを参照して見てください。ここでは、PrometheusのリモートストレージシステムとしてElasticsearchを使用するために、公式の Metricbeatモジュール、より具体的にはremote_writeメトリックセットを使います。

MetricbeatをDeploymentとしてデプロイする

Metricbeatのインスタンスは、KubernetesクラスターにDeploymentとして1つデプロイします。DeploymentのManifestは、cncf-projects/metricbeat-kubernetes-deployment.yamlに定義されていますが、基本的にはElasticsearchのクラスターURLとクレデンシャル情報を追加するだけでOKです。Manifestファイルにおける、Metricbeatのremote_writeメトリックセットの設定は以下のようになります。これで、MetricbeatはPrometheusからのhttpを介したremote writeリクエストを9201ポートで待ち受け、Prometheusからのメトリックをmetricbeat-* indicesに書き込みます。Metricbeatは、言わば、Prometheusのリモートストレージアダプタとして機能します。

# Metrics sent by a Prometheus server using remote_write option
- module: prometheus
  metricsets: ["remote_write"]
  host: "0.0.0.0"
  port: "9201"

Helm Chartを使ってPrometheusをデプロイする

PrometheusのKubernetesクラスターへのデプロイ方法はいくつかありますが、ここではHelm Chartを使ってPrometheusをデプロイします。Promethuesコミュニティが提供するHelm Chartについては、こちらのGit Hubレポを参照ください。Prometheusのコンポーネントについては、ChartのカスタマイズYAMLファイルを使ってカスタマイズが可能ですが、cncf-projects/prometheus_custom.yamlでは以下のようにカスタマイズをしています。ここでは、デフォルトのPrometheus ServiceをClusterIPからLoadBalancerに変更し、さらに前述のMetricbeatの9201ポートへメトリックを書き込むために、remote_writeのURLをMetricbeatのServiceに設定しています。AlertmanagerとPushgatewayについては、ここでは無効にしています。

alertmanager:
  ## If false, alertmanager will not be installed
  ##
  enabled: false

server:
  ## Prometheus server container name
  ##
  service:
    type: LoadBalancer
  ## https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write
  ##
  remoteWrite:
    - url: "http://metricbeat-svc.kube-system.svc.cluster.local:9201/write"

pushgateway:
  ## If false, pushgateway will not be installed
  ##
  enabled: false

アプリケーションのデプロイ

今回の対象となるCloud Votingアプリケーションは、ユーザーインターフェースをPython/Flask、データ層にRedisを使った、マルチコンテナの非常にシンプルなアプリケーションです。アプリケーションは、Prometheusのカスタムメトリックを公開するようにPrometheus Python Clientでインストゥルメントされています。カスタムメトリックは、cloud_vote_totalで、POSTリクエストの都度、voteに入ってくる変数をlabelとして、インクリメントしていくだけのシンプルなものです。

# Define prometheus counter
VOTES = Counter('cloud_votes_total', 'Cloud Votes Requested.', labelnames=['vote'])

@app.route('/', methods=['GET', 'POST'])
def index():

    if request.method == 'GET':

    ...

    elif request.method == 'POST':

        # Increment Counter
        VOTES.labels(request.form['vote']).inc()
        ...

Kibanaから見てみよう

さて、これで必要なコンポーネントが全てデプロイできました。早速、Cloud Votingアプリで何度かVoteしてから、Kibanaにアクセスしてみましょう。

ログ

Fluentdから投入されたログは、logstash-*というインデックスに書き込まれます。Kibana Discoverで確認すると、各コンテナのログが、kubernetes.*というメタデータが付与されてインデックスされていることがわかります。

Logstash Discover

さらに、KibanaのLogsアプリを使うことも可能です。このアプリを使って、Elastic Stackに収集した全てのログの検索、絞り込み、tailを実行できます。これで、Fluentdで収集した、Kubernetes上のコンテナログは、Logsアプリという単一のツールで扱うことができるようになりました。

Logstash logs

メトリック

Prometheusで収集したメトリックは、Metricbeatを介して、metricbeat-*というインデックスに書き込まれます。Prometheusメトリックは、prometheusという接頭語で修飾されたフィールドになります。通常のmetricbeat-*インデックスなので、このようにKibana Lensを使って、prometheusで始まるフィールドを簡単に可視化することができます。次の例は、container_memory_usage_byteslabels.pod毎に可視化したものです。

Metrics CPU

アプリケーションのカスタムメトリックのcloud_vote_totalもこの通りです。labels.voteはPOST時の変数になっています。

Metrics cloud vote

MetricbeatのPrometheusモジュールにより、Prometheus固有の統計情報についての定義済みのダッシュボードも、何も設定せずに使うことができます。

Metricbeat Prometheus overview

アラーティング

Prometheusのデプロイでは、Alertmanagerは無効にしていました。Elastic Stackにログ、メトリックを収集した場合、Alertmanagerの代わりにKibana Alertを使ってアラーティングを実装することができます。Prometheusからのメトリックだけではなく、ログでも他のインデックスでも、Elasticsearchに格納されているデータは全て活用することができますし、機械学習を使ったより高度なアラーティングも可能になります。

Kibana alerting

まとめ

FluentdとPrometheusを使って、Elastic Stackにログとメトリックを投入し、Kubernetesを監視する方法をご紹介しました。Elastic Cloudの無料トライアルに登録したり、自前の環境にElastic Stackをダウンロードすれば、今日からお使いのKuberntes環境の監視を始めることができます。既にこうしたオープンソースツールをお使いの場合でも、Elastic Stackを活用して、包括的なオブザーバビリティを確立することができることがご理解いただけたかと思います。お困りのことや、ご質問がおありの場合はディスカッションフォーラムをご活用ください。活発なコミュニティが待っています。

本ブログに続く将来の投稿では、ログとメトリックの監視にElasticを活用した別の方法に引き続きご注目ください。