初めてのDatadogとすぐに始められるサンプルコードを添えて

# 概要

Datadogのトライアルで試した各種設定方法をメモとして残したいと思います
なお、以下の各種設定をすぐに試せるように後述のソース（cloud-config）を用意したので、EC2のuser-dataにペーストすればすぐに試せるようになっています。起動するインスタンスを複数台にすることでコンテナ群として監視できるようになるので、是非お試しください

# 前提条件

以下の内容はAWSのリソースを監視する各種設定になります

# サンプルソース

サンプルソースでは以下のDatadog設定やサンプルアプリが含まれている

- APMを試すためのDockerコンテナで作られたNode.jsアプリ（WEB、APP、DBの3層構成）
- dockerコンテナの監視を有効
- ec2タグを取得するを有効
- ログファイルの取得を有効（nginxのアクセスログ収集）
- プロセスの取得を有効
- APMの有効

**※使用する前にコード内の`<datadog api key>`をご自身のAPI KEYに差し替えてください**

```yaml:cloud-config.yml#cloud-config
repo_update: true
repo_upgrade: all

write_files:
  # Sample APP Docker compose file
  - path: /var/docker/docker-compose.yml
    permissions: 0644
    owner: root:root
    content: |
      version: "3.7"
      services:
        web:
          image: nginx:latest
          container_name: web-container
          ports:
            - "80:80"
          labels:
            qiita.comefigo.service: "web"
          volumes:
            - ./web/default.conf:/etc/nginx/conf.d/default.conf
            - ./web/log:/var/log/nginx
          depends_on:
            - app
        app:
          build:
            context: ./app
            dockerfile: Dockerfile
          image: app
          container_name: app-container
          labels:
            qiita.comefigo.service: "app"
          depends_on:
            - db
        db:
          image: redis:4
          container_name: db-container
          labels:
            qiita.comefigo.service: "db"
  # Sample APP app.js
  - path: /var/docker/app/app.js
    permissions: 0644
    owner: root:root
    content: |
      const tracer = require("dd-trace").init();
      const http = require("http");
      const redis = require("redis");
      const async = require("async");

      const redisClient = redis.createClient({
        host: "db",
        port: 6379
      });
      redisClient.on("error", function(err) {
        console.log("error");
        console.log(err);
      });

      // サンプルデータの投入
      redisClient.set("data1", "hello world 1");
      redisClient.set("data2", "hello world 2");

      const handleRequest = function(request, response) {
        let resp = "";
        async.parallel(
          [
            function(callback) {
              redisClient.get("data1", function(error, value) {
                callback(null, value);
              });
            },
            function(callback) {
              redisClient.get("data2", function(error, value) {
                callback(null, value);
              });
            }
          ],
          function(err, results) {
            for (let i = 0; i < results.length; i++) {
              resp += "<ul> " + results[i] + " </ul>";
            }
            response.writeHead(200);
            return response.end("<h1>redis value:</h1><br/>" + resp);
          }
        );
      };

      const www = http.createServer(handleRequest);
      www.listen(3000);
  # Sample APP package.json
  - path: /var/docker/app/package.json
    permissions: 0644
    owner: root:root
    content: |
      {
        "name": "datadog-trial-app",
        "version": "1.0.0",
        "description": "A simple Node.js app for Datadog trial",
        "main": "app.js",
        "dependencies": {
          "async": "^2.6.2",
          "dd-trace": "^0.10.2",
          "redis": "^2.8.0"
        },
        "devDependencies": {},
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [
          "Datadog",
          "APM"
        ],
        "author": "comefigo",
        "license": "ISC"
      }
  # Sample APP Dockerfile
  - path: /var/docker/app/Dockerfile
    permissions: 0644
    owner: root:root
    content: |
      FROM node:8
      ADD ./package.json /app/package.json
      WORKDIR /app
      RUN npm install
      ADD ./app.js /app/app.js
      CMD node ./app.js
  - path: /var/docker/web/default.conf
    permissions: 0644
    owner: root:root
    content: |
      server {
        listen       80;
        server_name  localhost;

        access_log  /var/log/nginx/host.access.log  main;

        location / {
            proxy_pass http://app:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
      }
  # datadog config for monitoring docker containers
  - path: /tmp/docker.yaml
    permissions: 0644
    owner: root:root
    content: |
      init_config:

      # docker socket経由でDockerの監視
      instances:
          - url: "unix://var/run/docker.sock"
            collect_events: true
            new_tag_names: true
            collect_container_size: true
            collect_exit_codes: true
  # datadog config for monitoring nginx log
  - path: /tmp/nginx.yaml
    permissions: 0644
    owner: root:root
    content: |
      logs:
        - type: file
          service: sample_service
          path: /var/docker/web/log/host.access.log
          source: nginx
          sourcecategory: http_web_access
  # datadog config
  - path: /tmp/datadog.yaml
    permissions: 0644
    owner: root:root
    content: |
      api_key: <datadog api key>
      tags:
        - env:prd
        - role:app
        - tenant:datadog-app

      # EC2のタグを利用する
      # ただし、Nameタグは利用されない
      collect_ec2_tags: true

      # ログ収集を有効
      # 各種ログの設定は別途必要
      logs_enabled: true

      # container labelを別名のタグ名に変換
      docker_labels_as_tags:
        qiita.comefigo.service: service

      # プロセスの監視
      process_config:
        enabled: "true"
     
     # APM
     apm_config:
       enabled: true

runcmd:
  - [
      sh,
      -c,
      'DD_API_KEY=<datadog api key> bash -c "$(curl -L https://raw.githubusercontent.com/DataDog/datadog-agent/master/cmd/agent/install_script.sh)"',
    ]
  - sudo systemctl start datadog-agent
  - sudo systemctl enable datadog-agent
  - [sh, -c, "amazon-linux-extras install -y docker"]
  - sudo systemctl start docker
  - sudo systemctl enable docker
  - [sh, -c, "usermod -aG docker ec2-user"]
  - [sh, -c, "usermod -aG docker dd-agent"]
  - [
      sh,
      -c,
      'curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose',
    ]
  - [sh, -c, "chmod +x /usr/local/bin/docker-compose"]
  - [sh, -c, "mkdir -p /var/docker/web/log"]
  - sudo chown ec2-user:ec2-user -R /var/docker
  - [sh, -c, "docker-compose -f /var/docker/docker-compose.yml up -d"]
  - sudo mv /tmp/docker.yaml /etc/datadog-agent/conf.d/docker.d/docker.yaml
  - sudo chown dd-agent:dd-agent /etc/datadog-agent/conf.d/docker.d/docker.yaml
  - sudo mv /tmp/nginx.yaml /etc/datadog-agent/conf.d/nginx.d/nginx.yaml
  - sudo chown dd-agent:dd-agent /etc/datadog-agent/conf.d/nginx.d/nginx.yaml
  - sudo mv /tmp/datadog.yaml /etc/datadog-agent/datadog.yaml
  - sudo chown dd-agent:dd-agent /etc/datadog-agent/datadog.yaml
  - sudo systemctl restart datadog-agent
```


# 大きな流れ

1. Agentの導入
2. 設定ファイルの作成
3. ダッシュボードの作成
4. アラート（Monitors）の作成

# 始める前に得ていた情報

以下では営業担当者との情報交換で得た情報になります

## 料金関連

基本的に[料金プラン](https://www.datadoghq.com/pricing/)通りの価格である
500ホスト/月以上を扱う場合はサポートに相談

料金に関する質問などをQA形式

### Q. 開発用のホスト（すぐ破棄や停止するホスト）やオートスケールした分のホストは課金対象になるのか？

ホストにタグ付けすることで課金対象の範囲を絞り込むことができるので、
そういった用途のホストはその対象外のタグにすることで課金除外することはできる

### Q. 利用時間が1月未満の課金ホストの扱いはどうなるのか？

時間単位の課金もあるので、必要に応じて時間課金($0.03/h)を利用することも可能
例えば、年払いで一定数のホスト分を予め支払い、一時利用で超過したホスト分を時間単位で追加課金することも可能

参考：[課金に関するFAQ](https://docs.datadoghq.com/ja/guides/billing/)

### Q. 課金対象数の算出方法は？

1か月の間で課金対象のホスト数の最大値が課金対象となる
一時利用で立ち上げたホストも課金対象となるので要注意！

### Q. 年払いと月払いを混合できるか？

混合はできない
2契約にするならできなくもない

# 各種設定

## エージェントの導入

導入方法は非常に簡単
「Integrations」-「Agent」でプラットフォームにあった導入方法で簡単に導入することができる
![datadog_agent.png](https://qiita-image-store.s3.amazonaws.com/0/30522/5e000608-29b8-4ee9-3f36-d1c11df1623e.png)

## Slack

若干癖がある設定･･･

1. Datadogの「Integration」から「Slack」をインストール
2. SlackのAppで「Datadog」をインストールし、「Webhook URL」をコピー
3. DatadogのSlack設定で「Slack Account Name」に通知先のSlackワークスペースを識別できるように適当な名前を付ける。「Slack Account Hook」に前述でコピーしたWebhook URLを設定し、**一度「Update Configuration」←ここ重要！**
4. Slack設定で「Channel to post to」に「#チャネル名」、「Account」に「Slack Account Name」で設定した名前を選択し、「Update Configuration」で完了
![datadog_slack_config.png](https://qiita-image-store.s3.amazonaws.com/0/30522/c0e56486-1940-373b-475f-fcd49570371c.png)
5. 通知テスト  
ダッシュボードの適当なメトリックの「📷」アイコンで「@」+「Slack Account Name」で送信  
Slackのみならず、登録されているアカウントやDatadogのサポートへ送信することもできる
![annotation_metrics.png](https://qiita-image-store.s3.amazonaws.com/0/30522/770db0b2-f407-4a77-719a-fc3468c6487a.png)
6. Slackで受信  
![slack_recive_annotation.png](https://qiita-image-store.s3.amazonaws.com/0/30522/6e4e0013-e3e2-31a6-1374-41531a2dfde2.png)

## 設定ファイル

拡張機能の設定ファイルは`/etc/datadog-agent/conf.d`配下に適当に置けば読み込んでくれるかと思ったが、きちんと所定のフォルダに配置しなければならない

### 設定確認

`datadog-agent status`でagentに適用されている設定を確認する
設定が間違っている場合は`Error`となっているので、デバッグとしても利用できる

```shell

Getting the status from the agent.

===============
Agent (v6.10.1)
===============

  Status date: 2019-03-15 00:12:44.720529 JST
  Pid: 21905
  Python Version: 2.7.15
  Logs: 
  Check Runners: 4
  Log Level: info

  Paths
  =====
    Config File: /etc/datadog-agent/datadog.yaml
    conf.d: /etc/datadog-agent/conf.d
    checks.d: /etc/datadog-agent/checks.d

  Clocks
  ======
    System UTC time: 2019-03-15 00:12:44.720529 JST

  Host Info
  =========
    bootTime: 2019-03-14 23:08:43.000000 JST
    kernelVersion: 4.14.97-90.72.amzn2.x86_64
    os: linux
    platform: amazon
    platformFamily: rhel
    platformVersion: 2
    procs: 197
    uptime: 1h4m0s
    virtualizationRole: guest
    virtualizationSystem: xen

  Hostnames
  =========
    ec2-hostname: xxxxx.ap-northeast-1.compute.internal
    hostname: xxxxx
    instance-id: xxxxx
    socket-fqdn: xxxxx.ap-northeast-1.compute.internal.
    socket-hostname: xxxxx.ap-northeast-1.compute.internal
    hostname provider: aws
    unused hostname providers:
      configuration/environment: hostname is empty
      gce: unable to retrieve hostname from GCE: status code 404 trying to GET http://xxxxx/computeMetadata/v1/instance/hostname

=========
Collector
=========

  Running Checks
  ==============
    
    docker
    ------
      Instance ID: docker [OK]
      Total Runs: 1
      Metric Samples: Last Run: 21, Total: 21
      Events: Last Run: 0, Total: 0
      Service Checks: Last Run: 1, Total: 1
      Average Execution Time : 11ms
      
========
JMXFetch
========

  Initialized checks
  ==================
    no checks
    
  Failed checks
  =============
    no checks
    
=========
Forwarder
=========

  Transactions
  ============
    CheckRunsV1: 0
    Dropped: 0
    DroppedOnInput: 0
    Events: 0
    HostMetadata: 0
    IntakeV1: 1
    Metadata: 0
    Requeued: 0
    Retried: 0
    RetryQueueSize: 0
    Series: 0
    ServiceChecks: 0
    SketchSeries: 0
    Success: 1
    TimeseriesV1: 0

  API Keys status
  ===============
    API key ending with : API Key valid

==========
Endpoints
==========
  https://app.datadoghq.com - API Key ending with:

==========
Logs Agent
==========

  tomcat
  ------
    Type: file
    Path: /var/log/hogehoge.log
    Status: OK
    Inputs: /var/log/hogehoge.log

```

### [ホストやコンテナのタグ付け](https://docs.datadoghq.com/tagging/)

ダッシュボードでホストやリソースをグルーピングやフィルタリングするためにタグ付けをします
今どきの監視ツールにおいてはなくてはならない仕組みなので、予めタグ設計をしとくと良いでしょう
例えば、同じ役割を担うホストを`app`とタグ付けすることで、それらをまとめて俯瞰することができる
後述になりますが、「Host Map」で閾値に従ってビジュアライズすることもできるので、一目で問題箇所を特定することができる

```yaml:/etc/datadog-agent/datadog.yaml
tags:
  - env:prod
  - role:app
```

EC2のタグを用いる場合は`collect_ec2_tags: true`を追加しましょう
**※Nameタグは利用されないので、ご注意！**

```yaml:/etc/datadog-agent/datadog.yaml
collect_ec2_tags: true
```

#### Host Map

ホストまたはコンテナをタグ別に俯瞰できる
冗長化されたシステムやWEB、APP、DBなどの3層式構成の場合は、階層ごとにタグを分けることでどこで障害が発生しているのかも見つけやすくなる
下記の画像ではHostsのHost Mapですが、前述のサンプルを用いた場合は、ContainersのGroup hosts By tagを`service`にすることで同様な表示になります

![datadog_hostmap.png](https://qiita-image-store.s3.amazonaws.com/0/30522/7c1b7e54-620a-e04b-9712-a3b629046d3f.png)

#### タグ付けの制限

リファレンスにも書かれていますが、改めて

- unicodeで200文字まで
- すべて小文字
- スネークケース（`test_tag`）
- キーで`host`、 `device`、 `source`、`service`は予約語のため使用してはいけない

## Monitors

メトリックスが閾値に達した時、規定値を外れた時、普段とは異なる値になった時（機械学習）などにお知らせしてくれます
また、例えば特定の状況下でシステムの自動再起動も設定することができる
![datadog_metric_trigger.png](https://qiita-image-store.s3.amazonaws.com/0/30522/855658e0-9297-7f13-543c-9fd57b5da15a.png)

### 通知

検知したメトリックスの画像付きで通知してくれるので、非常にわかりやすい
また、メッセージで[使用できるメッセージテンプレート変数](https://docs.datadoghq.com/monitors/notifications/?tab=is_alertis_warning#message-template-variables)も豊富なので、痒い所に手が届く作りかなと思う

![datadog_metric_slack.png](https://qiita-image-store.s3.amazonaws.com/0/30522/12d38cec-b897-766c-3c09-7a0948cb6d40.png)

### 履歴

メトリックスの履歴が保存されている

![datadog_metric_history.png](https://qiita-image-store.s3.amazonaws.com/0/30522/dc43e2a7-0b29-acd7-a6af-04c1bb70918d.png)


## ダッシュボード

集めたデータ（各種メトリックス、ログ、発生したイベント、APM）をダッシュボードでシステム全体を俯瞰することができる
個人的に一番気に入っているのはダッシュボード内の情報を絞り込むための**変数**を定義することができるのでダッシュボードの可用性が高まる
例えば同じ中身でホストが違うダッシュボードをいくつも作るようなこともなくなる

![datadog_dashboard_variables.png](https://qiita-image-store.s3.amazonaws.com/0/30522/452ce308-abba-b1f0-fdc1-9672c1378347.png)


## レスポンステスト（[Synthetics](https://www.datadoghq.com/pricing/#section-synthetics)）

グローバルゾーン(アメリカ、イギリス、ドイツ、東京)でレスポンステスト（ステータスコード、レスポンスタイム）が可能
HTTPメソッドごとにテストを作成できる
POSTパラメータを投入することでフォームのテストも可能
※連続したページのテストはできない
テストごとにタグを設定することでテスト結果のフィルタリングも可能

![synthetics_setting.png](https://qiita-image-store.s3.amazonaws.com/0/30522/78fc329b-d9fc-809a-31b4-ae1398a033ad.png)

### テスト結果

サイトの稼働率が一目でわかる

![synthetics_result.png](https://qiita-image-store.s3.amazonaws.com/0/30522/9abee10d-cc82-12f6-7355-97e5d717544d.png)

問題検知時と回復時にSlack通知
![synthetics_slack.png](https://qiita-image-store.s3.amazonaws.com/0/30522/d5e94afc-ccce-94c6-6736-46e11751e5c4.png)


一旦トライアルが切れたのでここまですが、今後APMや他の設定を試したら追加していきたいと思います