DatadogのLogmanagementにCloudwatchLogsのログを転送してみた

DatadogのLogs（logmanagement）をつかってみる検証の備忘録的なものです。
https://docs.datadoghq.com/logs/explorer/?tab=logstream
（まあ長めかも）

Datadogの画面の、
Logs＞docsからログソースを選択すると手順が表示されるのでそれに従うとすんなりいく感じです。
Integrationできるログは色々あるようです
https://docs.datadoghq.com/integrations/#cat-log-collection

#### ホストのログを送る

- 前提条件
 - dd-agentが6以上じゃないとlogmanagementつかえない。
 - 検証してみたホストOSはAmazonLinux1


- 公式以外の参考
https://developers.cyberagent.co.jp/blog/archives/12565/
http://htnosm.hatenablog.com/entry/2017/12/11/090000#%E3%83%AD%E3%82%B0%E5%8F%8E%E9%9B%86%E6%9C%89%E5%8A%B9%E5%8C%96
http://htnosm.hatenablog.com/entry/2018/03/29/090000
http://htnosm.hatenablog.com/entry/2018/03/30/090000

- エージェント側の設定

```yaml:/etc/datadog-agent/datadog.yaml
logs_enabled: true
logs_config:
  container_collect_all: true
```

```yaml:/etc/datadog-agent/conf.d/nginx.d/conf.yaml
init_config: null
instances:
    [{}]

logs:
  - type: file
    service: nginx-test-app
    path: /home/test/logs/access.log
    source: nginx access.log
    sourcecategory: http_web_access

  - type: file
    service: nginx-test-app
    path: /home/test/logs/error.log
    source: nginx error.log
    sourcecategory: http_web_access
```
※コンテナからマウントして保存しているホストにあるログ

どうやらdd-agentに権限が要る模様

```
# sudo -u dd-agent tail /home/test/logs/access.log 
/home/test/logs/access.log
```
dd-agentにsudoしながらtail打ってnot foundやPermission deny等でなければ見られるはず。

```
# initctl restart datadog-agent
# /opt/datadog-agent/bin/agent/agent status  
～抜粋～
==========
Logs Agent
==========
  nginx
  -----
    Type: file
    Path: /home/test/logs/access.log
    Status: OK
    Inputs: /home/test/logs/access.log 
  
    Type: file
    Path: /home/test/logs/error.log
    Status: OK
    Inputs: /home/test/logs/error.log 
```

コンテナからマウントして保存しているホストにあるログは取れるようになった。
（Web画面からも取れてる風にみえた）

syslogはカスタムログでも送る設定できるがdd-agentの権限で読めるようにlogrotatedで切り回すときの処理を仕込むのがアレなら[rsyslog](https://docs.datadoghq.com/integrations/rsyslog/)や[journald](https://docs.datadoghq.com/integrations/journald/)とのIntegrationをしたほうがよさそう。

#### コンテナのログを送る

https://docs.datadoghq.com/logs/log_collection/docker/
hostの設定ファイルだと、/etc/datadog-agent/datadog.yamlに以下のように書く。
`docker logs <container-id>`で取れるコンテナが標準・標準エラー出力吐く内容が転送される。

```
logs_enabled: true
logs_config:
  container_collect_all: true
```
dockerコンテナで起動するエージェントだと、dockerコマンドでdatadog-agentのイメージを起動するときに以下のオプションをつける。

```
-e DD_LOGS_ENABLED=true ##ログ収集を有効化
-e DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true ##すべてのコンテナのログ収集を有効化
-v /opt/datadog-agent/run:/opt/datadog-agent/run:rw ##再起動等でコンテナログ消失防止に、収集され>た最後のログ行ホスト保存
-e DD_AC_EXCLUDE="name:datadog-agent" ##Datadogエージェントのログを収集しない
-v /var/run/docker.sock:/var/run/docker.sock:ro ##ログはstdout/stderrDockerソケットから収集される
```

####コンテナのログをCloudwatchLogsに送る

https://docs.datadoghq.com/logs/log_collection/docker/


- CloudwatchLogsの制限
 - https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html


```Dockerfile:テストに使ってたDockerfile
FROM centos:centos7.1.1503
ENV container docker
#RUN export http_proxy=http://<Proxy-server>:8080 && \
#    export https_proxy=http://<Proxy-server>:8080 && \
RUN yum swap -y fakesystemd systemd && \
    yum update -y && \
    yum install -y epel-release && \
    yum install -y nginx && \
    yum clean all && \
    ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
ADD nginx.conf /etc/nginx/
ADD default.conf /etc/nginx/conf.d/
#RUN systemctl enable nginx
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
#CMD ["/usr/sbin/nginx", "-g", "daemon off;, access_log syslog:server=logserver,facility=local1 main; error_log syslog:server=logserver,facility=local2 notice;"]
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80
#LABEL "com.datadoghq.ad.instances"='[{"nginx_status_url": "http://%%host%%:%%port%%/nginx_status"}]'
LABEL "com.datadoghq.ad.logs"='[{"source": "nginx", "service": "nginx-test-app"}]'
```
nginxの場合はログを標準・標準エラー出力にリンクするとdocker logsコマンドで補足できるようになるようです。

- awslogsパッケージ入れて設定する

```
# yum install -y awslogs
# vi /etc/awslogs/awslogs.conf
# cat /etc/awslogs/awslogs.conf|grep -v '^#'|sed -e '/^$/d'
[general]
state_file = /var/lib/awslogs/agent-state
[/var/log/messages]
datetime_format = %b %d %H:%M:%S
file = /var/log/messages
buffer_duration = 5000
log_stream_name = {instance_id}
initial_position = start_of_file
log_group_name = /var/log/messages

# cat /etc/awslogs/awscli.conf 
[plugins]
cwlogs = cwlogs
[default]
region = ap-northeast-1   ##★ここ変えないとバージニアに飛んでく

# service awslogs restart
```
- コンテナ消してオプション足して上げなおす

```shell-session
# docker stop b21adda2caa7
# docker rm b21adda2caa7
# docker run -d -p8888:80 \
-v /root/docker-test/html:/usr/share/nginx/html:ro \
-l "com.datadoghq.ad.instances"='[{"nginx_status_url": "http://%%host%%:%%port%%/nginx_status"}]' \
--name nginx-test --hostname nginx-test \
--log-driver=awslogs \
--log-opt awslogs-region=ap-northeast-1 \
--log-opt awslogs-group=datadog-test \
--log-opt awslogs-create-group=true \
--log-opt awslogs-stream=ip-10-0-2-68 \
centos:nginx-test

# docker ps
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS                  NAMES
11d80c094430        centos:nginx-test                    "/usr/sbin/nginx -g …"   5 seconds ago       Up 3 seconds        0.0.0.0:8888->80/tcp   nginx-test
```

- docker的な備考：
 - dockerコンテナのログはdocker logs <container>で補足できるように標準出力・標準エラー出力に出すのが定石
 - ミドルウェアによってファイルに出してるログを標準出力に出してdocker logs <container>で補足できるようにする手順は異なる
 - dockerでのログの管理方法は3通りでコンテナに出すかホストにマウントするか標準出力をどこかに飛ばすかになる
　コンテナが永続でない揮発性が高い場合がほとんどなため、どこかに飛ばすのが一般的
 - 転送にはfluentd,awslogs,gcplogsなどのドライバがつかえる。（tag使うなどログの切り分けがわりと手間ではある模様）
 - ログディレクトリをローカルホストにボリュームマウントしてると標準出力に出なかった
- cloudwatch的な備考：
 - awslogs-datetime-formatは実際のログの行頭と一致していないとcloudwatchlogsにログが届かない
 - docker logs <container>コマンドで見られるのはjsonとjournalだけでlog-driver=awslogsだと動確に使うことはできない
 - EC2インスタンスなら必要なポリシー入りのRoleつけとけばクレデンシャル指定は不要でログをCloudwatchLogsに投げ込める（ロールはインスタンス起動したまま動的に変えられる）
 - リージョン指定変えないとデフォルトだとバージニアに届いてしまう
 - dockerコマンドでオプション渡すのだとログストリーム名にホストの変数は使えなさそう

とりあえずここまででコンテナのログが出るとこまでは行ったので続いてラムダ連携

#### CloudwatchLogsのログをLambdaでDatadogに送る

- 前提条件
 - AWSインテグレーション設定が事前に必要
 - CloudwatchLogsにログが飛んでる

AWSインテグレーションのタブ切り替え`CollectLogs`みると手順のリンクが出てくる（最新情報がここらへんにあるようになってる模様）のでそちらに従って進める（Lambda関数作ったあとでそのARNを入力する）
（画像はChromeが自動翻訳してるので日本語なだけで元は英語です）

![dd_logs_aws1.png](https://qiita-image-store.s3.amazonaws.com/0/25031/1a805f47-003e-fd88-58c4-96f73d9440bb.png)

- 参考
 - https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:464622532012:applications~Datadog-Log-Forwarder
 - https://github.com/DataDog/datadog-serverless-functions/blob/master/aws/README.md

この ↑ リポジトリのなかに各種どこから送りたいかのディレクトリがあってCloudwatchLogsから送るやつのREADMEみつつIntegrationのマニュアルみつつ準備する感じでした。
awsコマンドでCloudFormationのテンプレート使ってLambda関数登録してくれるっぽいコマンドがREADMEで紹介されてたけど何かが足らなかったのかまともに動かず手動登録しました。

- KMSで鍵をつくる（推奨らしい）

マネジメントコンソールからポチポチカスタマーマスターキーをつくり、でてきたKMSのARNを使ったIAMポリシーでつかいます。
http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
で、DatadogのAPIキーを以下のように作ったKMS鍵のIDを指定して暗号化して、出てきた文字列をLambdaファンクション登録するときに暗号化された環境変数として使用します。

```shell-session
# aws kms encrypt --key-id alias/dd-test --plaintext '<my-datadog-api-key>'
```

- ポリシー作ってロール登録

```json:lambda_executionポリシー（KMS
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:<myaccount-number>:key/<my-key-id>"
            ]
        }
    ]
}
```

KMS用の実行ユーザにもlambda-executionポリシーを直接アタッチしておいた

- lambda関数について

関数名適当にスクラッチで登録。
関数名：loglambdaddfunction
Runtime: python2.7
実行ロール：lambda-execution
ハンドラ：lambda_function.lambda_handler

https://github.com/DataDog/datadog-serverless-functions/tree/master/aws/logs_monitoring

このディレクトリのなか ↑ のlambda_function.pyをコピぺしてAPIキーのあたりを環境変数で登録してそれを呼ぶ感じに書きます。

ロールの登録してそれをLambda関数側で指定、メモリ1Ｇとタイムアウト120秒指定とか、Datadog側にLambda関数のARNをIntegrqationのログのタブから登録するとか、あとからLambda関数側にトリガ指定してストリーム絞る、メタタグ追加する等、以下のような手順かと。

- Lambda関数登録（lambda_functionのあたりにlambda_function.pyをコピぺ）
![dd-log-lambda-test-function1.jpg](https://qiita-image-store.s3.amazonaws.com/0/25031/5819fa27-2421-76bf-ccb6-809a56a8b6e2.jpeg)

※変えたとこはAPIキーのあたり。

```diff:lambda_function.py
diff --git a/aws/logs_monitoring/lambda_function.py b/aws/logs_monitoring/lambda_function.py
index 4de7ac3..b17c788 100644
--- a/aws/logs_monitoring/lambda_function.py
+++ b/aws/logs_monitoring/lambda_function.py
＠＠ -38,7 +38,15 ＠＠ except Exception:
     is_ipscrubbing = False
 
 # DD_API_KEY: Datadog API Key
-DD_API_KEY = "<your_api_key>"
+#DD_API_KEY = ""
+DD_API_KEY = os.environ["DD_API_KEY"]
+## parameters
+metadata = {
+    "ddsourcecategory": "aws",
+    "env": "prod",
+    "DD_TAGS": "nginx",
+}
+
 try:
     if "DD_KMS_API_KEY" in os.environ:
         ENCRYPTED = os.environ["DD_KMS_API_KEY"]
＠＠ -137,12 +145,13 ＠＠ class DatadogConnection(object):
 
 def lambda_handler(event, context):
     # Check prerequisites
-    if DD_API_KEY == "<your_api_key>" or DD_API_KEY == "":
+    if DD_API_KEY != os.environ["DD_API_KEY"] or DD_API_KEY == "":
         raise Exception(
             "You must configure your API key before starting this lambda function (see #Parameters section)"
         )
     # Check if the API key is the correct number of characters
     if len(DD_API_KEY) != 32:
+        #print(len(DD_API_KEY))
         raise Exception(
             "The API key is not the expected length. Please confirm that your API key is correct"
         )
```
※printはlengthチェック失敗してた時の動確用

- 環境変数とKMS設定
![dd-lambda-kms-setting.png](https://qiita-image-store.s3.amazonaws.com/0/25031/17977007-e34a-7fb0-6733-82f62d3a9850.png)
カスタマーマスターキーを使う設定↑に気付かずに復号化した文字列の長さチェックがとおらなくて焦りました。

- Lambda関数登録時の、ロールの指定、メモリ1Ｇとタイムアウト120秒指定など
![dd-log-lambda-test-other-setting1.jpg](https://qiita-image-store.s3.amazonaws.com/0/25031/0017441f-cee4-de9c-adfa-ad13f7c7cf4e.jpeg)

- 作ったLambda関数のARNをDatadog側にIntegrqationのログのタブから登録する
![dd-log-lambda-arn-regist1.jpg](https://qiita-image-store.s3.amazonaws.com/0/25031/8ac2e6ea-c589-9a3e-d41c-0d8c68412480.jpeg)

- トリガの指定してストリーム絞る
Datadog側でARN登録してからトリガ指定しないとエラーでてARN登録失敗してました。
あとIntegrqationしてないアカウントだとLambda関数のARN登録もできない模様でした。

![dd-log-lambda-test-trigger.jpg](https://qiita-image-store.s3.amazonaws.com/0/25031/8bbf3d76-b11f-0836-0c0b-7dc265bfa841.jpeg)

https://docs.datadoghq.com/integrations/amazon_web_services/#log-collection
https://docs.datadoghq.com/integrations/amazon_web_services/#collecting-logs-from-s3

テストをプロダクト(ClowdWatchLogs)選んでテンプレからそのまま作成してテストボタンを押すとFunctionがまともに動くかを確認できます（このテスト時点ではDatadogには届かない）

とどいた様子（ログでるように作ったnginxコンテナのURLリロードしたアクセスログ）

![dd-log-lambda-test-result1.jpg](https://qiita-image-store.s3.amazonaws.com/0/25031/987c18d4-2ea9-a0ed-a137-1245f43e3df0.jpeg)

送った後Datadog側でPipeline機能でログを加工するというのが可能なようで、
ログのログレベルを抽出、severityを設定、アプリ名をattributeになどができるようです。
https://docs.datadoghq.com/logs/processing/
https://developers.cyberagent.co.jp/blog/archives/12565/
https://qiita.com/14kw/items/fe05a5cbbd289d7ae276

そのご[severity設定してみた](https://qiita.com/smallpalace/items/d002715d9a79f852a9db#datadog%E3%81%A7severity%E3%83%91%E3%83%BC%E3%82%B9%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F)様子。

#### そのた

- CloudwatchLogsのログをKinesis経由でDatadogに送る

CloudwatchLogsのサブスクリプションフィルタは1つのロググループに1つしか付けれないが複数に送りたい場合にKinesisを経由さす、という方法があるようです。
http://laughingman7743.hatenablog.com/entry/2018/04/18/235119
https://dev.classmethod.jp/cloud/aws/cloudwatchlogs-firehose-s3/

kinesisFirehouseはAWSのサービスが送り先ならポチポチでいけるのでS3に出せばLambdaでdatadogに流せる感じ。
https://dev.classmethod.jp/cloud/aws/cloudwatchlogs-firehose-s3/
https://dev.classmethod.jp/cloud/aws/awf-waf-comprehensive-logging/

まあs3からとってくるlambda関数は公式リポジトリにあったのですでに出てればそれでもいいのかな。
→今回つかったのと同じファンクションでトリガ指定S3にしてバケットとプレフィックス（フォルダ）指定するのとlambdaが使ってるIAMロールにバケットへの権限追加することでs3に置かれたログがトリガでdatadogに送られるように。lambdaファンクションとS3バケットのリージョンは合わせないとダメそうでした。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "logs:CreateLogStream",
                "kms:Decrypt",
                "s3:ListBucket",
                "logs:PutLogEvents",
                "s3:GetBucketLocation"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*",
                "arn:aws:s3:::<S3バケット名1>/*",
                "arn:aws:s3:::<S3バケット名1>/",
                "arn:aws:s3:::<S3バケット名2>/*",
                "arn:aws:s3:::<S3バケット名2>/",
                "arn:aws:kms:ap-northeast-1:<myaccount-number>:key/<my-key-id>"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}
```

- 価格を追跡する

log_managementの価格の追跡をしたい場合に。
https://docs.datadoghq.com/account_management/billing/log_management/#tracking-log-events

以上