次世代監視の大本命！ Prometheus を実運用してみた

こんにちは！freeeでインフラゾンビをやっている @sugitak です。ゲームではレベルを上げて物理で殴る派です。

freee ではたまに[インフラエンジニアの数が減る](http://qiita.com/manabusakai/items/56a5b7dd6def3b8aa93f)のですが、その減ったインフラエンジニアはインフラゾンビへと進化し、社内を闊歩します。インフラゾンビは主に開発チームに所属して、アプリっぽいインフラの仕事をインフラからアプリ側へと持っていきます。デプロイとか、Dockerとか、Jenkinsとかの、いわゆる DevOps 系のところですね。こうすることで開発者は手を出せるものの自由度が増えるし、インフラはより本来のインフラとして純度を上げていける、 so, win-win ってわけです。

さて、そんなわけで監視です。[freee Engineers Advent Calendar 2016](http://qiita.com/advent-calendar/2016/freee)の9日目の記事として、 Prometheus による監視が最高なのでみんなもっと使おうという布教記事を書きます。

# Prometheus

みなさん、 O'reilly の SRE 本、読んでますか？
Google の社内で利用されている技術はたいてい筋が良いから真似たいですよね。MapReduce とか Golang とか、Google 発で定着しきった技術は枚挙にいとまがありません。監視だって、 Google 式の監視が自分でできるとしたら、そりゃー真似たい。そう思いますよね。

**それ、できるんですよ。 [Prometheus](https://prometheus.io/) があるから！！**

Prometheus は、 Google 出身者が Google 社内監視ツール Borgmon にインスパイアされて作成したものです。SRE 本には Borgmon の遺伝子を継いだ OSS としてこの Prometheus が紹介されています。まさに次世代監視の大本命と言えましょう。

Prometheus がとりわけ得意とする監視は、 Kubernetes で作られた Docker ホスト群や EC2 等で構築されてオートスケールするホスト群のような動的なやつですが、実は古典的なネットワーク機器の監視にも力を発揮します。[PromCon 2016 の資料には、 2万人・サーバ1万台・スイッチ500台という超大規模のネットワーク監視に Prometheus を使ったという話が紹介](https://promcon.io/2016-berlin/talks/monitoring-dreamhack-the-worlds-largest-digital-festival/)されています。このときの監視は Cacti → Sensu → Prometheus　と遷移していったそうです。

この Prometheus を freee 社内でも数百台のEC2ホストの監視に使用してみたところ、**実際最高でした**。これはぜひみなさんに使っていただかねばなるまいと、今回のアドベントカレンダーではこの紹介記事を書くことに決めました。

# 優れているポイント

## 構築が簡単

**Prometheus は単バイナリを動かすだけでいい**んですが、これって**監視ツールでは本ッ当に強みです**。

OSS監視ツールの構築維持管理って従来、地味に大変でしたよね。Perl をアップデートしたら `<@INC>` エラーが出まくったりとか、何時間かけても Pandora FMS を立てられなかったりとか、 port upgrade したら Cacti のプラグインが消えたりとか、 RabbitMQ 運用するつもりなのかと先輩に詰められたりとか、全部私のことだけど、単バイナリで監視ツールが動かせるのならそんな悲しい思いをする運用者が地上からいなくなるんですよ…！

**ライブラリをアップグレードして安心な監視ソフト**って、もうそれだけで革命的ですよね。

さあ、[あなたと Prometheus、今すぐダウンロード](https://prometheus.io/download/)。

## autoscaling にサクサク追従

Prometheus は pull 型監視です。つまり、サーバ側から監視先ホストを叩いて回るモデルです。 そのため、「どこのホスト・ポートを叩きに行けばいいか」を Prometheus に教える必要があります。と言っても、全部書く必要はもちろんありません。[Service Discovery](https://prometheus.io/docs/operating/configuration/) を使って探させればいいのです。
AWS や GCP, Azure で立てたサーバを、 API を使って一覧取ってきて、監視対象として認識できます。

この service discovery は Prometheus 本体にがっちり組み込まれているので、設定ファイルにちょいっと書くだけで EC2 のホスト一覧を取ってこられます。もうね。最高ですよね。

```yaml
scrape_configs:
  - job_name: ‘my_ec2_hosts’

    # EC2 Service Discovery Config
    ec2_sd_configs:

      - region: <region>
        access_key: <access-key>
        secret_key: <secret-key>
        port: 9100  # 監視対象ポート
```

上の無駄のない設定だけで、全ホスト一覧が確保できます。

全ホストを監視するわけではないのなら、絞り込みが必要ですね。絞り込みは下のように書きます。
やや冗長ですが、何を書いているかは一目瞭然ですし、なにより一度書いてしまえばメンテナンスfreee。便利です。

```yaml
    relabel_configs:

      # "Stage" が "production" のもの
      - source_labels: [__meta_ec2_tag_Stage]
        regex: production
        action: keep

      # "Role" が "web" のもの
      - source_labels: [__meta_ec2_tag_Role]
        regex: web
        action: keep
```

なお Service Discovery はこれ以外にも [Consul や Kubernetes, DNS などがあります](https://prometheus.io/docs/operating/configuration/)。Service Discovery に頼らないで監視対象を直接指定することももちろん可能です。

## ラベルベースで検索できる

Web サービスって、 `Service` `Stage` `Role` と、3段階くらいにホストを分類しますよね。Datadog や Mackerel などの現代的監視ツールはここをツリー状に扱いますが、 Prometheus はフラットに扱います。
つまり、以下のようなことをサクッとできてしまうわけです。


- **サービスやステージにかかわらず**、web サーバの load average を観察したい

```
node_load5{Role="web"}
```

- デプロイ前後で性能変化があるか見たい。このサービスの web サーバの平均空きメモリを並べる。ステージごとに。

```
avg(node_memory_MemAvailable{Service="myservice", Role="web"}) by (Stage)
```

本家ではこれを `multi dimensional` なデータだと呼称しています。

このクエリ、難しそうに見えるかもしれませんが、実際には Prometheus の "WebUI" ですぐに試すことができます。
WebUI ではクエリを打て、即座にグラフを見られるので、独自言語を使うストレスがまったくありません。JavaからREPLのあるLL言語に移ったときのような心地よさがあります。

<img width="640" alt="webui_small.png" src="https://qiita-image-store.s3.amazonaws.com/0/71400/a98d308f-1cf6-d428-5a28-cf92bd7d6342.png">


ホスト一覧やアラートなど、監視に関する状態はすべてこの WebUI から確認します。とても重要な画面です。

<img width="640" alt="prometheus_targets_small.png" src="https://qiita-image-store.s3.amazonaws.com/0/71400/1cd210e5-6e0c-0081-a844-637cbffb8391.png">


## Grafana で高度にビジュアライズ

Prometheus のビジュアライズは WebUI でもできなくないのですが、 WebUI は監視ツールとしては貧弱です。 Grafana で最高のUXを体験しましょう。[本家でテンプレートが公開されている](https://grafana.net/dashboards)ものをベースにすれば難なく綺麗な画面が手に入れられます。

<img width="640" alt="single_graph_small.png" src="https://qiita-image-store.s3.amazonaws.com/0/71400/31b68a08-74d1-8e64-54fc-4b354c3a3086.png">

また、同じようなグラフの繰り返しも Grafana の得意とするところです。 Zabbix で苦労して作っていたL2スイッチ48ポートそれぞれのI/O packets のグラフも、 repeat panel 機能にかかれば一瞬でしょう。
もっと言うと、そもそも48個のグラフを描くのって必須ではないはずです。**どうせ「突然ピークが現れた」ところしか普段は目では確認できない**のだから、「上流以外の全ポートの中でもっともトラフィックの多いもの」を1つのグラフで見えるようにしておけば十分でしょう。こういうアグリゲーションは Prometheus のクエリが効くところですね。

<img width="640" alt="multiple_hosts_small.png" src="https://qiita-image-store.s3.amazonaws.com/0/71400/ff588cc4-532f-c907-2768-0223bcf95f5b.png">

このように、 Prometheus + Grafana の組み合わせは本当に強力で、「本当に見たかったもの」が何だったのかを根本から変えるほどの自由度があります。この二つはしっかり Integrate されているので、使用していてとても直感的です。

Prometheus のビジュアライズの哲学には、「必要最低限の線とグラフ数に抑える」ことと、「インサイトを得られないグラフは描画しない」ことが含まれているようです。平時に見るグラフと緊急時に見るグラフはまた別だと考えれば、これはきわめて納得する考え方ではないかと思います。

Prometheus に限らず Datadog や Mackerel のような現代的な監視ツールでは、**事象のトレンドを見るための機能が充実しています**。アグリゲーション系関数を使っていると、[ペット扱いだったサーバがどんどん家畜へと変わっていく](http://blogs.itmedia.co.jp/narisako/2014/07/post-047f.html)のが実感できます。

## モジュール分割の設計が最高

ビジュアライズは Grafana でやっていると聞くと、いったい Prometheus　本体はどこまでを担当しているんだ、という点が気になるかもしれません。
Prometheus 本体の主な仕事は三つだけです。

- メトリクスの収集・格納
- クエリへの回答
- アラートを上げる

下図は Prometheus 公式のもの（直接リンク）です。真ん中にあるのがサーバで、ようはデータの取得・加工だけをやっている格好です。

![Prometheus Architecture](https://camo.githubusercontent.com/df3e3daf7d6809ba82986eb33664a4283314f7a9/68747470733a2f2f63646e2e7261776769742e636f6d2f70726f6d6574686575732f70726f6d6574686575732f653736316630642f646f63756d656e746174696f6e2f696d616765732f6172636869746563747572652e737667 "Prometheus Architecture")

データは左から取得し、真ん中に溜め込まれ、右側からのクエリに答えて時系列データを返します。さらに、溜まったデータに対してルールを適用してアラートを生成します。

従来型の監視ではビジュアライズやアラート、データ収集そのものを図にすることが多いですが、 Prometheus は**グラフは作らずとにかくデータを収集する**ような設計で、だから data centric な図になっています。
これは Datadog の哲学ともよく合致しています。

> _Collecting data is cheap, but not having it when you need it can be expensive, so you should instrument everything, and collect all the useful data you reasonably can._
[DATADOG -- Monitoring 101: Collecting the right data](https://www.datadoghq.com/blog/monitoring-101-collecting-data/)

曰く、データを収集することが第一で、活かすのはそれからでもいい。

さて、右側にはアラートが描かれていますが、これがいったいどうなっているのか。次の節でご説明します。

## アラート

Prometheus のアラートの哲学は簡単です。

**アラート受けたくねえええええ**

もちろん半分冗談ですが、半分は本当です。
インフラエンジニアなら、下のようなアラートを受けたことありますよね。

- 大量のメールが飛んできた。監視サーバ近くのネットワーク瞬断だ
- 大量のアラートメールでメールサーバが溢れた
- 日曜日、電話で報告受けたけど対応必要ないやつだ。そう分かっていてもパソコンを取り出してチケットを閉じないといけない
- 翌営業日対応でいいもので日曜午前４時に起こされる
- アプリケーションの問題なのでぼくにはどうすることもできない

そしてそういうアラートは受けたくないですよね。
[Prometheus ではこういうアラートを潰すことを哲学としています](https://docs.google.com/document/d/199PqyG3UsyXlwieHaqbGiWVa8eMWi8zzAn0YfcApr8Q/edit)。

より正確には、以下の三点が重要なポイントです。

- アラートは必要最低限出す。それ以上はノイズにしかならない
- アラートは、人間によるアクションが必要なもののみに絞る
- アラートは、緊急性のあるものだけにする

これを実現するために、 Prometheus には Alertmanager があります。

### Alertmanager

Alertmanager の仕事は三つです。

- 同じアラート群をまとめる
- 誰に送るかルーティングする
- 再送やクローズのコントロール

なかでも大事なのは、やはりアラートのまとめ方でしょう。同じ種類のアラートなのに、大量にメールや Slack 通知が来てしまっては、本当に危険なアラートを見逃してしまうおそれがあります。

```yaml
route:
  receiver: infra         # default receiver

  group_by: ['alertname', 'Service', 'Stage', 'Role']
  group_wait: 30s         # wait for aggregating alert
  group_interval: 5m      # wait for alert (next time)
  repeat_interval: 3h     # wait for alert (re-sending same one)
```

上は、 Alertmanager の設定の一部です。この例では「同じアラートで同じサービス・ステージ・ロールのものが 30 秒以内に来たら、それは同じアラートだ」と判断し、以後この種類のアラートが来ても5分ごとにしか通知はしません。また、一度起きたアラートが次回通知されるのは3時間後です。
アラートをまとめるためだけに、三種類の時間を設定しているの、徹底していますよね。

ルーティングも便利です。「デフォルト通知先はインフラ。このサービスに関するアラートは、サービス開発者に通知する。ただし非同期ジョブであれば非同期ジョブのチームに ack を送る」のように、アラートの種類や属性によって細かく通知先をコントロールできます。

通知は、メール・Webhook・Slack・Hipchat・PagerDuty・OpsGenie と、ほしいところは揃っています。不足に感じることはないでしょう。

### Prometheus で上げるアラート

Alertmanager ではアラートの分類ができますが、その元となる「アラートの種類や属性」を決めたり、そもそもアラートを発する条件を決めるのは、 Alertmanager ではなく Prometheus 側です。
このへんの役割分担は、一度使ってみないと腑に落ちないかも。設計は美しいので、使えばまず間違いなく納得します。

以下のような条件式を "rule" として Prometheus に設定します。

```
ALERT MemoryTooLow
  IF node_memory_MemAvailable / node_memory_MemTotal < 0.1
  ANNOTATIONS {
    description="{{ $labels.instance }} has memory below 10%",
    summary="{{ $labels.instance }} has not enough memory"
  }
```

すると、 Prometheus は数十秒おき程度にこの条件式を評価してくれ、条件を満たしたときには Alertmanager に向けてアラートを発します。
この挙動のイメージとしては、リアルタイムストリーミング処理とか CEP とかが近いかも。

Grafana ではこのアラートを赤い縦棒としてグラフに載せることもできます。アラートの起こったタイミングをグラフと一発で見比べられるのは、想像してた以上に便利なものです。

## 高性能

ここまで読んでいただいた方の中には、 Elasticsearch + Kibana を思い浮かべる方もいらっしゃるかもしれません。あれけっこう使うの難しいですよね。めちゃめちゃマシンパワーも食うし。

そうすると Prometheus の性能も気になってくるかと思いますが、**これがまた、いいんですよ。性能**。

### CPU/メモリ面

freee では Prometheus による数百台のホスト監視を t2.large(2コア8GBメモリ) で実行していますが、性能的にはまったく問題ありません。むしろ監視だけなら t2.medium(2コア4GBメモリ) で十分なほどです（ビジュアライズのためにやや大きめにしています）。 [Prometheus Casual Talks #2](https://connpass.com/event/41370/) では、1000台くらいまでは t2.medium で監視できるというお話もありました。私の実感としてもだいたい同じくらいです。

Grafana のスクリーン表示は十分早い、少なくとも遅すぎはしないです。体感では100ホストくらいでグラフ15個表示すると10秒弱、うちCPU使用率のグラフが取る時間が半分以上ぐらい、というかんじです。やはり関連する点が増えてくると、さすがに時間もかかるようです。
アノマリを目で見つけるだけなら load average で事足りるので、CPU使用率は描画しないのが正解かもしれません。

### 安定面

安定性も高いです。
Prometheus　が pull 型をとっている理由の一つが性能問題です。pull 型というのは、サーバ側から監視ホストを叩きに行ってデータを取る方式のことですね。サーバ負荷が高まったときはこの巡回を遅らせるなどして調整をしてくれるようになっています。

…というと理念的な話に聞こえると思うんですが、実際にこの仕組みがしっかり実装されているってのは凄まじいことです。グラフのクエリ処理で CPU がフル回転していても、監視のデータ取得が落ちることはまずない。
一回、 Grafana のクエリを積みすぎて Prometheus を落としたことがあるんですが、特に DB 壊れるなどの問題もなく Supervisord で静かに再起動されてきました。実装レベルに対しては安心感が相当あります。

### ディスク容量面

容量面は、やや弱いかもしれません。現時点ではダウンサイジング機能が備わっていないのです。

手元のデータを見てみたところ、どうも一ヶ月一台あたり数百MB使っているようです。同様の監視に劣る数値ではありませんが、決して少なくはないですね。このデータを細かい粒度で何ヶ月も保持するのは厳しいので、データのダウンサイジングが必要となってきます。

ダウンサイジングについては [Prometheus Casual Talks #1](http://d.hatena.ne.jp/wyukawa/20160615/1465962814) でも話題になっていました。幾つかやり方はありますが、まだBCPは確定していないというのが実情のようです。

私は細かいことを考えて運用負荷を増やすことはしたくないので、一ヶ月程度でデータを消しています。アラートとその調査のためであれば、それでやや過剰なくらいです。

## 監視対象もバイナリ一個

さて、これまで監視する側の話ばかりしてきたところで、「監視される側はどうなの」という疑問が増してきたかと思います。
監視される側では、 exporter と呼ばれるバイナリを daemon として動かすだけです。オプションなし、適当なユーザ権限で実行するだけで OK です。

ホスト情報に関しては [node exporter](https://github.com/prometheus/node_exporter) を使用するのですが、各種 system statics がこれでもかというほどに取られてくるので、「このデータがない…」という困り方をすることがありません。メモリやCPU時間はもちろん、ファイルシステム容量やネットワークインターフェイスの統計情報などはデフォルトで取れます。

ややニッチな話になりますが、各種状態のソケットの数も取れます。 TIMEWAIT 状態のソケットが溜まって辛い思いをした方はたくさんいらっしゃると思いますが、これもオプション一個追加するだけで追えるようになるわけです。なお、これがデフォルトでオンになっていないのは潜在的な性能問題とのことです。まあ高負荷状態のサーバで netstat 打つと１分くらいかかったりしますしね。わかる。

node_exporter で得られる情報は、かなりしっかりと公開設定を整えた SNMP のホストに対して snmpwalk したときの結果に似ています。SNMP と大きく違うのは、負荷が少ない点と、プロトコルが HTTP という点です。下のコードは、実際に curl で取ってきたときのものです。全体では1500行ありますが、0.01秒程度で取得できました。

```bash
$ curl 2>/dev/null http://test-server.example.com:9100/metrics | head -6
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 8.4269e-05
go_gc_duration_seconds{quantile="0.25"} 0.000312835
go_gc_duration_seconds{quantile="0.5"} 0.000336743
go_gc_duration_seconds{quantile="0.75"} 0.00039158600000000003
```

その他、外形監視をしたい場合は [blackbox_exporter](https://github.com/prometheus/blackbox_exporter), ネットワーク機器を使う場合は [snmp_exporter](https://github.com/prometheus/snmp_exporter) があります。各種データを集取するための exporter の実装が進んでいますので、必要なものは大体そこで揃えられます。

## カスタムメトリクスも node_exporter で

アプリごとのカスタムメトリクスはどうするのか？という疑問も出てきますよね。

Google SRE 本を読んでる方はご存知かもしれないのですが、実は Borgmon (Prometheusの元になったやつ) の設計思想では、各アプリが `/varz` というパスでアプリケーションメトリクスを公開することになっているんですね。アプリの中身についてはアプリが一番知っているので、なるほどこれは納得します。

が、ぶっちゃけ、よく分からない監視のためにアプリケーションに手を入れたくないですよね。**監視はあくまでアプリとは離し、いつでも他に乗り換えられるようにしておくべき**です。

そう思う場合は、 node_exporter の `collector.textfile.directory` オプションを使えば、 node_exporter からカスタムメトリクスを送信できるようになります。こちらは私もこれからという段階なのであまり詳しく説明できないのですが、 [cron で5分ごとにファイル書き込みをする例なんかがweb上に](https://www.robustperception.io/monitoring-directory-sizes-with-the-textfile-collector/)共有されています。

# まとめ

各項目で褒めまくった Prometheus ですが、今回使ってみてもちろん弱点も見つかりました。最大の弱点は、**ドキュメントと日本語の記事が足りない**ところです。
ここまで見てきたように、 Prometheus は自由度が高く、作ろうと思えばいくらでも自分好みのメトリクスを取ったりアラートをあげたりできるようになっています。その一方で、その自由度を活かすための資料がまったく不足していて、導入のハードルが高くなってしまっているようです。

今回この記事を書いた目的は、そこのハードルを下げることでした。「ふつーの Web アプリの監視に Prometheus はすでに十二分に使える状態になっているよ」ということを共有することで、より多くの方と情報を交換していきたいと思っています。

今時は、サーバの監視といえば Mackerel や Datadog, NewRelic を使うのが正解でしょう。マネージドな監視の利点は本当に数え切れません。ディスク管理や冗長性の維持、真の意味での外形監視に加え、技術の人依存やアップグレード労力までサービスに任せられるのですから、価格以上のものを得られることには疑いの余地がありません。だからこそ、 [freee では Mackerel を監視の中心に据えて](https://mackerel.io/ja/blog/entry/2016/10/18/161951)、インフラの運用コストを徹底的に下げています。

しかし一方で、イニシャル段階のサービスや開発環境、社内ネットワーク監視、やたらとスケールアウトするバッチサーバ等、コストを抑えたかったりどうしても自前で立てる必要があるような監視サーバはまだまだあります。こういったところの新しい選択肢として、今後 Prometheus が加わっていくことでしょう。

# おわりに

さて、そろそろ忘れている頃かと思いますが、この記事は[freee Engineers Advent Calendar 2016](http://qiita.com/advent-calendar/2016/freee)の9日目の記事でした。というわけで、[freee では開発をサイコーにやりやすく変えていきたいタイプのエンジニアも募集しています](https://jobs.freee.co.jp/)！

明日は、freee が誇るモバイルバーサーカー族が一員、落ち着き具合に熟練を感じる @laprasDrum です！エモになるのか、テックで来るのか、正直予想がつきません。いやー、楽しみですね。楽しみですね。