エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

エムスリーのサービスレベル監視基盤を構築した話

こんにちは、エンジニアリングGの高橋です。

去年の11月にエムスリーにSREとして参画してから、サーバのセットアップ作業などの基本的なインフラ作業に加えて、各サービスのサービスレベルの設定や監視の仕組み作りなども行ってきました。

今回はそのサービスレベルを監視する仕組みをご紹介したいと思います。

本稿の流れ

  • SLI設定
  • SLO設定
  • 各種メトリクスの収集
  • アラーティング
  • 監視ダッシュボードの作成
  • まとめ

全体像

f:id:tshohe:20180823153223j:plain

ざっくりとした全体像としては上図のような感じです。

また、この取り組みを実施した前後で、下のような変化(効果)がありました。

    1. ログの収集はしているが、全サービスでは取れていない
    2. ログの収集経路がサーバによって異なる(Service AからElasticsearchとかもあったり)
    1. 全サービスのアクセスログを収集・閲覧可能
    2. ログの収集経路を共通化
    3. ほとんどのサービス(70以上)のサービスレベルを規定
    4. サービスレベルの超過監視が出来るようになった

SLI設定

まず決めなくてはならないのはサービスレベルの指標(Service Level Indicator)を何にするかですが、弊社では稼働率HTTPリクエストのレイテンシを主なターゲットとしました。

あまり多すぎると初期設定が大変なので、最低限だけ設定し、必要に応じて後から追加するという方針にしています。

SLO設定

最初にどれくらいの値を設定するかは結構悩みどころだと思います。

試験的に監視を開始した数サービスでは現在のSLIの値を参考に決めていました......が、それを全サービスに対してやっているとあまりにも時間がかかりすぎるので、とりあえず一律でざっくり決めて、3ヶ月~半年程度の間隔で開発陣とSREでミーティングを設けて修正していくという形をとっています。

最初に設定したSLO

  • 稼働率
    • ヘルスチェック用のURLへのリクエストが99.9~99.99%以上200応答を返すこと
    • メール送信サービス等のアプリケーションの稼働時間がそれほど重要でないものに関してはエラー率等別の指標を検討中
  • HTTPリクエストのレイテンシ
    • トップページ or 全URL の 98~99% percentile が 1000ms 以下
    • サービスごとに重点的に監視したいURLがあれば別途追加

各種メトリクスの収集

次にどうやってSLIとして定めた値を収集するかをご紹介します。

稼働率

弊社ではサービスの死活監視ツールとしては下記2つを主に使っています。

  • Nagios
    • 各サーバの死活監視用
  • NodePing
    • 社外からのURL外形監視用
    • とても安い(いいところ)
    • レスポンスタイムも取れますが、アクセス元によってばらつきがあるので未使用

NodePingからの稼働率収集

この手のURL外形監視サービスでは大体あるのではないかと思うのですが、uptimeを返してくれるAPIがあるので、取得期間を絞って定期的に取得してElasticsearchに格納していきます。

https://nodeping.com/docs-api-results.html#uptime

Nagiosからの稼働率収集

NagiosにはAvailability Reportという機能があり各監視対象の稼働率を表示することが出来ます。
が、割と枯れたツールなのでJSON等の扱いやすい形式でデータを返してくれるAPIがなかったりします。(いくつかREST APIプラグインはあるようですが、Availability Reportは対応していなかったり)

こんなのです f:id:tshohe:20180823152457p:plain

じゃあ作ればいいじゃん?という話ではありますが......簡単に実現したいので、力技ではありますが、PythonのBeautifulSoupでHTMLをパースしてState OKのTimeを取得しています。

    soup = BeautifulSoup(res.text, "html.parser")
    elements = soup.select('.serviceOK')[3:]
    pattern = r'[0-9]+'
    lists = re.findall(pattern, elements[0].text)
    lists[0] = int(lists[0]) * 24 * 60 * 60
    lists[1] = int(lists[1]) * 60 * 60
    lists[2] = int(lists[2]) * 60
    lists[3] = int(lists[3])
    uptime = sum(lists)

HTTPリクエストのレイテンシ

基本的にはNginx/Apache/Play等のアクセスログからリクエストのレイテンシ(レスポンスタイム)を取得しています。 各サーバにはログを送付するためのFluentdが導入されており、ltsv形式で出力されたログを中継用のFluentdに送付しています。

また中継用のFluentdでは各種フィルタリング処理や、長期保存用のインデックスを作成するためのサンプリング処理を実施しています。

下記はrawの付いたタグのイベントを間引いてsampleを生成している処理

  @type sampling_filter
  interval [アクセス数に応じたサンプリングレート]
  remove_prefix raw
  add_prefix sample

サンプリングすることで精度は下がりますが下記のメリットがあります。

  • データを長期間保存してもデータ量がそれほど肥大化しない
  • 集計時のElasticsearchの負荷軽減(低リソースのVMで動かしているので大事)
  • フィールドが元々のログと同様であるため、グラフを使い回せる

一定期間で集約した値を蓄積していく方法もあるとは思いますが、Kibanaでは加重平均のグラフを作成することが困難なため採用しておりません。

例:
02:00-03:00 アクセス数: 1, レスポンスタイム5000ms
03:00-04:00 アクセス数: 100, レスポンスタイム: 1000ms
-> 平均が3000msになってしまったり...(アクセス数に応じた重み付けが必要)

アラーティング

下記のようなアラートは存在しましたが、

  • Nagios/NodePing/Warder*1/CloudWatch/Prometheus+Grafanaによる障害時アラートメール
  • 定期的なレスポンスタイム監視スクリプトによるアラートメール

これらに加えて、過去5日間の範囲でSLOを超過していないか監視するようにしました。

方法としては定期的にPythonでElasticsearchに稼働率やレスポンスタイムのパーセンタイル値を求めるAggregationクエリ(集約関数のようなもの)を実行し、結果がSLOを超過している場合はIncomming WebhookでSlackに通知するようにしています。

各SLOは下記のような感じでyamlファイルで管理

  xxx:                      # サービス名
    channel: xxx      # 投稿先Slack Channel
    dashboard: xxx # 監視ダッシュボードのURL
    slo:
      xxx:                          # 各SLI固有の名称(~avail等)
        template: xxx.j2     # Elasticsearchへのクエリのテンプレート(jinja2)
        index: xxx               # クエリ対象のインデックス
        operator: "<="        # 下記valueより大きいのか小さいのか
        value: 1000             # SLOの値
        var:                          # Elasticsearchのクエリに埋め込む変数値
          query_list:
            - 'uri: [任意URL]'

各開発側のチームとの連絡用Channelにはサービスごとに通知。 f:id:tshohe:20180823173911p:plain

SRE用のChannelには超過状況を一覧出来るメッセージを通知します。 f:id:tshohe:20180823164904p:plain

監視ダッシュボードの作成

状況を一覧するために、全サービスのSLO超過状況を横断的に見るダッシュボードをKibanaにて作成しました。

右上のSLI Selectorというところでサービスと指標を指定するとグラフが絞り込むことが出来ます。

f:id:tshohe:20180823164937p:plain

過去情報はひと月ごとに集計した情報をTimelionという機能を使ってグラフ化しています。
Kibana赤線がSLOで、青線がSLIの値となっています。
※ 下図はMR君というサービスのApacheのレスポンスタイムの値を描画しています。

f:id:tshohe:20180823164952p:plain

Timelion記載例

$sli=.es(index=[月ごとの値を格納したインデックス],split=indicator:1,metric=max:sli),
$slo=.es(index=[月ごとの値を格納したインデックス],split=indicator:1,metric=max:slo),
($sli).lines().label("SLI"),
($slo).lines().label("SLO"),
($sli).if(gt,($slo), $sli, null).points().color(red).label(""), <- SLO超過時に赤丸をつけて強調する
($sli).trend().label("Trend") <- 線形回帰トレンド

まとめ

上記取り組みにより、簡単にではありますが各サービスのサービスレベル監視とアラーティングの仕組みを作ることが出来ました。
ただ、一番重要なのはSLOを超過しないようにすばやく改善施策を決めて実行していく体制作りだと思うので、そのあたりを進めていけたらと思っています。

またMicroservice化したサービスでは、単一のアクセスログでは、全体のレイテンシが把握しきれないものが多く、現状としてはあまり細かくSLOを設定できていないです。 一部サービスではzipkinやX-Rayを利用して分散トレーシングしていますが、今後はそれらをより強化していきたいと考えています。

We're Hiring!

弊社では一緒に働いてくれるインフラエンジニア/SREを絶賛募集しております!

  • GitLabやJenkinsを用いたCI/CDの実践
  • Fluentdなどを使ったログ収集、管理の技術
  • KibanaやGrafana, CloudWatchなどによるシステム指標の可視化技術
  • AnsibleやTerraformを用いたInfrastructure as Codeの実践スキル
  • オンプレミスとパブリッククラウド両方に関わるシステム設計、構築、運用の経験
  • Microservicesシステムに対する監視や運用の技術
  • DockerやECSを用いたシステムの構成技術や運用ノウハウ

上記のようなことに少しでもご興味が湧くようでしたら、 ぜひ下記フォームからお気軽にエントリーしてみて下さい。

jobs.m3.com

*1:M3の内製監視ツール、Nagiosが対応していないプロトコルのヘルスチェックを担当