tvOS入門」でした。こんにちは、AbemaTVサーバサイドエンジニアのギアです。去年は新卒のiOSエンジニアとして、「ReactiveCocoaとMVVMモデル」という記事を書きましたが、今年はサーバサイドに関することを書きます。StatusCakeというサービスを使っています。上のリソースに対するメトリクスしかありません。各マイクサービス間の通信やサービスのカスタマイズメトリクスなどのアプリケーションレベルのメトリクスも収集し、監視したいので、良い時系列データベース、良いモニタリングサービスを探しました。Databasesの記事を読みました。この記事では現在有名な時系列データベースの比較について述べました。スプレッドシートに掲載されています。それらの時系列データベース中にDalmatinerDBは一位ですが、我々はAbemaTVにPrometheusの導入を選択しました。今日Prometheusを選択した理由も含めて、Prometheusで何ができるか、AbemaTVでどのように設定・利用しているか、Prometheus内部のアーキテクチャまでも話したいと思います。Prometheus紹介Prometheusは、オープンソースのサービスモニタリングシステムです。開発は音楽のソーシャル・プラットフォームを展開しているSoundCloud社によって2012年から行われています。現在の最新バージョンは1.4.1です。Githubでのリポジトリを見てみると、現在でも活発に開発されているようです。Prometheusの特徴としてまずはメトリクス収集方式としてPull型を採用していることです。ほとんどのモニタリングシステムはPush型だと思います。Push型はモニタリング対象サービスからモニタリングサーバにメトリクスをpushするというモデルです。この型だとモニタリング対象が増えてもモニタリングサーバの設定変更などをしなくても良いというメリットがあります。モニタリング対象ではないホストがメトリクスをpushしても受け入れてしまいますし、メトリクスを受け取れない場合に障害なのか、pushを止めただけなのか、判断できないというデメリットが存在しています。逆に、Pull型はそいうデメリットを解決できますが、モニタリング対象サービスを変更するとモニタリングサーバの設定を変更する必要が発生します。PrometheusはHTTP経由でモニタリング対象サービスへ定期的にメトリクスを取りに行くPull型です。Azureなどで動いているサービス対象を自動検知することができ、設定の変更をする必要がなくなります。次の特徴は多次元データモデルです。pair(ラベル)セットから構成されます。handler="/tracksのラベルで識別しています。実際の内部にはメトリクス名も__name__というラベルキーとしてハンドルしているようです。handler="/tracksのラベルセットで識別します。全ラベル集合の中にあるラベルセットから一つの時系列を表現することができます。Prometheusは上記の多次元データモデルを基づいて設計されているので、PromQLという独自のクエリ言語を提供し、ラベルベースにクエリを行っています。多次元データモデルのおかげで、クエリにのフィルタリングやアグリゲーションが簡単にできると書かれています。rateなどのようなクエリに役に立つ関数もたくさん用意されています。Prometheusは時系列データベース以外に、AlertManagerも提供しています。deduplicatingなど設定でき、emailやslackなどの外部サービスに通知できるものです。Databasesによると、DalmatinerDBは一位ですが、今回我々はPrometheusを選択しました。次に選択の理由について説明します。一つ目の理由として、PrometheusはKubernetesの友人です。Kubernetesで動いているPrometheusは設定がシンプルで、下の図のように、アプリケーションレベルからコンテナ、ホストまでの色々なメトリクスを収集することができるようになります。そこで、各メトリクスレベルは別々のモニタリングサービスを利用するのは要らなくなり、すべてのメトリクスはPrometheusで収集を行えます。nodeの自動検知に便利です。Discoveryの役割は非常に重要です。現在AbemaTVではGoogleクラウドとKubernetesを積極的に利用しますので、これは一つの大きな理由です。二つ目はPrometheusの性能です。samples/sまでハンドルできると書かれています。一つのモニタリング対象は800時系列があり、かつ10秒の間隔でメトリクスを収集している場合にも、10000対象まで扱えます。Federationも提供しているので、もっとスケールしたい場合にもすぐできると思っています。データ量の面でも素晴らしいです。8bytes)が必要です。OpenTSDBは12bytes/sampleです。Prometheusは1.3bytes/sampleだけです。圧縮率は12.3倍は素晴らしい数字です。三つ目はPrometheusがPromQLという素晴らしいラベルベースのクエリ言語を持っています。PrometheusはSQLクエリを利用するではなく、独自開発したクエリ言語を使います。「へ、なんでSQL使わないの?」という質問がありましたら、Prometheusの作者は以下の回答がありました。確かに初めて使いますが、クエリ書きやすくて、見やすいです。直近5分間でのrequests/secondを計算するのは以下のようなクエリです。rate(http_requests_total[5m])もうちょっと複雑なクエリを見てみましょう。あるサービスの直近5分間での500系で結果としたrequests/secondをhandler毎にグルーピングすると以下のようなクエリです。(handler)上のクエリで、結果の以下のような感じです。0四つ目はPrometheusとGrafanaは友人です(やっぱり友人は多い方がいいですね)。現在、Grafanaのbuilt-inデータソースはPrometheusが入っていますので、PrometheusのデータをGrafanaで可視化するのは簡単です。五つ目はアラート機能とAlertManagerです。最近Grafana4.0もアラート機能を提供していますが、それを使うとPrometheusへのクエリ数がだいぶ増えるじゃないかな個人的に考えています。Prometheus自身のアラート機能はアラートルールに基づいて、アラート検知を行っています。自分自身の機能なので、内部に何か最適化できているかなと考えて、Prometheusのアラート機能を使ってみたかったです。deduplicatingなどを行った後の結果のみをemailやslackなどに通知します。そこで、同じサービスの似ているアラートをたくさんの通知が来るのを避けることができます。最後の理由としてはPrometheusがGolangで書かれていることです。AbemaTVもGolangを使っていますので、Prometheusの資料に述べていないことがある時やPrometheus内部みたい時などにすぐわかるので、良いと思います。AbemaTVでのPrometheus次はAbemaTVでPrometheusをどいう設定、どいう感じに使っているかについて説明します。GrafanaのコンテナーはGCPの上にKubernetesで管理を行われています。PrometheusはAbemaTVの他のマイクロサービスと同じクラスタ内に動いています。上の図のとおり、Prometheusは自分自身も含めて、クラスタ内の各マイクロサービスからexporterを通して、アプリケーションレベルのメトリクスを収集しています。アプリケーションレベルのメトリクスなので、サービスによって異なり、exporterは自分で実装する必要があります。AbemaTVでは基本的にGRPCのメトリクスのexporter、HTTPのメトリクスのexporter、Batchのメトリクスのexporter、動画処理・配信のメトリクスのexporterなどを作成しました。各マイクロサービスでは必要なexporterを利用し、メトリクスを出力しています。Discoveryを設定しています。kubernetes_name上の設定の大事なところは2つがあります。endpointsです。一つのサービスは複数podsで動いていますので、サービズの全てのendpointsのexporterからのメトリクスを収集するための設定です。relabel_configsの最初の3行です。scrape=trueを設定しているサービスのみをメトリクスを取集することです。メトリクスを取集したいサービスのservice.ymlに以下のようなのannotationsを追加するだけです。"30001"それではアプリケーションレベルのメトリクスの収集の設定は以上です。APIを利用し、新く作成されたpod、削除されたpodを自動的に検知し、収集ターゲットリストを更新します。コンテナーやホストなどの他のレベルのメトリクスのexporterの実装は不要です。収集したいものは上のconfig.ymlに適切なroleを設定し、メトリクスを収集できます。ディスク・メモリの設定まずはPrometheusで必要なディスク量についてです。ディスク量は以下のことに依存しています。必要なディスク量は約26GBです。ラベルなどのメタデータもありますので、少し違いますが、こういう感じで正確に計算できると思います。bytes/sampleなので、10GBぐらいになります。)ディスク量の計算は上のように正確に計算できますが、現在のPrometheusは利用するメモリ量の計算は正確に出来ません。現在のPrometheusではメモリ内に保存するchunk数の制限は以下のパラメータで設定できます。bytesなので、デフォルトでchunkの必要のメモリ量は1GBです。利用するメモリは来たメトリクスの一時保存のためだけではなく、オバーヘッドやPromQLで利用するのも必要となリます。後者の分は現在正確に計算が難しくて、Prometheusの資料によると、トータル必要なメモリはchunkの必要なメモリの3倍ぐらいで設定することをお勧めしています。私のケースは4倍(4GB)を設定してみたんですが、たまにメモリのlimit値を超えて、リスタートされることがあったので、現在7GB設定しています。時系列数によってstorage.local.memory-chunksを変更することが必要です。390000chunksぐらいで良いらしいです。rangeも含めて考えた方がいいと思います。必要になり、storage.local.memory-chunksを上げた方がいいかもしれないです。Grafanaとの連携Prometheus自身でもグラフ化機能を持っていますが、可用性と機能についてGrafanaと比べられないと思います。Grafanaはダッシュボード、テンプレート、アノテーションなどの良い機能があります。今回我々もGrafanaを導入し、Prometheusと連携し、Prometheusのデータをグラフ化しています。GrafanaのダッシュボードのjsonファイルはKubernetesのConfigMapとして扱っています。APIでダッシュボードをインポートするスクリプトを実行させています。以下は幾つかのダッシュボードの写真を投稿しています。AbemaTVでは色々なメトリクスを収集・監視を行っています。左)HTTPのメトリクスのグラフは上のような感じです。Golangメトリクスも便利です。Prometheus自身のメトリクスも収集・監視しています。AlertManagerPrometheusのアラートの機能を使うために、Prometheus側にアラートルール定義が必要です。我々はアラートルールは以下のように、rulesフォルダーに保存し、KubernetesのConfigMapとして扱っています。http_request_duration_quantile.rulesrulesフォルダー内の全てのファイルをConfigMapとしてapplyする時に以下のコマンドで行えます。}}`)",}上のアラートルールで以下のような通知がSlackに来ています。Prometheus内部のアーキテクチャエンジニアにとってすごいサービス・ツールを使ってみるのは楽しいですが、内部の設計・すごかった理由の理解ももっと楽しいではないでしょうか。今回私もPrometheusのコードを読んでみると、勉強になったことが多くて、ドキュメントに述べていないことも理解でき、すごく楽しかったです。記事が長くなってしまいますので、Prometheusのローカルストレージについてのみ書きます。Prometheusの資料を初めて見る時に、自分自身に以下の二つ質問がありました。bytes/sampleまでエンコードできているが、どうやってできるのか皆さんもそんな質問がありませんでしたか。PrometheusはLevelDBとロカールのファイルシステムにデータの保存を行っています。時系列データをメタデータとサンプルデータの2つ種類に分割され、前者はLevelDBに保存し、後者はファイルシステムに保存しています。以下のメトリクスで見てみましょう。126上の3つメトリクスは以下の3つレベルセットから識別しています。service="comment"}各メトリクスのラベルセットからFNV-1A-64bitを用いて、唯一のhash値(フィンガープリントと呼ばれています)を作成します。de86e7778f8ba16c上のフィンガープリントはメトリクスのサンプルデータの保存場所を決定しています。metric_value)ペアをサンプルポイントとして適切なファイルに追加します。152.0000000ポイント1)メトリクスデータの特徴は時間順次に来るので、writeオペレーションは基本的にファイルにappendだけなので、速いですね。Prometheusではサンプルデータを1024bytesの固定サイズのchunkに分けて、保存しています。新サンプルーデータが来た時に最新のchunkに入れて、chunkがfullになったら、新しいchunkを作成し、追加します。メモリ内に各メトリクスは何個かのchunkがあり、storage.local.memory-chunks/number_of_timeseries値を超えると、古いchunkをディスクファイルにappendします。PromQLエンジンはLevelDB内のメタデータを用いて、クエリを解析し、必要な時系列サンプルーデータのchunksをメモリにロードし、演算を行います。ポイント2)固定サイズのchunkでファイル内のchunkにランダムアクセスできるようになります。ここまででPrometheusのデータ管理は理解できましたね。次はデータ圧縮の話です。圧縮が必要なところは基本的にサンプルデータです。bytesで扱っています。