Yakumoのモニタリングとコンテナ時代のDataDog活用例

こんにちは、Yakumoチームの@ueokandeです。 秋といえばモニタリングですよね。 本日はYakumoプロジェクトにおけるモニタリングの取り組みについて紹介します。

YakumoはUS市場にKintoneを展開することをゴールとしたプロジェクトで、その一環として国内のデータセンターで提供しているKintoneをAmazon Web Service (AWS)に移行しています。 つい先日、Yakumoで開発・運用してるKintoneがリリースされました。 本記事では、Yakumoにおけるモニタリングと、YakumoチームでのDataDogの活用例について紹介します。

再入門・監視

監視とは、メトリクス、ログ、アラート、オンコール、障害対応などたくさんのことを扱います。 その中でもメトリクスのモニタリングは、サービスの健康状態を知るための大事な手がかりです。 Webサービスにおけるメトリクスは、障害対応やパフォーマンスチューニングなど様々なことに利用されます。

オライリー・ジャパン出版の『入門 監視』によると、“ビジネスを監視せよ” と書いてありますが、同時にシステムのメトリクスも役に立つとあります。 実際これまでのサイボウズの運用経験上、OSやミドルウェアなどのメトリクスはとても役に立っています。 たとえば一時的なサービス障害が発生したとしても、それを単なるシステム負荷として終わらせません。 障害発生当時のメトリクスやアクセスログを元に、なぜ発生したか原因究明までします。 そのための状況証拠として、アプリケーションやOSなどのメトリクスは欠かせません。

リリースしたばかりのYakumoは、パフォーマンスの改善よりも安定運用に力を入れてるフェーズです。 そのためYakumoでのモニタリングも、障害発生時に原因の切り分けや障害対応を迅速に行えることを第1目標としました。 この記事でも、OSやアプリケーションのメトリクスをモニタリングすることにフォーカスを絞って説明します。

Yakumoのモニタリングシステム

モニタリングシステムの方針

AWSにもCloudWatchというメトリクスを保存する仕組みはあります。 AWSのマネージドサービスは、標準でCloudWatchにメトリクスが蓄積します。 それとは別に、Yakumoが開発・運用しているサービスのモニタリングには、DataDogを利用します。

DataDogはOSやアプリケーションのメトリクスの収集と可視化の用途に利用しています。 逆にDataDogのアラート機能などはほとんど使用せず、ユーザー操作に影響する指標は別の仕組みで監視しています。

Kintone本体やKintoneの認証サービスなどはKubernetes (Amazon Elastic Kubernetes Service: EKS) にデプロイしています。 DataDogが収集する対象は、Kubernetes本体とNode、そしてKubernetes上にデプロイされているYakumoのサービスです。

SQSやKinesis Data Streamなどのマネージドサービスについては、引き続きCloudWatchメトリクスを利用しています。 これらのサービスもKintoneの運用を支えていますが、お客様への影響が少なく緊急度が低いと判断しました。 そのためKintoneのメトリクスと並べて比較しないということで、DataDogの対象にしませんでした。

なぜDataDogを選んだか

Yakumoでモニタリングシステムを導入するとき、いつくかのサービスを選定しました。 その中でDataDogを採用した理由は主に以下のとおりです。

  • CloudWatchと比較して操作性が高い
  • Kubernetes/Dockerとの親和性が高い
  • 社内で運用実績や知見が溜まってる

特に3つ目の理由が大きかったです。 国内データセンターで提供してるKintoneのモニタリングにもDataDogを採用しています。 そのためDataDogの運用の知見やJavaアプリケーションのモニタリングの知見が社内に十分溜まっていたため、YakumoでもDataDogを採用しました。

DataDogのデプロイとモニタリングの設定

DataDogの構成

DataDogをKubernetes上で利用するには、以下の2つのアプリケーションをデプロイします (詳しいデプロイ方法やKubernetesマニフェストの記述例は公式ドキュメントにあります)。

  • DataDog Agent ... OSやアプリケーションからメトリクスを収集するプロセス。Kubernetes上ではDaemonSetリソースでデプロイできる。
  • DataDog Cluster Agent ... クラスタ単位で取得するメトリクス(Kubernetes APIサーバーやElasticsearchクラスタなど)を、どのDataDog Agentがモニタリングするかを選出するプロセス。Kubernetes上ではDeploymentリソースでデプロイできる。

上記の2つのアプリケーションをデプロイすると、特に設定が無くとも、OSとDockerのメトリクス、そしてKubernetes APIサーバーのメトリクスが収集されます。 次はアプリケーションのメトリクスのモニタリングです。

Autodiscovery

VMベースのアプリケーションをDatadog Agentでサービスをモニタリングしていたときは、モニタリングの対象を設定ファイルに記述していました。 一方Kubernetesなどのコンテナベースのアプリケーションでは、コンテナがどのノードに配置されるかわかりませんし、IPアドレスも動的に変わります。 これを解決するためのKubernetes上のプラクティスの1つとして、Side-carパターンがありますが、同じプロセスが何個もデプロイされて、効率的ではありません。 DataDogはv6からAutodiscoveryという仕組みが導入されました。

上に記述したとおり、Kubernetes上であってもDataDog Agentのデプロイは各Nodeに1プロセスでOKです。 Autodiscoveryを使うと、DataDog Agentはモニタリングの対象のPodをKubernetes APIサーバーに問い合わせます。 モニタリングの項目を増やすには、DataDog Agentではなく、デプロイするPodの定義に記述できます。 そのためDataDogの設定を複数人が管理せず、アプリケーション責任者がモニタリングの設定を管理できます。

例えば以下のYAMLは、NGINXのステータスAPI (ngx_http_stub_status_module) をモニタリングをする例です。 Podのannotationsフィールドに、モニタリングする項目、初期設定、モニタリングの対象の3つを記述します。 この設定は、DataDogのNGINX integration を利用します。

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      annotations:
        ad.datadoghq.com/nginx.check_names: '["nginx"]'
        ad.datadoghq.com/nginx.init_configs: '[{}]'
        ad.datadoghq.com/nginx.instances: '[{"nginx_status_url": "http://%%host%%:10081/nginx_status"}]'
    spec:
      containers:
        - name: nginx
          image: nginx

Spring Bootアプリケーションのモニタリング

YakumoではいくつかのサービスをKotlinとSpring Bootで記述しています。 Spring BootにはActuatorという、モニタリングやアプリケーションを管理する仕組みがあります。 Actuatorを導入すると、JVMのヒープ領域の使用量や、Jettyのメトリクスなどを取得できます。 これらをDataDogに公開することで、Spring Bootアプリケーションをモニタリングできます。

メトリクスの公開形式は自由に選択できますが、YakumoではOpenMetrics (Prometheus互換の形式) で公開しています。 Spring BootでOpenMetrics形式でメトリクスを公開するには、以下のパッケージを追加します。

// build.gradle
dependencies {
  implementation('org.springframework.boot:spring-boot-starter-actuator')
  implementation('io.micrometer:micrometer-registry-prometheus')
}

そしてアプリケーションプロパティに、公開するメトリクスやURLを設定すると、外部から観測できるようになります。

# application.properties
management.endpoints.web.base-path=/
management.server.port=19100
management.endpoints.web.exposure.include=prometheus

これをモニタリングの対象に含めるために、PodのアノテーションにOpenMetrics integrationの設定を追記します。

# my-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      # ... snip ...
      annotations:
        ad.datadoghq.com/my-app.check_names: '["openmetrics"]'
        ad.datadoghq.com/my-app.instances: |
          [
            {
              "prometheus_url": "http://%%host%%:19100/prometheus",
              "namespace": "openmetrics",
              "metrics": [
                "jvm_*",
                "jetty_*",
                "http_server_requests_*"
              ]
            }
          ]
        ad.datadoghq.com/my-app.init_configs: '[{}]'

マネージドサービスのモニタリング

先にAmazonのマネージドサービスのモニタリングはCloudWatchを利用してると説明しました。 しかしElasticsearchやRDB (Amazon RDS) などのKintoneが直接利用するいくつかのサービスは、障害対応時に参照することが多いです。 そのためKintone本体のメトリクスと並べて可視化できるよう、DataDogのモニタリングの対象としています。

DataDogから外部のクラスタのメトリクスを取得するには、KubernetesのServiceリソースを利用します。 こちらもPodと同じようにannotationsにAutodiscoveryの設定を定義すると、DataDogはServiceリソースをモニタリングの対象に含めます。 以下はElasticsearchをモニタリングするためのServiceリソースのマニフェスト例です。

# kintone-elasticsearch-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: kintone-elasticsearch
  annotations:
    ad.datadoghq.com/service.check_names: '["elastic"]'
    ad.datadoghq.com/service.init_configs: '[{}]'
    # AWS Elasticsearch Serviceでは/_cluster/pending_tasks を提供しないので
    # "pending_task_stats": falseに設定する
    ad.datadoghq.com/service.instances: |
      [
        {
          "url": "https://kintone-elasticsearch-service.us-west-2.es.amazonaws.com",
          "cluster_stats": true,
          "pshard_stats": true,
          "index_stats": true,
          "pending_task_stats": false
        }
      ]
spec:
  type: ExternalName
  externalName: "kintone-elasticsearch-service.us-west-2.es.amazonaws.com"

カスタムメトリクスの取得

Yakumoではプッシュ通知の配信にGaurunを利用しています。 Gaurunはアプリケーションの状態をHTTPエンドポイントとして公開してますが、DataDog標準の機能ではメトリクスを収集できません。

DataDogは標準の機能で取得できないメトリクスは、DataDogのカスタムAgentチェックを利用します。 カスタムAgentチェックは、Pythonスクリプトを記述して、独自のメトリクスを収集できます。

以下はGaurunをモニタリングする設定例です。 カスタムAgentチェックで利用するスクリプトはConfigMapで記述して、DataDog AgentのPodにファイルとしてマウントします。

# datadog-agent-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: datadog-agent
  namespace: kube-system
spec:
  template:
    spec:
      containers:
        - name: datadog-agent
          image: datadog/agent:6.13.0
          volumeMounts:
            # entrypointのスクリプトで/checks.dに置くと/etc/datadog-agentにコピーされる
            - name: datadog-checksd-config
              mountPath: /checks.d/
              readOnly: true
          # ... snip ...
      volumes:
        - name: datadog-checksd-config
          configMap:
            name: datadog-checksd-configmap
# datadog-agent-configmap.yaml
kind: ConfigMap
metadata:
  name: datadog-checksd-configmap
  namespace: kube-system
data:
  gaurun.py: |
    from datadog_checks.checks import AgentCheck

    import json
    import urllib2

    __version__ = "1.0.0"

    # Gaurunが公開してるメトリクスをHTTPから取得する
    class GaurunCheck(AgentCheck):
        def check(self, instance):
            host = instance['host']
            port = instance['port']
            tags = instance['tags']

            url = "http://%s:%s/stat/app" % (host, port)

            req = urllib2.Request(url)
            j = json.load(urllib2.urlopen(req))
            self.monotonic_count('gaurun.push_success.ios', j["ios"]["push_success"], tags)
            self.monotonic_count('gaurun.push_error.ios', j["ios"]["push_error"], tags)
            self.monotonic_count('gaurun.push_success.android', j["android"]["push_success"], tags)
            self.monotonic_count('gaurun.push_error.android', j["android"]["push_error"], tags)

まとめ

この記事では、Yakumoにおけるモニタリングと、Kubernetes上でのDataDogの設定例をいくつか紹介しました。 DataDogは社内で実績があるとはいえ、Kubernetes上のデプロイは初めての試みでした。

『入門 監視』にもあるように、モニタリングシステムは作れば終わりではなく、継続的に育てていくものです。 障害対応で必要なメトリクスに気付いたり、可観測性が低いサービスに気づくことができます。 Yakumoは9月にリリースされて、モニタリングシステムはまさに成長期なのです。 今後も継続的にモニタリングシステムを育てて、より迅速に障害対応やパフォーマンスチューニングができる仕組みを作っていく予定です。