「家族アルバム みてね」を支えるモニタリング活動（2017年末版）

# はじめに

いつもお世話になっております。「[家族アルバム みてね](https://mitene.us)」の立ち上げから現在までの開発と開発チームのマネジメントも行なっている酒井と申します。この記事はmixi advent calendar 2017 20日目のために書きました。

# TL;DR
* 「[みてね](https://mitene.us)」のサービス規模は今年1年でかなり拡大した。
* 大規模なサービスでも専任インフラエンジニアを配置せずに、スマホアプリ開発エンジニアが自分たちでシステム運用することを実現している。
* 価格は高いが思い切ってNewRelic Proを契約し、モニタリング・アラートを自動化、開発者はできる限りユーザーの課題解決に集中する環境を作っている。

# 「[みてね](https://mitene.us)」の今

## サービス規模

* 先日[プレスリリース](https://miteneus.tumblr.com/post/166938406326/20171030)をだした通り、ユーザー数は200万人を超え、現在もユーザー数、アクティブ率は伸び続けています。
* 写真・動画は毎日100万枚以上シェアされ、月間にすると4000万件以上ものメディアがアップロードされています。
* 今年7月には、国際化対応されたバイナリもリリースされ、世界中で利用者を増やしています。

## 機能
* 2017年には大きいものでは、以下のような機能をリリースしています
    * 国際化対応
    * エコー動画配信サービス[Angel Memory](https://www.angel-memory.jp/AgmPage/Common/Kazocinfo)との連携
    * メディアのお気に入り登録機能・より長期に思い出を振り返ることのできる新UIの提供

## コードベース
* まだ提供前の機能の話も含むので細かいことは書けませんが、現在はさまざまな機能を分散して小さく管理するようにしており、4つのRuby on RailsリポジトリとiOS/Androidリポジトリが存在しています。
* その他、OpsWorksで利用するCustom Chef RecipeやAWSの権限周りをコードで管理していたり、複数リポジトリから利用される共通ライブラリとしてのプライベートなgemなどもあるので、そういった細かいリポジトリを考慮すると運用しているリポジトリは20個ほど存在しています。

## 開発チーム

### チーム構成・技術スタック

#### アプリ開発チーム

主にクライアントアプリの開発とすべてのインフラの運用を通して、ユーザーニーズを高速に課題解決して行くチームです。今年は少しメンバーを増員し、現在は7名のエンジニアが在籍しています。

* 業務内容
    * Server(Web Page、API、動画・画像処理）、iOS/Android UIなどアプリの機能全てを開発する
    * 機能を提供するためのすべてのインフラの作成・運用
* プラットフォームにこだわらず、ユーザーの課題を解決することが最優先ミッションであり、エンジニア全員がすべてのプラットフォームや言語を横断的に開発を行っていく

##### 技術スタック

* 開発言語: Ruby, Swift, Kotlin, JavaScript
* Web系フレームワーク: Ruby on Rails, Vue.js
* ミドルウェア: imagemagick, ffmpeg, SoX
* CI: Circle CI, Danger(Lintチェック)
* AWS: OpsWorks, EC2, S3, RDS, ElastiCache, Lambda, SNS, SES...
* デプロイ, プロビジョニング: Chef Solo

#### コンテンツ開発チーム

機械学習・DeepLearningを使ったアプローチを導入しながら、1秒動画やフォトブックの提案機能など、自動配信コンテンツの開発と提供をおこない、アプリをより魅力づけしていくチームです。

いつかこちらのチームのことも書いていきたいと思います。が、今回はこちらのチームの話ではないので詳細は省略します。

# 「[家族アルバム みてね](https://mitene.us)」を支えるモニタリング

ようやく本題です。
現在のみてね開発チームは上記の通りの構成ですが、その特徴を振り返ります。

1. チーム内のエンジニアは開発対象のプラットフォーム専任ではなく、APIからアプリUIまで全てを書く
2. インフラもエンジニアが自分たちで必要なものを見積もり、AWS上に作成し、運用もすべて行う
3. システムのモニタリングも自分たちで必要な開発・設定を行う
4. モニタリングの結果改善が必要な場合、それらもユーザーニーズと捉え自ら積極的に改善を行なっていく

最初にご紹介したとおり、非常に大きなサービス規模ではありますが、現在までにいわゆる「インフラエンジニア」は置いていないません。ただし、安定性と信頼性の担保について優先度を低く設定しているわけではありません。

むしろ、アプリ開発エンジニアが自分たちが書くコードが動くインフラまでを意識しながら注意深く開発できるような環境のために人的以外、主にAWSやサードパーティのモニタリングツールにコストを積極的に支払い、恩恵を受けるような方針としています。

## モニタリング方針

さて、実際にシステムの何をモニタリングすべきなのか？という大きな方針をチーム内で共有しています。細かくは色々とあるのですが、大きくは以下の観点からシステムのモニタリングを行なっています。

1. Applicationのエラー
2. Applicationのパフォーマンス
3. ServerのCPU、メモリ、ネットワーク、ストレージなどのリソース利用状況

そのほか、後の分析のためのロギングとログ収集基盤の構築なども行なっています。

また、いわゆる[SRE本](https://www.oreilly.co.jp/books/9784873117911/)を参考にしつつ、自分たちなりにモニタリング結果の情報をカテゴリ分けし、システム改善に生かすために必要なアクティビティをそのカテゴリごとに定義しています。

* Alert
    * あと少しでも放置するとでシステムが壊れてしまいそうな状況にある。もしくはすでに壊れている。
    * Slackなどにアラートを行い、即時人間が対応する必要がある。
* Issue
    * このまま長期間放置しておくといずれシステムが壊れて障害を引き起こす可能性がある。
    * GithubにISSUEを作成したり、バックログとして開発案件として登録し、期限付きで対応する。
* Analyze
    * システムのパフォーマンスがだんだん下がってきていないか？エラーの数がだんだん多くなってきていないか？
    * 障害があったときの対応は適切だったか？障害から何を学んだか？今後同じ問題を引き起こさないために手は尽くしたか？
    * 様々な観点からシステムと人間の行動の分析と診断を行う。

## NewRelicの導入

上記のモニタリングポイントのほとんどを[NewRelic](https://newrelic.com/)を利用して監視できるようにしています。NewRelicを使っている理由は、AWSとのIntegration、RailsとのIntegrationが簡単な点と、この後紹介する機能がアプリ開発者にとって非常にわかりやすい点です。

利用方法はとくに特殊なことはしていませんが、上記の通り専任インフラエンジニアのいない開発チーム構成なので、思い切ってNewRelic Infrastructure, APMのPro版を契約し、がっつりコストをかけています。（APM Proは、相当高いので導入時は注意！！！）

### Applicationのエラー

NewRelic APMのError Analyticsを使い、エラー発生時のbacktraceを追いかけられるようにし、エラー発生率の閾値を超えた場合にアラートするように設定しています。

<img width="1283" alt="スクリーンショット 2017-12-16 23.00.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/30355/fc2fbd13-13ac-1b20-9679-be5e14ba4ba3.png">

NewRelic Errorsはもうすぐサービス停止し、今後はError Analyticsに移行されるとのことです。
ただし、いまのところErrorsの方が機能が豊富で少し不安を感じているので、NewRelic社に積極的に改善要求を上げていこうと思います。

### Applicationのパフォーマンス

こちらもNewRelic APMのRPM計測機能やTransaction機能を利用し、APIのレスポンス速度とやバックグラウンドジョブの処理数などを監視しています。

RPMやTransactionは非常に便利で、開発時にもリリース前のコードのパフォーマンステストを事前に行なってからユーザー向けに公開する目的でも使っています。

以下はとあるバックグラウンドジョブの処理内容を可視化した状況ですが、どのレイヤーでどのぐらいの時間がかかっているかが一目で把握できます。Transaction機能はさらにActiveRecordが投げるQueryの詳細まで遡ることができるため、パフォーマンスをチューニングする場合はこれらの結果を参考にしています。

<img width="851" alt="スクリーンショット 2017-12-16 23.40.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/30355/2bb095a1-da1e-fd0b-1945-6705d9bb0670.png">

#### Apdex

Apdexとは、アプリケーション性能指標のことで、アプリケーションの応答時間やサービスの応答時間を基にユーザー満足度を測定するための業界標準の指標です。

これらも参考値としていちじるしくApdex値が低下している場合はアラートします。

Apdexの値についてはちゃんと理解するのは難しいので以下をなんども読んでなんども忘れたりしています。

[Apdex：ユーザー満足度の測定 | Degica x New Relic (New Relic 日本代理店)](https://newrelic.degica.com/docs/apm/new-relic-apm/apdex/apdex-measuring-user-satisfaction)
[Apdex とは何か - The King's Museum](http://hjm333.hatenablog.com/entry/2015/01/08/224805)

#### デプロイの記録

エラー発生率の向上、パフォーマンス低下のはデプロイをきっかけに大きくなることが多いです。それを明確にするためにデプロイの履歴をNewRelic上に記録しておくことで、障害発生時の原因特定のための情報として活用ます。

以下のAPIを実行してデプロイ時にNewRelic上にデプロイ履歴を残しておくことで、エラー発生率やパフォーマンス低下のきっかけをグラフ上から認識することができます。
[Record deployments | New Relic Documentation](https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/recording-deployments)

エラー発生時のコードの状態を特定しやすくするために、deployment apiに以下を登録しています。

```ruby
changelog = `git --no-pager log --format=%s -1 HEAD`.strip
revision  = `git rev-parse --short HEAD`.strip
```

以下は明確なデプロイきっかけのエラー発生パターンです。
12/11 22:10手前に縦に伸びるグレーな線がデプロイの完了時点で、そのきっかけでエラーレートが明らかに高まっていることがわかります。

<img width="848" alt="スクリーンショット 2017-12-16 23.30.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/30355/a6afc231-348d-d8fc-c946-346433ea05fc.png">

### CPU、メモリ、ネットワーク、ストレージなどのリソース利用状況

こちらはNewRelic Infrastructureを利用しています。
Applicationを軸に監視するAPMとちがってServer Hostを軸に監視するInfrastructureは以下の目的で利用しています。

* バックグラウンドジョブを処理するサーバーが適切にCPUやメモリを利用できているか？
    * インスタンスが起動しすぎている、大きいディスクをマウントしすぎている、などコスト的な観点から無駄がないか？
* AWSの各サービスのリソース利用状況もIntegrateし、Insight上で常に一つの画面ですることができる

また、APM側のApplicationの情報とも連携されているので、お互いに行ったり来たりしながら障害発生時の情報源としても利用しています。

## 改善のためのアクティビティ

### Alert

NewRelicによるモニタリングで情報を自動収集・把握し、人間が対応すべき場合にはアラートを送り即時対応します。

* Error Rateが閾値を超える
* Apdexが閾値を下回る
* リソースが不足し、閾値を超えている

また、NewRelicだけでなくCloudWatchの各種メトリクスもAlerm設定し、合わせてSlackの特定チャンネルに流し、エンジニアがすぐに状況を確認し詳細を確認後、必要な対応を取ります。

### Issuing, Analyze

### デイリーチェック

* エンジニアは出社後、毎朝15~30分ほどを費やし、NewRelicのエラーレートやパフォーマンスの推移を確認します。
* デイリーチェックではピンポイントで問題を確認するのではなく、日々の変化を俯瞰的に見ることで、状態が徐々に変化していないかを確認します。
* 問題が発生している場合はGithubにISSUE登録するか、すぐに周りのエンジニア・POと相談してバックログとして登録します。

### ウィークリーチェック

* 毎週金曜日に、NewRelicを見ながらその週のデプロイ内容を振り返りながらパフォーマンスやエラーレートの変化、DBのスロークエリを確認しあい、認識のすり合わせがなされます。
* その結果、改善が必要な箇所を再確認できたり、時には設計の見直しやプログラミングガイドラインにまで発展するような議論もでき、非常に学びの多い時間です。

#### 障害報告

* 万が一障害が発生してしまった場合には、ウィークリーチェックの時間を利用して、対応者が障害内容の共有と原因の報告を行い、その対応が適切であったかをエンジニア全員で確認します。
* 障害報告と確認のフォーマットは昔の上司が書いているこちらを参考にしています。
    * [エンジニアなら知っておきたい障害報告&再発防止策の考え方 - Qiita](https://qiita.com/hirokidaichi/items/f9f4549c88aaf8b38bda)

# おわりに

少し長くなってしまいましたが、今回の記事のように「[みてね](https://mitene.us)」はこれまでインフラ専任エンジニアを置かず、アプリ開発エンジニアが積極的に自分たちが開発したシステムを動かすインフラに介入しつつ、アプリ開発エンジニアでも安定運用できる体制を意識的に作ってきました。

インフラ管理・運用に対するコスト支払いは人的なものにではなく、特にNewRelicの利用代金として全力投資し、開発者はできる限りユーザーの課題を解決するほうに集中できるようにしてきました。

ひとまず2017年はサービスの大幅な成長を果たすこともできましたし、それを支えるシステムを運用した実績もでき、ホッとしています。

2018年は果たしてどういうことになっているかはわかりませんが、今後とも「[みてね](https://mitene.us)」をよろしくお願いします！