Datadogハンズオン！ログの収集・管理に疲弊している人集まれ〜

この記事は[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware) 6日目の記事です。
この記事では、携わっている案件で使用しているDatadogについて、
簡単に紹介させていただきます。

## はじめに
こんにちはー
社内でチームのDeveloperをしています、佐々木です。

入社1年目にシステムのメトリクス収集・可視化業務を担当していたこともあり、
ログ収集・管理と言えば、ElasticStackというイメージがありました。
そこから数年の時が経ち、今年の2月からアジャイル案件に従事していて
Datadogというクラウドサービスを知りました。

このDatadogというサービス、
今までの感覚がひっくり返るのほど簡単に
システムのログ収集・管理をしてくれます。

何なら、ログ収集だけではなく、
システム運用にかかる手間を解消してくれる様々な機能を提供しています。

本記事では、DatadogのAPMとLogsという機能を用いて、
KubernetesのPodから出力されているアプリケーションログを収集し、
簡単に検索できるようにするための設定方法や使用方法を紹介したいと思います。


## 本記事を読んで得られるもの

- Podに出力されているアプリケーションログを、Datadogに送信できるようになる
- APMとLogsという機能を使って、アプリケーションログを検索して、エラー解析に役立てられる


## Datadogとは

SaaSの形式で提供されるITシステムのモニタリングサービスです。
様々なプラットフォームで稼働するITシステムをモニタリングできます。

Datadog設立の経緯↓
>Datadogは、Wireless Generationで同僚として働いていたOlivier PomelとAlexisLê-Quôcによって2010年に設立された。WirelessCorporationがNewsCorpに買収された後、2人は、相反する目的で作業することが多い開発者とシステム管理者チームとの間の摩擦を軽減できる製品を提供しようと試みた。

>彼らは、Datadogをダッシュボード、アラート、およびメトリクスの視覚化を備えたクラウドインフラストラクチャ監視サービスとして構築した。クラウドの採用が増加するにつれて、Datadogは急速に成長し、Amazon Web Services（AWS）、Microsoft Azure、Google Cloud Platform、Red Hat OpenShift、OpenStackなどのサービスプロバイダーをカバーするために製品提供を拡大した。
[Wikipediaより](https://ja.wikipedia.org/wiki/Datadog)

開発者とシステム管理者チームとの間の摩擦を軽減できる製品
っていう部分が良いですよね。

ダッシュボード、アラート、およびメトリクスの視覚化を備えた
クラウドインフラストラクチャ監視サービスとしてスタートしたそうです。

AWSやGCP等のクラウドサービスが流行ると共に、
急速な成長を遂げています。


## 背景と問題
なぜDatadogがとても便利なのかを考えるためには、
システム運用における背景と問題をおさらいするのが早いです。

- システムの健全な運用には、大きな時間とコストが必要
- システムのメトリクスやアプリケーションログを、人間の手で集めて確認するのはとても大変で現実的ではない
- そこで、システムのメトリクスやアプリケーションログを1つの場所にまとめて管理し、必要になったときに検索できるようにしたいと考える
- とはいえ、このような仕組みを0から構築・運用するのには、結局のところ大きな時間とコストが必要


## 解決策
上記の問題に対する解決策として、
Datadogのようなクラウドサービスを用いることが考えられます。

OSSを用いたログ収集・管理システムを運用するとなると、
環境準備や構築作業等を自前でやらなければなりませんが、
Datadogのようなクラウドサービスだと、その辺の手間を大きく省けるのが強みですね。

Datadogの機能自体はたくさんあるのですが、
本記事では、アプリケーションログに焦点を当てて、
Kubernetes内のAPIが出力したログをDatadogに送信し、
Datadogでログを検索できる仕組みを実装してみます。
（今回は14日間の無料お試し期間を活用します）

![名称未設定.018.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/34b991ae-9f10-f088-51bd-da7066260cc4.jpeg)


## バージョン
- node: v14.18.1
- docker: v20.10.8
- minikube: v1.23.2
- helm: v3.7.1
- kubernetes
    - Client Version: v1.20.4
    - Server Version: v1.20.2
- Datadog Agent: v7



## 前提条件
- Dockerがインストール済であること
- minikubeがインストールされていること
- DockerHubに作業用のリポジトリが作成されていること

## ハンズオン
ここからはハンズオンパートです。

主に、以下の流れで進めていきます。

1. Datadogにログを送信するサンプルAPIアプリの作成
1. Datadogの登録
1. Datadogの設定
1. Datadogを使ってみる

### 事前準備
まずは下準備から進めていきます。

#### サンプルAPIアプリの作成
まずはログを出力するサンプルのAPIアプリを作成します。
今回はminikubeを使用するので、
Kubernetes内にbusyboxコンテナを立てて、
そのコンテナからAPIに対してリクエストを送ります。

![名称未設定.019.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/99b118fb-f38a-4235-8a97-94377c844d18.jpeg)

サンプルAPIはとてもシンプルです。
/success にリクエストを投げると、status 200を返します。
/error にリクエストを投げると、status 500を返します。

![名称未設定.020.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/949b5731-a9d2-9c4a-b676-a3c1042602e9.jpeg)


##### ディレクトリ構造
今回のハンズオンで使用する
全てのファイルに関するディレクトリ構造は以下のとおりです。
![スクリーンショット 2021-11-28 22.28.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/d9b1b49f-0344-ad6c-e5cf-b248397cdc6a.png)

##### ソースコードと実装
以下、ファイルを準備しましょう。

```package.json
{
  "name": "api",
  "version": "1.0.0",
  "description": "Node.js on Docker",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.16.1"
  }
}
```

```server.js
'use strict';

const express = require('express');
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';

// success
const app = express();
app.get("/success", (req, res) => {
  res.send("Datadog APM Test Success")
})

// error
app.get("/error", (req, res) => {
  res.status(500).send("Datadog APM Test Error")
})

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
```

package.jsonとserver.jsファイルを作成したら、
packageをインストールします。
その後、node.jsのサーバを立てて、
ローカル環境でAPIの動作を確認しましょう。

```sh
~/D/w/j/i/D/e/api ❯❯❯ yarn install
yarn install v1.22.10
warning package.json: No license field
warning api@1.0.0: No license field
[1/4] 🔍  Resolving packages...
success Already up-to-date.
✨  Done in 0.78s.

~/D/w/j/i/D/e/api ❯❯❯ yarn run start
yarn run v1.22.10
warning package.json: No license field
$ node server.js
Running on http://0.0.0.0:8080
```

別のターミナルを開き、APIに対してcurlコマンドを実行してみます。

```sh
~/D/w/j/i/D/exercise1 ❯❯❯ curl -i localhost:8080/success                                                                            ✘ 130 
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 24
ETag: W/"18-I7869XbEC6HwfWnbOsW4Su4KNX8"
Date: Sat, 23 Oct 2021 11:57:00 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Datadog APM Test Success%                                                                                                                  


~/D/w/j/i/D/exercise1 ❯❯❯ curl -i localhost:8080/error
HTTP/1.1 500 Internal Server Error
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 22
ETag: W/"16-tK4+NQ9vglErJFlFghaDSSEP52I"
Date: Sat, 23 Oct 2021 11:57:07 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Datadog APM Test Error%  
```

APIから成功とエラーが返ってきた場合、サンプルのAPIは完成です。

次に、APIのDocker Imageを作成し、ImageをDockerHubへpushします。

まずはAPIのDocker Imageを作成するために、Dockerfileを作ります。

``` Dockerfile
FROM node:14-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN yarn install
COPY . .
EXPOSE 8080
CMD [ "node", "server.js" ]
```

ImageをDockerHubへpushします。
以降の作業については、
DockerHubのリポジトリ名を「hogehoge/sample-api」と仮定します。
自分で作業を進める場合、個人用のリポジトリを作成してください。

```sh
docker build . -t sample-api
docker tag sample-api hogehoge/sample-api:latest
docker push hogehoge/sample-api:latest
```

DockerHubへサンプルAPIイメージをpushすることができました。


#### Kubernetesマニフェストファイルの作成
KubernetesのDeploymentとServiceを作成していきます。

```deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sample-api
  name: sample-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-api
  strategy: {}
  template:
    metadata:
      labels:
        app: sample-api
    spec:
      containers:
      - image: hogehoge/sample-api
        name: sample-api
```

```service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: sample-api
  name: api-service
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: sample-api
status:
  loadBalancer: {}
```

deployment.yamlとservice.yamlファイルを作成したら、applyして動作確認します。

```sh
~/D/w/j/i/D/exercise1 ❯❯❯ kubectl apply -f deployment.yaml
~/D/w/j/i/D/exercise1 ❯❯❯ kubectl apply -f service.yaml
~/D/w/j/i/D/exercise1 ❯❯❯ kubectl get svc api-service -w   

✘ 1
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
api-service   ClusterIP   10.101.251.160  <none>        8080/TCP   2d17h


~/D/w/j/i/D/exercise1 ❯❯❯ kubectl run busybox --image=busybox --restart=Never --rm -it sh
If you don't see a command prompt, try pressing enter.


/ # wget 10.101.251.160:8080/success
Connecting to 10.101.251.160:8080 (10.101.251.160:8080)
saving to 'success'
success              100% |*******************|    24  0:00:00 ETA
'success' saved

/ # wget 10.101.251.160:8080/error
Connecting to 10.101.251.160:8080 (10.101.251.160:8080)
wget: server returned error: HTTP/1.1 500 Internal Server Error
```

APIをKubernetes上で動かすことができました。

ここまでで、事前準備は完了です。
長かったですね。
次から、いよいよ本題です。


### Datadogの設定
次に、Datadogの設定を行い、Agentをインストールします。
![名称未設定.021.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/1e89d5a3-ff43-7015-02af-8c937017d332.jpeg)

#### アカウントの登録
[公式ホームページ](https://www.datadoghq.com/ja/free-datadog-trial/)からDatadogのアカウントを登録しましょう。

#### Datadog Agentのセットアップ
次に、Datadog Agentをセットアップします。
基本的には、Datadogのドキュメントの指示に従って設定します。
今回はKubernetesを使用するため、プラットフォームは”Kubernetes”を選択します。

![image12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/aeb09ee1-c8ae-53ac-5f8b-e5ac514b4cab.png)

実際にDatadog Agentを設定していきます。
Kubernetesにおける[Agentの設定方法](https://docs.datadoghq.com/agent/kubernetes/?tab=helm)には3つの方法があります。

- Helm
- DemonSet
- Operator

今回は、最も簡単に設定可能なHelmを使って、Datadog Agentを設定します。


まだHelmをインストールしていない場合、
[こちらの手順](https://v3.helm.sh/docs/intro/install/)を参考にしてインストールしましょう。

HelmにDatadog用のレポジトリを作成し、アップデートします。

```sh
helm repo add datadog https://helm.datadoghq.com
helm repo update
```

次に、datadog-values.yamlを作成します。

datadog-values.yamlのデフォルトの設定値は[こちら](https://github.com/DataDog/helm-charts/blob/main/charts/datadog/values.yaml)に記載されています。
datadog-value.yamlで設定を上書きすることで、Datadogのサービスを有効化します。

yamlを作成したら、Datadog AgentをDeployします。

```sh
[Format]
helm install RELEASE_NAME -f datadog-values.yaml --set datadog.site='datadoghq.com' --set datadog.apiKey='hogehoge' datadog/datadog

[example]
helm install datadog -f datadog-values.yaml --set datadog.site='datadoghq.com' --set datadog.apiKey='hogehoge' datadog/datadog
```

先程作成した空のdatadog-values.yamlに
APMとLogsを有効化する設定を追加します。

```datadog-values.yaml
 logs:
    enabled: true
    containerCollectAll: true
 apm:
    portEnabled: true
    # enabled: true # Deprecated this config
```

Datadog Agentをアップデートします。

```sh
helm upgrade datadog -f datadog-values.yaml --set datadog.site='datadoghq.com' --set datadog.apiKey='hogehoge' datadog/datadog
```

これで、Datadog Agentのインストールと設定は完了です。
次に、サンプルAPIとDatadog Agentを連携させてみましょう。

#### サンプルAPIとDatadogの連携
今の設定だと、APIのログがDatadog Agentに収集されず、
Datadogにログが送信されません。

![image3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/e57e378f-5165-e6c0-fe99-278a30c3e812.jpeg)

そこで、Datadogにログを送信できるように、
Kubernetes用のIntegrationをインストールし、
KubernetesマニフェストとAPIを修正します。


※ここからちょっと寄り道
Kubernetesのインフラ状況を可視化するために、
さくっとIntegrationをインストールします。

Integrationから、「Kubernetes Integration」、
「Kubernetes Metrics Server」をインストールします。

![image13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/dfc3524e-b6e5-c280-cad7-7edd1b46148d.png)
※寄り道終了

それでは、APIとmanifestを修正して、
APMのトレースとLogsを確認できるようにします。

##### サンプルAPIの修正
まずはAPIを修正します。

Datadogにログを送信するためのライブラリ
「logger.js」を新たに追加します。

また、ロギングに必要な情報を含めるために、
ロギングライブラリ「winston」を使用します。
winstonは、ログレベル、メッセージ、タイムスタンプ等の情報を
ログに含めることができます。

これらの詳細は、Datadog公式ドキュメントの
[Node.jsの設定方法](https://docs.datadoghq.com/logs/log_collection/nodejs/?tab=winston30)をご覧ください。

また、[エラーの内容をAPMでトレース](https://docs.datadoghq.com/tracing/setup_overview/setup/nodejs/?tab=containers)出来るようにするために、
ライブラリ「dd-trace」を追加します。

```package.json
{
  "name": "api",
  "version": "1.0.0",
  "description": "Node.js on Docker",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
+   "dd-trace": "^1.5.0",
    "express": "^4.16.1",
+   "winston": "^3.3.3"
  }
}
```

```logger.js
const { createLogger, format, transports } = require("winston")

const httpTransportOptions = {
  host: "http-intake.logs.datadoghq.com",
  path: "/api/v2/logs?dd-api-key="+process.env.DD_API_KEY+"&ddsource=nodejs&service=api",
  ssl: true,
}


const logger = createLogger({
  level: "info",
  exitOnError: false,
  format: format.json(),
  transports: [
    new transports.Http(httpTransportOptions),
  ],
})

exports.logger = logger
```

```server.js
'use strict';

const express = require('express');
+ const winstonLog = require("./logger.js")

// Constants
const PORT = 8080;
const HOST = '0.0.0.0';

+ // APM Settings
+ const tracer = require("dd-trace").init({
+   logInjection: true,
+ })

// success
const app = express();
app.get("/success", (req, res) => {
+   winstonLog.logger.log("info", "/success logger test!")
  res.send("Datadog APM Test Success")
})

// error
app.get("/error", (req, res) => {
+   winstonLog.logger.log("error", "/error logger test!")
  res.status(500).send("Datadog APM Test Error")
})

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
```

注意：logger.jsには未完成の点が2つあります。

1. Datadog Agentを使わずに、「httpTransportOptions」を用いて直接Datadogへログを送信している点
Datadog AgentからDatadogにログを送る方法が上手くいきませんでした...
minikube環境なのも関係しているかもしれません。
時間の都合もあり、直し方が分からないため、[エージェントレスの方法](https://docs.datadoghq.com/logs/log_collection/nodejs/?tab=winston30)を使用しました。

2. process.env.DD_API_KEYで環境変数を読み込んでいる点
datadogのapi keyを暗号化無しで設定するのはセキュリティの観点からNGです。
実際に運用する際には、Kubernetesでsecretから読み込ませるようにしましょう。


##### Kubernetesマニフェストの修正

エラーが出力された際に、PodのAPIのバージョンや環境等が分かると、
より楽にトラブルの解析が進められます。

そこで、環境変数にこれらの情報を事前に設定します。
すると、Datadog Agentがこれらの情報をタグ付けして、
Datadogにログを送信できます。

詳細は[こちらの公式ドキュメント](https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/?tab=kubernetes)をご覧ください。

修正点は以下のとおりです。

```deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sample-api
+    tags.datadoghq.com/env: "test"
+    tags.datadoghq.com/service: "api"
+    tags.datadoghq.com/version: "1.0.0"
  name: sample-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-api
  strategy: {}
  template:
    metadata:
      annotations:
        ad.datadoghq.com/sample-api.logs: '[{"source":"express","service":"api"}]'
      labels:
        app: sample-api
+        tags.datadoghq.com/env: "test"
+        tags.datadoghq.com/service: "api"
+        tags.datadoghq.com/version: "1.0.0"
    spec:
      containers:
      - image: hogehoge/sample-api
        name: sample-api
        env:
+          - name: DD_AGENT_HOST
+            valueFrom:
+              fieldRef:
+                fieldPath: status.hostIP
+          - name: DD_ENV
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.labels['tags.datadoghq.com/env']
+          - name: DD_VERSION
+            valueFrom:
+              fieldRef:
+                fieldPath: metadata.labels['tags.datadoghq.com/version']
+          - name: DD_LOGS_INJECTION
+            value: "true"
+          - name: DD_API_KEY
+            valueFrom:
+              secretKeyRef:
+                name: dd-api-key
+                key: DD_API_KEY
```


DD_API_KEYを環境変数から読み込むために、
DD_API_KEYのsecretマニフェストを作成します。

```sh
kubectl create secret generic dd-api-key --from-literal=DD_API_KEY=DD_API_KEY --dry-run=client -o yaml > dd-api-key-secret.yaml
kubectl apply -f dd-api-key-secret.yaml
```

ファイルの修正は完了です。
変更内容を反映させます。

```sh
# Push the API Image to DockerHub
docker build . -t sample-api
docker tag sample-api hogehoge/sample-api:latest
docker push hogehoge/sample-api:latest

# Re-create the deployment to update the Kubernetes image
kubectl delete -f deployment.yaml
kubectl apply -f deployment.yaml
```

ようやく設定が完了しました。

### 使い方
作成したAPIを叩いてみて、DatadogのAPMとLogsを確認します。
※何度かAPIを叩きましょう

```sh
~/D/w/j/i/D/exercise1 ❯❯❯ kubectl get svc api-service                                                             ✘ 1
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
api-service   ClusterIP   10.101.251.160   <none>        8080/TCP   2d17h

~/D/w/j/i/D/exercise1 ❯❯❯ kubectl run busybox --image=busybox --restart=Never --rm -it sh
/ # wget 10.101.251.160:8080/success
/ # wget 10.101.251.160:8080/error
```

APMを確認します。

Service: APIのAPMが確認できます。
![image11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/0b304a54-598f-6083-27e8-c8264ccd45a9.png)


APIのLatencyが確認できます。処理が遅いリクエストの特定もできます。
![image14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/1ba0534d-b368-a202-e041-22e8b57273c2.png)


各エンドポイント（/error, /success）のAPMが確認できます。
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/86aac3d7-ffc8-e9f0-43fe-848c3093ed6f.png)

/errorのAPMを確認してみます。
ログの時間、ステータスコード等が一覧で確認できます。
ログを集積することでシステムの解析が楽になりますね。
![image16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/71aa1c5d-aad7-eb69-7339-db75b4c2ad53.png)


1つリクエストの処理時間の内訳をトレースすることができます。
処理のボトルネックを見つけることにも役立ちます。
![image8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/a0674e70-d99a-4149-fd6c-ec8c4856f430.png)


次にLogsを確認します。
LogsではDatadogで集めたログ全てを表示、検索できます。
特定サービス、環境のログを表示したい場合、
Kubernetesのマニフェストの中で埋め込んだタグを元に、
フィルタリングすることもできます。
![image17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303285/84f6ccc2-dac3-93d8-21d2-87b996dd9976.png)


ここまで見ていただき、ありがとうございます。
これで、一通りのハンズオンは終了です。

アプリケーション側に簡単な設定を入れるだけで、
ログを収集し、Datadog側で確認することが出来ます。

自前でログ収集・管理システムを構築するよりも、
ぐっと簡単なのが伝わりますでしょうか。

### 今後の展望
Datadogには、ログを可視化以外にも、
システムメトリクスの可視化、外形監視等
様々な機能が提供されています。

どれも、システムの運用に深く関わる重要な機能ですので、
興味が湧いた方は詳しく調べてみてはいかがでしょうか。

例えばですが...

- Alerting and Notification
APM、Logsと親和性が高い
集積したログを元に、ユーザに通知を飛ばすことで、運用の自動化が出来る

- Metrics
同じく、APMやLogsと親和性が高い
APMと組み合わせることで、より精密に通知を飛ばすことができる 等

Datadogを導入して、クラウド上に構築されているシステムの運用監視を
より簡単に行ってみてはいかがでしょうか？

## おわりに

今回は、DatadogのAPMとLogsを設定し、
アプリケーションログの集積、検索方法を説明しました。

Datadogがこんなにも便利なのを知ってしまったら、
自前で運用システムを構築するなんて、やりたくなくなりますね。

今回紹介した機能の他にも、AlertingやMetricsと組み合わせることで、
Datadogではより高度な運用、監視が出来ます。

長々とお付き合いいただき、ありがとうございました。
引き続き、Advent Calendarをお楽しみいただければ幸いです！

※記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。