Dockerコンテナ内のログをSplunkに入れてみた

# はじめに
通常Dockerコンテナ内で稼働しているプロセスのログはコンテナの中にそのまま残ります。
結果、集中管理が難しくなるところですが、Splunkを使ってあらゆるコンテナのログを集約して管理できるようにします。

ということで、Dockerのコンテナ内に吐き出されるログをSplunkに流してみました。

コンテナ内のログを外に出してSplunkに送る方法は**Logging Driver**を使います。
https://docs.docker.com/engine/admin/logging/splunk/

また、Splunkでログを受け取る仕組みは、**HTTP Event Collector**を使います。
http://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector

# 設定方法
この2つを使ってログを送信する設定について記載します。
今回使用したバージョンは以下
Splunk: 6.5.3
Docker: 17.03.1

## Splunkの設定
まずはSplunkでHTTP Event Collector（通称HEC）をセットアップします。
[設定] > データ入力 > HTTPイベントコレクタ
<img width="619" alt="Screen Shot 2017-07-08 12.32.12 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/205a6b58-a5b9-1e1c-dbd0-6a55050787cf.png">

<img width="568" alt="Screen Shot 2017-07-08 12.41.17 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/9be39236-582a-b31e-bb24-c9d43365fb7e.png">

HEC設定画面右上の [グローバル設定] をクリック
<img width="651" alt="Screen Shot 2017-07-08 12.42.44 PM_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/34e1e6b8-1e56-f6f4-23f2-6f08dcc15c38.png">

[全トークン] を「有効」にする
<img width="796" alt="Screen Shot 2017-07-08 12.42.59 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/0d9b79f7-6286-503f-a8e9-ff4b2b85cfc9.png">

続けて、 [新規トークン作成] をクリック
<img width="651" alt="Screen Shot 2017-07-08 12.42.44 PM_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/6c333ad1-8cad-2927-c3af-08bad489425e.png">

名前を適当につける
<img width="628" alt="Screen Shot 2017-07-08 12.48.47 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/3956ee7f-19fa-75cb-edf9-94c3a956ba78.png">

次の画面に進んで、インデックスを設定
<img width="998" alt="Screen Shot 2017-07-08 12.50.49 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/4368e448-8199-711d-0a36-760922c15917.png">

今回はDockerのnginxイメージから作ったコンテナを利用するので、`web`インデックスを許可します。
一応`main`インデックスも許可しておきます。
デフォルトは`main`インデックスにしていますが、`web`インデックスに入れるよう後のステップでDocker側で設定します。

次の画面に進んで、設定内容を確認して　[実行] します。
<img width="614" alt="Screen Shot 2017-07-08 12.54.55 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/174837/8bd3b7e3-c89e-b44a-9c62-23326b4bdf26.png">

トークンが発行されるので、それをメモ帳等に保存しておきましょう。
![Screen Shot 2017-07-08 12.55.55 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/1357e88a-51d7-2c79-ee94-05657f0a80ce.png)


これでSplunkの設定は終了。


## Dockerの設定
続いてDockerの設定です。

### Splunk証明書の配置
SplunkにHTTPSでデータを投げるので、証明書の認証が必要になります。
Splunkに自己署名の認証局（CN=SplunkCommonCA） `$SPLUNK_HOME/etc/auth/cacert.pem` をコピーして、Dockerが入っているサーバーの任意の場所に配置します。

```bash
$ openssl x509 -in cacert.pem -subject -noout
subject= /C=US/ST=CA/L=San Francisco/O=Splunk/CN=SplunkCommonCA/emailAddress=support@splunk.com
```

ちなみに、Splunkサーバー証明書は `CN=SplunkServerDefaultCert` です。
これは後でコンテナ起動時に使用します。

```bash
$ openssl x509 -in server.pem -subject -noout
subject= /CN=SplunkServerDefaultCert/O=SplunkUser
```

### コンテナ起動
あとはコンテナ起動時のオプション指定だけでSplunkにログが飛んでいきます。

```bash
docker run --name splunk-nginx -p 80:80 -d \
 --log-driver=splunk \
 --log-opt splunk-token=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX \
 --log-opt splunk-url=https://splunk.example.com:8088 \
 --log-opt splunk-capath=/etc/docker/cacert.pem \
 --log-opt splunk-caname=SplunkServerDefaultCert \
 --log-opt splunk-index="web" \
 --log-opt splunk-sourcetype=access_combined \
 --log-opt splunk-format=raw \
 nginx
```

`--log-driver=splunk` でSplunk用Logging Driverを指定しています。
その他の設定を `--log-opt` で指定しています。

`splunk-token` --> HECのトークン
`splunk-url` --> SplunkサーバーのURL（ポートはHEC設定に従う）
`splunk-capath` --> Splunkからとってきたcacert.pemのパス
`splunk-caname` --> サーバー証明書のCN（なぜ"ca"name？）
`splunk-index` --> Splunkのインデックス（HEC設定で指定した`web`インデックスを使用）
`splunk-sourcetype` --> Splunkのソースタイプ（nginxなので`access_combined`を使用）
`splunk-format` --> ログのフォーマット

### （補足）ログのフォーマットについて
`splunk-format` には `json` `inline` `raw` の3種類が指定できます。
デフォルトは`inline`ですが、その場合、Splunkではこのように見えます。
![Screen Shot 2017-07-08 1.22.23 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/c172bd28-c90c-3875-6989-d15182589b1f.png)

JSON形式となって`line`にログが記録されています。
ただ、この場合フィールドが自動抽出されずに後々ダッシュボード作成等でメンドいので、あえて`splunk-format=raw`を指定します。

ちなみに `splunk-format=json` を選択してもnginxの場合は `inline` と変わりません。
`json` はJSON形式のログをちゃんとJSONとしてパースしてくれるだけです。

## Splunkで検索してみる
![Screen Shot 2017-07-08 1.15.07 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/5681dd5c-8e9e-6333-bf20-3052bc54c29d.png)

ん？

![Screen Shot 2017-07-08 1.33.49 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/cb7afe04-6fda-4537-04e1-a4393f9224b6.png)

あれ？

ログ先頭にコンテナIDが入ってしまてますね。

![Screen Shot 2017-07-08 1.35.42 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/29596511-f14a-ebab-8625-7a2e433ee2c3.png)

そのせいで、せっかく `access_combined` を指定したのに、フィールドが自動抽出されていないようです。

## 再びSplunk設定
強引にコンテナIDをソースに上書きして、ログから除去します。
Splunkの `props.conf` と `transforms.conf` を使います。


```splunk:props.conf
[source::http:docker_logging_driver]
TRANSFORMS-1_docker_source = replace_docker_source
TRANSFORMS-2_docker_raw = drop_docker_id
```

```splunk:transforms.conf
[replace_docker_source]
REGEX = ^([0-9a-f]{12})\s
DEST_KEY = MetaData:Source
FORMAT = source::$1

[drop_docker_id]
REGEX = ^([0-9a-f]{12})\s([\s\S]+$)
DEST_KEY = _raw
FORMAT = $2
```

この設定でソースをコンテナIDに上書きしつつ、ログから先頭のコンテナIDを消し去っています。

（参考）
[props.confマニュアル](https://docs.splunk.com/Documentation/Splunk/latest/Admin/Propsconf)
[transforms.confマニュアル](https://docs.splunk.com/Documentation/Splunk/latest/Admin/Transformsconf)

## もっかいSplunkで検索
![Screen Shot 2017-07-08 1.47.36 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/39fa6f22-c49e-9e92-5295-f78f471a0257.png)

いけたかな？

![Screen Shot 2017-07-08 1.48.19 PM_mosaic.png](https://qiita-image-store.s3.amazonaws.com/0/174837/1e922035-b46d-bd11-01e1-0e4fa8cbd7d6.png)

うん、いけてますね。
フィールド抽出できてます。

# 最後に
けっこう簡単な設定でコンテナ内のログをSplunkに入れることができますね。
他にも `docker stats` でコンテナの統計情報をとるスクリプト作ってSplunkに入れたり、 `/var/log/docker` をSplunkからモニターして入れたりしても良さそうです。


# おまけ
Docker設定していて気づいたことを少々。

## daemon.jsonではSplunk Logging Driverの設定できないっぽい
↓のように作ってみて `--log-driver` や `--log-opt` の指定なしでコンテナ起動してみたのですが、ログはSplunkに入ってきませんでした。

```json:daemon.json
{
  "log-driver": "splunk", 
  "log-opts": {
    "splunk-token": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 
    "splunk-url": "https://splunk.example.com:8088", 
    "splunk-capath": "/etc/docker/cacert.pem", 
    "splunk-caname": "SplunkServerDefaultCert", 
  }
}
```

正確には `--log-opts` で諸々のSplunk設定することがサポートしていないらしく、こんなログが出てきました。

```log:/var/log/docker
time="2017-06-29T17:09:59.886826680Z" level=error msg="the following directives don't match any configuration option: splunk-token, splunk-url, splunk-capath, splunk-caname"
```

## コンテナIDについて
Splunkで `props.conf` と `transforms.conf` を設定して強引にコンテナIDをソースに指定していますが、そもそも↓みたいなオプション指定できれば楽なのになぁ。

```bash
docker run --name splunk-nginx -p 80:80 -d \
 --log-driver=splunk \
   ...
 --log-opt splunk-source="{{.ID}}" \
 nginx
```

どうやら `{{.ID}}` とか `{{.Name}}` は `tag` でのみ利用可能のようです。
https://docs.docker.com/engine/admin/logging/log_tags/

## Splunk証明書の認証を無効化
コンテナ起動時のオプションで↓を指定すると証明書の認証を無効にするようです。

```
--log-opt splunk-insecureskipverify=true
```

これを使えば、Splunkから証明書をとってきてオプションで指定して、、、というメンドくさいことが省略できますね。
ただし、中間者攻撃のリスクは出てくるので正直オススメはできないですね。

（追記 2017/08/23）
証明書の無効化オプションを `--log-opt splunk-verify-connection=false` と記載しましたが、正しくは `--log-opt splunk-insecureskipverify=true` でした。修正しました。