はじめての Docker -「grafana + CloudWatch」を、わりとマジメに運用するまで

CloudWatch で生暖かく見守れば十分なサーバもありまして、だけど AWS アカウント複数あるし見た目アレだしやる気が出ないなぁと思っていたところ [grafana](http://grafana.org/) を知りました。「ほほう、CloudWatch 対応とな」と。
Docker イメージもあるそうなので、Docker 歴２時間の私が勉強がてらやってみます。

元ネタ：[Prometheusを触ってみる。のをDockerでやってみる。のとgrafana。](http://bufferings.hatenablog.com/entry/2016/12/11/130254)
（Prometheus に反応するのが正解なのでしょうが、grafana の方にヒットしてしまいました）

# ゴール
- grafana にログインするだけで「**複数 AWS アカウント**」の CloudWatch が見えること。これが最大の要件。
- ログイン URL の変更＋αくらいのことはする。
- 適当な EC2 に docker インストールして運用する（基本を体験しておきたい）。

ちなみにローカル環境は mac です。

# grafana おためし起動
[Installing using Docker](http://docs.grafana.org/installation/docker/) のとおり。docker でさくっと起動。

```
$ docker run -i -p 3000:3000 grafana/grafana
```

で、localhost:3000 見たらもう動いてた。docker すごい。
![001ds.png](https://qiita-image-store.s3.amazonaws.com/0/116572/9296a2d5-60fb-4d0c-9cf9-6d8f44046f49.png)

# AWS につなげる

GitHub の [grafana/grafana-docker](https://github.com/grafana/grafana-docker) によると、AWS の Credential は環境変数で指定出来るとのこと。
やってみる。

```
$ docker run -d -p 3000:3000 --name=grafana \
-e "GF_AWS_PROFILES=default" \
-e "GF_AWS_default_ACCESS_KEY_ID=ごにょごにょ" \
-e "GF_AWS_default_SECRET_ACCESS_KEY=ごにょごにょ" \
grafana/grafana
```

で、admin / admin でログイン。
DataSource の設定はこう。

![002ds.png](https://qiita-image-store.s3.amazonaws.com/0/116572/567b5b56-5473-68b8-f234-50fc3ba4c48c.png)

Credentials profile name の値は GF_AWS_PROFILES で指定したもの。
何事もなく繋がった。

で、ぽちぽちしたらグラフ描けた。grafana すごい。

![003ds.png](https://qiita-image-store.s3.amazonaws.com/0/116572/74b89267-2d72-5850-f2b0-d1202849736f.png)

ダッシュボード名とか、普通に日本語使えてびっくりした（多分英語にするけど）。

# データの永続化

このままだとせっかく作ったダッシュボードとかが消えてしまうので永続化する。
ダッシュボード情報が格納されている sqlite3 は /var/lib/grafana に、設定ファイルは /etc/grafana に配置されているとのこと。
設定ファイルはとりあえずデフォで良いとして sqlite3 だけ外にだす。

ちょっと勉強のため docker-compose 使ってみた。

こうして、

```docker-compose.yml
version: '2'

services:
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      GF_AWS_PROFILES: default
      GF_AWS_default_ACCESS_KEY_ID: ごにょごにょ
      GF_AWS_default_SECRET_ACCESS_KEY: ごにょごにょ
    volumes:
      - ./var-lib-grafana:/var/lib/grafana
```

起動。

```
$ mkdir var-lib-grafana
$ docker-compose up
```

で、ダッシュボードとか設定して ./var-lib-grafana をみると、なんか出来てた。

```
$ ls var-lib-grafana/
grafana.db  plugins/  sessions/
```

おっけ。docker 楽しくなってきた。

ところで複数 AWS アカウントに繋げる場合の environment はこう。

```docker-compose.yml（env部分）
    environment:
      GF_AWS_PROFILES: accountA accountB

      GF_AWS_accountA_ACCESS_KEY_ID: ごにょごにょ
      GF_AWS_accountA_SECRET_ACCESS_KEY: ごにょごにょ

      GF_AWS_accountB_ACCESS_KEY_ID: ごにょごにょ
      GF_AWS_accountB_SECRET_ACCESS_KEY: ごにょごにょ
```

[grafana/grafana-docker](https://github.com/grafana/grafana-docker) の 「You may also specify multiple profiles to GF_AWS_PROFILES (e.g. GF_AWS_PROFILES=default another).」 を見逃しててすっごくハマった。

# ちゃんと設定する

admin パスワードが admin とか問題外なわけで。勝手にユーザ登録できちゃうし。

> All options defined in conf/grafana.ini can be overriden using environment variables by using the syntax `GF_<SectionName>_<KeyName>`.

とのこと。以下４つを設定する。

| 変数 | 意味 |
|:------|:------|
| GF_SECURITY_ADMIN_USER | admin ユーザ名 |
| GF_SECURITY_ADMIN_PASSWORD | admin パスワード |
| GF_USERS_ALLOW_SIGN_UP | ユーザが自分でサインアップできるか |
| GF_USERS_ALLOW_ORG_CREATE | 非 admin ユーザが organization 作成できるか |

docker-compose.yml の environment 部分に以下を追記する。

```docker-compose.yml（追記分）
    environment:
      GF_SECURITY_ADMIN_USER: admin-hogehoge
      GF_SECURITY_ADMIN_PASSWORD: admin-hogehoge
      GF_USERS_ALLOW_SIGN_UP: "false"
      GF_USERS_ALLOW_ORG_CREATE: "false"
```

ちなみに grafana.ini だと以下の部分。

```grafana.ini
[security]
# default admin user, created on startup
admin_user = admin-hogehoge
# default admin password, can be changed before first start of grafana,  or in profile settings
admin_password = admin-hogehoge

[users]
# disable user signup / registration
allow_sign_up = false
# Allow non admin users to create organizations
allow_org_create = false
```

grafana.ini に「default admin user, created on startup」とか書いてあるので DB もクリアしてから compose up。

```
$ rm -rf var-lib-grafana/*
$ docker-compose up
```

ちゃんと "Sign up" タブ消えた（自分でサインアップ禁止）。

![007ds.png](https://qiita-image-store.s3.amazonaws.com/0/116572/93780e97-f8db-cc0e-a149-e2cc059cbfad.png)

# nginx はさむ

DataDog や Mackerel のようにどこからでもアクセスできるようにしたいけど位置づけ的には「内部用」サーバ。nginx はさんでゴニョゴニョしておく。

## URL 変更

やり方は [Running Grafana behind a reverse proxy](http://docs.grafana.org/installation/behind_proxy/) を参照。
nginx は official のコンテナを使う。[jwilder/nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy/) が使えたらよかったのだろうけど、パスの変更に対応してないようなので残念。[pull-request](https://github.com/jwilder/nginx-proxy/pull/599) は出てるっぽい。

こんな ./nginx_conf.d/nginx.conf を用意して、

```nginx_conf.d/nginx.conf
server {
  listen 80;

  location /bjMtjuC9mdXj/ {
    proxy_pass http://con-grafana:3000/;
  }
}
```

docker-compose.yml は、こう。

```docker-compose.yml
version: '2'

services:
  con-grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      GF_AWS_PROFILES: default
      GF_AWS_default_ACCESS_KEY_ID: ごにょごにょ
      GF_AWS_default_SECRET_ACCESS_KEY: ごにょごにょ
      GF_SERVER_DOMAIN: con-nginx
      GF_SERVER_ROOT_URL: "%(protocol)s://%(domain)s:%(http_port)s/bjMtjuC9mdXj/"
      GF_SECURITY_ADMIN_USER: admin-hogehoge
      GF_SECURITY_ADMIN_PASSWORD: admin-hogehoge
      GF_USERS_ALLOW_SIGN_UP: "false"
      GF_USERS_ALLOW_ORG_CREATE: "false"
    volumes:
      - ./var-lib-grafana:/var/lib/grafana
  con-nginx:
    image: nginx
    ports:
      - "18080:80"
    volumes:
      - ./nginx_conf.d:/etc/nginx/conf.d
```

なにも考えずに設定すると yml とか conf のいたるところで "nginx" とか "grafana" という一般名詞が現れて値の関連がわかりにくくなるので、サービス名のアタマには "con-" を付けるようにした。

これで `http://localhost:18080/bjMtjuC9mdXj/` でアクセスできるようになった。

## BASIC 認証

普通にこうして（grafana の admin ユーザと同名にしている理由は後述）、

```
$ htpasswd -c nginx_conf.d/htpasswd admin-hogehoge
```

こう。

```nginx_conf.d/nginx.conf
server {
  listen 80;

  location /bjMtjuC9mdXj/ {
    auth_basic "restricted";
    auth_basic_user_file /etc/nginx/conf.d/htpasswd;

    proxy_pass http://con-grafana:3000/;
  }
}
```

で、実はこの後の動きがよくわからない。

まず grafana は認証関係が充実していて、その一つである auth-proxy という機能で「BASIC 認証の ID/PW で　grafana にログインする」こともできるそうです。
see also [Grafana Authproxy: have it your way](https://blog.raintank.io/authproxy-howto-use-external-authentication-handlers-with-grafana/).

そして auth-proxy はデフォルト OFF なのですが、nginx で BASIC 認証かけるとどうしてもその ID/PW で勝手に grafana にログインしようとしてしまう。なので BASIC 認証をパスした時点で grafana のログに "そんなユーザいないよ" と怒れてログイン画面を表示してくれない。

auth.proxy の設定してないのに。`GF_AUTH.PROXY_ENABLED: "false"` してもダメ。

だいぶハマりこけたのであきらめた。BASIC 認証でそのままログインすることにする（なにか基本的な考え方が違うのかな）。
というわけで、htpasswd には grafana admin ユーザの ID/PW を登録しています。ユーザを追加するなら htpasswd にも追加する運用で。

ま、BASIC 認証を突破しないとそこに grafana があることすら分からないのでむしろいいのかなと。

# EC2 に乗せる

おおよそ整ったので ECS に乗せて運用開始する。といってもここまでくれば後はいつもどおりな感じ。

まず適当にインスタンス立てて、以下を参考に Docker と docker-compose をインストールする。

- ECS Developer Guide / [Docker Basics](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html)
- Docker / [Install Docker Compose](https://docs.docker.com/compose/install/)

```
$ sudo yum update -y
$ sudo yum install -y docker
$ sudo service docker start

$ sudo usermod -a -G docker ec2-user

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
```

で、docker-compose up すれば動く。
「ローカルで動かしてたものが本番サーバでそのまま動く」ってすごい。

あとは Route53 アレしたり Certificate Manager で https にしたりとか、そーゆーあたりの話。
この辺は話題がずれてくるので割愛します。

<img width="500" alt="DropShadow ~ dashboard.png" src="https://qiita-image-store.s3.amazonaws.com/0/116572/8d89c9a9-6866-c797-1fb3-0ed2972dda74.png">
（まだ調整中のダッシュボード。grafana だけで複数 AWS アカウント横断で俯瞰できるのってとても便利。）

ECS とか k8s に進む前に、いったんこれで苦労してみようと思う。
構成管理とか CI もまた今度。

初 Docker の題材として、はからずも丁度よかった気がします。