Datadog Log Management と Kubernetes を連携する

Datadog Log Management 便利ですね。
https://docs.datadoghq.com/logs/

私も愛用していますが、日本語のブログ記事等では Hello World 的な記述が多い気がするので、比較的実践的な Kubernetes との連携について、備忘録的に記述をしておきます。

# 目的
Kubernetes 上で動作している何かしらのアプリケーションのコンテナログを、Kubernetes 上で動作する dd-agent 経由で収集し、 Datadog Log Management で扱えるようにする。

# 手順
## Version 6 用の dd-agent docker image の準備
Kubernetes 環境下で Datadog を使用する際は、Datadog Agent の Docker Image を Daemonset として動作させます。

Version 6 からはそれ以前に比べて Agent の構成が大幅に変更になったこともあってか、docker image が別の名前で提供されるようになっています。

- <= Version 5
  - `datadog/docker-dd-agent`
  - https://hub.docker.com/r/datadog/docker-dd-agent/
- >= Version 6 
  - `datadog/agent`
  - https://hub.docker.com/r/datadog/agent/

Log Management 機能は、 Datadog の Agent が Version が 6 以上である必要があるため、Version 6 以上用の Docker Image を Daemonset として起動します。

## datadog 用の RBAC の設定
Kubernetes 環境で dd-agent を動作させる際は、以下の公式ドキュメントのように RBAC を用いて権限設定をすることが推奨されています。これにより必要以上に dd-agent にファイルアクセスなどの権限を付与させることなく動作させることができます。

https://docs.datadoghq.com/integrations/faq/using-rbac-permission-with-your-kubernetes-integration/

```
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: dd-agent
rules:
- apiGroups:
  - ""
  resources:
  - services
  - events
  - endpoints
  - pods
  - nodes
  - componentstatuses
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  resourceNames:
  - datadogtoken             # Kubernetes event collection state
  - datadog-leader-election  # Leader election token
  verbs:
  - get
  - update
- apiGroups:  # To create the leader election token
  - ""
  resources:
  - configmaps
  verbs:
  - create
- nonResourceURLs:
  - "/version"
  - "/healthz"
  verbs:
  - get
- apiGroups:  # Kubelet connectivity
  - ""
  resources:
  - nodes/metrics
  - nodes/spec
  - nodes/proxy
  verbs:
  - get
 
---
kind: ServiceAccount
apiVersion: v1
metadata:
  name: dd-agent
  namespace: dd-agent
 
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dd-agent
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: dd-agent
subjects:
- kind: ServiceAccount
  name: dd-agent
  namespace: dd-agent
```

ここで設定した `ServiceAccount` の名前を、Daemonset の設定時も使用します。

## dd-agent の Daemonset 設定
以下のような YAML ファイルを用意し、Kubernetes に Apply することで、Datadog の Daemonset が動作します。

```
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: dd-agent
  namespace: dd-agent
spec:
  template:
    metadata:
      labels:
        app: dd-agent
      name: dd-agent
    spec:
      serviceAccountName: dd-agent
      containers:
      - image: datadog/agent:latest
        imagePullPolicy: Always
        name: dd-agent
        ports:
          - containerPort: 8125
            name: dogstatsdport
            protocol: UDP
          - containerPort: 8126
            name: traceport
            protocol: TCP
        env:
          - name: DD_API_KEY
            valueFrom:
              secretKeyRef:
                name: dd-agent-secret
                key: DATADOG_API_KEY
          - name: DD_LOGS_ENABLED
            value: "true"
          - name: DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL
            value: "true"
          - name: DD_TAGS
            value: "env:sada"
        volumeMounts:
          - name: dockersocket
            mountPath: /var/run/docker.sock
          - name: procdir
            mountPath: /host/proc
            readOnly: true
          - name: cgroups
            mountPath: /host/sys/fs/cgroup
            readOnly: true
          - name: pointerdir
            mountPath: /opt/datadog-agent/run
      volumes:
        - hostPath:
            path: /var/run/docker.sock
          name: dockersocket
        - hostPath:
            path: /proc
          name: procdir
        - hostPath:
            path: /sys/fs/cgroup
          name: cgroups
        - hostPath:
            path: /opt/datadog-agent/run
          name: pointerdir
```

いままでの Version 5 との設定の相違点は以下です。

### Log Management 系の環境変数の追加
- **DD_LOGS_ENABLED** : Log Management 向けのログ収集を有効にするかどうか。
- **DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL** : コンテナに出力されたすべてのログを収集するかどうか。

### Mount 先のディスクの追加
- **/opt/datadog-agent/run** をマウント先に追加。

### ServiceAccountName の設定
※こちらは厳密には Datadog Agent Version 5 / 6 による差異ではないです。

今回は RBAC 経由で権限を付与しているため、RBAC で設定をした ServiceAccount の名前を **ServiceAccoutName** に設定します。



## pod の設定
Datadog としての設定は上記までで完了ですが、実際は Pod (Deployment) 上で動作させている Web Application などのログを Datadog Agent 経由で収集することが目的になります。

ということで Pod の設定になりますが、基本的には以下の戦略に則るのが管理上楽ではないかなと思います。

- Datadog Log Management に収集するログは、コンテナログとする。
- コンテナログは、JSON フォーマットで出力する。

ここでは、Nginx のアクセスログを出力する例を記載します。

### Nginx のアクセスログを JSON 化する
Datadog Log Management に取り込んだログが JSON フォーマットの場合、Datadog 側で自動的にフィールドをパースしてくれます。

そのため、JSON フォーマットで出力をしたほうが Live Tail Dashboard や後続の Pipeline などでの処理が楽になりますので、基本的には JSON でログを扱うのが望ましいです。


Nginx の出力ログを JSON 化する方法については Qiita  上に参考になる記事が存在しますので詳細は割愛します。

https://qiita.com/progrhyme/items/c85d28eb18359f3f50d9

```
log_format json escape=json '{"time": "$time_iso8601",'
    '"host": "$remote_addr",'
    '"vhost": "$host",'
    '"user": "$remote_user",'
    '"status": "$status",'
    '"protocol": "$server_protocol",'
    '"method": "$request_method",'
    '"path": "$request_uri",'
    '"req": "$request",'
    '"size": "$body_bytes_sent",'
    '"reqtime": "$request_time",'
    '"apptime": "$upstream_response_time",'
    '"user_agent": "$http_user_agent",'
    '"forwarded_for": "$http_x_forwarded_for",'
    '"forwarded_by": "$http_x_forwarded_by",'
    '"forwarded_host": "$http_x_forwarded_host",'
    '"forwarded_proto": "$http_x_forwarded_proto",'
    '"referrer": "$http_referer"}';
```

### Pod の Annotation で Log Management 向けの設定を行う
https://docs.datadoghq.com/logs/log_collection/docker/

上記の公式ドキュメント内 **「Activate Log Collection」** に記載されているうちの、 **OPTION 2: AUTODISCOVERY** の方法となります。

Annotation 内に Datadog の AutoDiscovery 機能向けの設定を行い、収集対象となるコンテナログの設定を行います。

```
ad.datadoghq.com/(container name).logs: '[{"source": "nginx", "service": "webapp"}]'
```

以上で、設定は完了です。


動作イメージとして実稼働している画面を貼ろうと思ったのですが、ログの画面はセンシティブな内容が多くモザイクだらけになって意味がなさそうなので、割愛します。イメージが付き辛くすみません。