Datadog の AutoDiscovery 機能を用いて自動的に kubernetes pod の監視をする

# Motivation
Prometheus には **Service Discovery** という機能があり、監視対象のノードを自動的に補足し対象に追加する事が可能です。

(https://prometheus.io/docs/prometheus/latest/configuration/configuration/ の **_sd_config がそれらの設定になります。)

最近は Kubernetes などの Docker orchestration tool の普及により監視対象が動的に変化する環境が多く、Auto Scaling / Self Healing に自動的に追随して監視を行ってくれるツールの必須度が高くなっています。

個人的には Datadog が好きなので、同様な機能が Datadog にもあると良いな、と思っていたのですが、 Datadog でも AutoDiscovery という機能が提供されておりこちらを用いることで似たような事が実現可能です。

https://docs.datadoghq.com/agent/autodiscovery/


ここでは kubernetes 環境で動作させることを前提に、Docker の AutoDiscovery の設定方法について書きます。

# 構成
kubernetes についての説明は端折りますが、kubernetes では daemonset という機能を用いることで一つのホストインスタンス上に特定の docker image を一つだけ配置することができます。

datadog のような、一つのホストインスタンスに一つだけあれば良いような管理系の docker image については、daemonset として構成するのが一般的です。
（他に、fluentd なども同様な構成を取ることが多いです）

今回の例では

- daemonset: datadog
- deployment: nginx

という構成で kubernetes 上に配置し、datadog から各 nginx の監視を行います。

# 設定
## namespace
とくに必然性はないのですが、ここでは **sada4j** とします。


```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: sada4j
```

## daemonset
datadog の設定を行います。

あらかじめ docker に最適化された **docker-dd-agent** というイメージがあるので、そちらをそのまま使います。

env にて、API Key や、Kubernetes 上で動作させる際に必要となる設定を入れていきます。

```yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: dd-agent
  namespace: sada4j
spec:
  template:
    metadata:
      labels:
        app: dd-agent
      name: dd-agent
    spec:
      containers:
      - image: datadog/docker-dd-agent:latest
        imagePullPolicy: Always
        name: dd-agent
        ports:
          - containerPort: 8125
            name: dogstatsdport
            protocol: UDP
        env:
          - name: API_KEY
            value: "<your api key>"
          - name: KUBERNETES
            value: "yes"
          - name: SD_BACKEND
            value: "docker"
          - name: KUBERNETES_KUBELET_HOST
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
          - name: SD_CONFIG_BACKEND
            value: "etcd"
          - name: SD_BACKEND_HOST
            value: "127.0.0.1"
          - name: SD_BACKEND_PORT
            value: "4001"
        volumeMounts:
          - name: dockersocket
            mountPath: /var/run/docker.sock
          - name: procdir
            mountPath: /host/proc
            readOnly: true
          - name: cgroups
            mountPath: /host/sys/fs/cgroup
            readOnly: true
      volumes:
        - hostPath:
            path: /var/run/docker.sock
          name: dockersocket
        - hostPath:
            path: /proc
          name: procdir
        - hostPath:
            path: /sys/fs/cgroup
          name: cgroups
```

## deployment
ここでは nginx を起動し、/nginx_status を有効にすることのみを目的としています。

それ以外の各種設定値はサービス運用を前提としたものではないいい加減なものです。

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: sada4j-nginx
  namespace: sada4j
data:
  nginx.conf: |-
    user  nginx;
    worker_processes  1;
 
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
 
    events {
        worker_connections  1024;
    }
 
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
 
        sendfile        on;
        keepalive_timeout  65;
 
        server {
            listen 80;
 
            location /nginx_status {
                stub_status on;
                access_log   off;
            }
        }
    }
 
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sada4j-nginx
  namespace: sada4j
spec:
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        cluster_name: dev-sada4j-nginx
        env: dev
        project: sada4j
        tier: app
      annotations:
        service-discovery.datadoghq.com/frontend.check_names: '["nginx"]'
        service-discovery.datadoghq.com/frontend.init_configs: '[{}]'
        service-discovery.datadoghq.com/frontend.instances: '[{"nginx_status_url": "http://%%host%%:%%port%%/nginx_status"}]'
    spec:
      containers:
      - name: frontend
        image: nginx:alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
      volumes:
        - name: nginx-config
          configMap:
            name: sada4j-with-nginx
---
apiVersion: v1
kind: Service
metadata:
  name: sada4j-nginx
  namespace: sada4j
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    cluster_name: dev-sada4j-nginx
```

設定ファイルが長くなっていますが、ここでの要点は以下の部分です。

```yaml

annotations:
  service-discovery.datadoghq.com/frontend.check_names: '["nginx"]'
  service-discovery.datadoghq.com/frontend.init_configs: '[{}]'
  service-discovery.datadoghq.com/frontend.instances: '[{"nginx_status_url": "http://%%host%%:%%port%%/nginx_status"}]'
```

kubernetes の annotations 情報として、当該 pod の監視設定を記述しています。

こうすることで、datadog の agent が監視対象の container image と、何を監視するかについて知ることができる、という仕組みになっています。



各設定値については、以下のように設定する必要があります。

- **service-discovery.datadoghq.com/<container_name>.check_names**
  - 監視対象のスクリプトの名前です。datadog-dd-agent に同梱されているか、 datadog の docker image 内の /etc/dd-agent/checks.d/ 配下に設置されている checks スクリプトで同名のものがあれば、ここで指定された名前の監視スクリプトを実行します。
- **service-discovery.datadoghq.com/<container_name>.init_configs**
  - 監視処理を行うスクリプトの init_config に渡すパラメータを設定します。何を渡せるかは監視スクリプトの作り次第になります。
  - https://docs.datadoghq.com/ja/guides/agent_checks/#init-config
  - nginx の監視スクリプトには特に init_config に渡せる値はないので空の連想配列を渡しています。
- **service-discovery.datadoghq.com/<container_name>.instances**
  - 監視処理を行うスクリプトの instances に渡すパラメータを設定します。何を渡せるかは監視スクリプトの作り次第になります。
  - https://docs.datadoghq.com/ja/guides/agent_checks/#instances
  - nginx の監視スクリプトでは、監視対象の nginx のURL を指定できる ( nginx_status_url ) ので、上記例ではそちらを設定しています。


設定をする際に、以下がハマりやすいので注意が必要です。

- **container_name** の部分は、 containers に記述している name の値と一致している必要があります。
  - 上記例だと frontend
- 一つの annotation 記述で、複数の監視スクリプトを指定することが出来ます。
  - ex.  **service-discovery.datadoghq.com/frontend.check_names: '["nginx", "jmx"]'**
  - etcd にこちらの情報が書き込まれるのですが、左辺の値が key 文字列として用いられるので、複数の監視スクリプトを設定する場合は上記のようにしないとうまく動きません。

上記のような設定をすることで、container image を apply  したタイミング、もしくは container image の数を増やしたタイミングで、自動的に追加された container が監視対象になります。

# 動作確認
ただしく AutoDiscovery の設定を反映して監視スクリプトが動いているかどうか、結構判別が面倒くさいのですが、以下の２つの視点で確認をします。

## 1.　Datadog に値が送られているかどうか確認する
意図した値が Datadog に送られているか、metrics explorer などで確認をします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/44364/10743824-40b4-3d1c-7161-e40714b5ca12.png)


## 2.　datadog-agent info コマンドの結果を確認する
daemonset として起動している dd-agent の docker container の中に入り、datadog-agent info というコマンドを叩くと、現在 dd-agent が収集を行っているメトリクスの一覧が確認できます。

上記の設定例だと、ここで nginx のスクリプトが含まれていれば、OK です。

```shell
$ /etc/init.d/datadog-agent info                                                                          
====================
Collector (v 5.21.1)
====================
 
(snip.)
   
  Checks
  ======
   
    nginx (5.21.1)
    --------------
      - instance #0 [OK]
      - Collected 7 metrics, 0 events & 1 service check
   
    network (5.21.1)
    ----------------
      - instance #0 [OK]
      - Collected 20 metrics, 0 events & 0 service checks
   
    kubernetes (5.21.1)
    -------------------
      - instance #0 [OK]
      - Collected 108 metrics, 0 events & 3 service checks
   
(snip.)
```

Checks に nginx が含まれているので、正しく監視対象となっている、と判断できます。

エラーになっている際は、 **/var/log/dd-agent/collector.log** などを確認するとその状況が把握できるかもしれません。

# まとめにかえて
datadog の autodiscovery 周りは極めてドキュメントもなく動きを把握するのが結構たいへんだったので、個人的な備忘録的にまとめました。