RoomClip 開発者ブログ

TV番組「今夜くらべてみました」でRoomClipを紹介されて得られたたくさんのこと

みなさんこんにちわ。 ルームクリップ株式会社CTOの平山です。 残暑が厳しい日々ですね。

昨日の9日、表題の通り日本テレビ「今夜くらべてみました」でルームクリップが紹介されました。 ありがたい話です。

いつだってユーザさんに触れてもらうのは大変に喜ばしいことですが、 大きな衆目を集めるシーンでの言及となると、ちょっぴり特別な興奮がありますね。

これが「エンジニアとして」、となるとなおさらです。

なんてったって巨大トラフィックが襲撃してくるかも知れないわけです。 この手の興奮は上手にコントロールして、常に冷静に対処し、万全の対策で迎え撃たなければなりません。 ご多分に漏れず今回も我々は相応の準備をもって立ち向かいました。

というつもりでしたが、、、

結果として、今回はもっと興奮しておくべきでした。

TVは、

「今夜くらべてみました」という番組は、

私の想像のはるか上空を飛び、ずいぶん時間がたちこのブログを書いている今ですら、おおきな影響を及ぼし続けております。

本ブログでは、「こんくら」メンバーの影響力を甘く見ていた一人のエンジニアの猛省と、 これから同じ轍を踏むかもしれないすべてのエンジニアに向けての転ばぬ先の杖を記していこうと思います。

中には「え、こんなの当たり前でしょ、むしろなんでやってなかったの?」ってものもあるかと思います。

わかっております。不安にならずとも皆様の手厳しいご意見は、これ以上ないくらい私の脳幹をえぐっています。

ただ、時には包み隠さないことも大事です。

わたしは後ろ暗さを抱えて危険なシステムを運用しているすべてのエンジニアの味方です。 「正しい運用しようぜ」の流れを全力で社内に引き込むための道具として、ぜひ本ブログをお使いください。

その前に…

改めまして、今回突発的に発生しましたアクセスに対応しきれず、長きに渡ってサービスに障害を起こしてしまいましたことをこの場を借りて謝罪いたします。 期待をもってアクセスしていただいたすべてのユーザの皆様、また、すべての関係各位の皆様に対し大変にご迷惑をおかけいたしました。重ね重ね申し訳ありません。

以下、専門的な話になりますが公開できる限りにおいて、障害の経緯を共有させていただきます。

概要

簡単に言ってしまうと下記のようなことが起きました。

  • TV局から連絡を受けて流入トラフィックを推計した
    • 余裕で外れた
  • 単一障害点はスケールアップで大丈夫だろうと高をくくった
    • 余裕で外れてそいつのせいで全部落ちた
  • 分散しまくればなんだって耐えうるだろうと甘く構えた
    • ネットワークリソースについて忘れてて分散できず落ちた
  • 対策範囲なんてアプリケーションサーバとDBくらいだろ、って思った
    • バックエンド色々マイクロ化したため対策ポイントが増えててんやわんやになって気分が落ちた

一つでも「あ、これは対岸の火事じゃないぞ」と思ったエンジニアは今すぐ点検しましょう。 単一障害点は本当に危険です。当たり前ですが大事なことです。 「当たり前のことができていない」というケースは「当たり前に多い」と思ってます。 心当たりあるエンジニアは、今日だけは責めないので、そっと確認しましょう。

それでは、一つ一つ見ていきましょう。

TV番組のトラフィック見積もり

一般的には視聴率と世帯数から視聴者数を概算して母数を求め、 その後に「サービス言及中に見ている人の割合」とか「その時間帯に検索する人の割合」とかを適当に見積もって積算して当たりをつけるものだと思います。

大事になるのは瞬間最大風速req/secなので、秒単位で平坦にならすのではなく、適当にピーク分布を積算して出すのがまぁ妥当かと思われます。

今回わたしが見誤ったのは、「その時間帯に検索する人の割合」でした。 番組の性質上、検索やサービス起動を促す作りになっていて(これは本当にすばらしい!)、思った以上のリクエストが発生しました。数値でいうと2倍程度見誤りました。

でも、2倍です。

皆様、「危険だな」と思ったら妥当な計算のあと、「2倍」しましょう。

ちなみに私は、放送直前に「チュートリアル徳井さんがサービス名を発話するかも」という情報を追加で得ました。(このあたりの連絡網を強化することも必須ですね!)

この時点で実はかなり迷いました。今の対策が不十分である可能性がよぎっていたのです。

・・・皆さん、虫の知らせがやってきたら迷わず「最大値の2倍」にしましょう。

単一障害点への対策

これが本当に悔やまれるポイントです。 どの本にだって書いてあることです、こういうボトルネックは障害時にも影響を波及させないように対策しておけと。 それができないなら、どんなアクセスにも耐えられるような設計にしておけと。

ところで、RoomClipはiOS、Android、ブラウザ向けにサービスを展開しており、そのバックエンドとして概ね下記のようなクラウドサービスを利用しています。

  • アプリケーションサーバ/ストレージサーバとしてAWS
    • EC2、ECSを併用
    • DBはAurora
  • 画像配信/変換サーバとしてさくらインターネット
  • 検索サーバとしてElastic Cloud

共有リソースに対するAPIアクセス、が多発するような状態と言えるでしょう。

かつてのルームクリップにおいて負荷障害発生時の犯人の9割はDBでした。 よって我々はDBに対してどんな奇行種も入ってこれない防壁で守っておりました。 実際今回のアクセスに対しても唯一と言っていいくらい割と大丈夫だったのはDBです。 皆さんもそこは大丈夫だと思います。

問題は、かねてよりバックエンドで進めていたマイクロ化にあります。 特に検索サーバ・画像配信サーバ、このあたりはサービスの隅々で使われている重要な機能なので特別に本体設計から切り離し、大事に保守メンテしていました。それはそれでいいことだと思います。

だが、そのAPIの呼び出し部分が悪かった。

一部のページにおいて、APIのレスポンスをタイムアウトせず待つ、帰ってこなければExceptionを投げエラー終了する、というコードになっていました。

運命共同体コードです。

ぞっとしますね。

本当に反省しています。

本来であればサービスメッシュやenvoyレイヤのように、API接地面の安全な設計は暗黙的に処理する予定でした。 しかし、そのレイヤについてちゃんと決めきることを少し後回しにて、とりあえず(後からどうとでもなるような形で)切り離すということを優先していました。 これはこれで限られたリソースや検証という意味ではメリットのあることだと思います。 ただ外部環境は待ってくれません。 TV番組の話は突然来ますよ。 「まぁ一旦これでいって、ある程度わかったらちゃんと固めてこうや」 これは悪くない選択肢です。 ただ「リスクを受容した」ということはもっと強烈に認識すべきでした。

皆さん、モノリシックなシステムをDDD的思想でもって妥当な範囲でバラしたく気持ちはわかります。

しかし、いつでも想定しておいてください。

来週、あなたのサービスについてチュートリアル徳井さんが言及するかも知れないことを。

想定外のネットワークリソース枯渇

さて、異常を察知して直ちにリソースの水平スケールをアップした我々ですが、そこで地団駄を踏むことになります。

インスタンス・コンテナが全く増えません。

ルームクリップのAPIサーバ、Webサーバは基本的にECS(一部EC2)という状況だったので、オートスケーリンググループをいじれば続々と援軍追加されるはずでした。 が、いくらたってもエラーで立ち上がりません。AWSリソース制限かとも思ったのですが、つい最近あげたばかりです。

いろいろ調査してわかったことは、サブネットのPrivateIP枯渇でした。事情あってサブネットの許可IPのCIDRを厳しくチューンしていたため、ここの上限にぶつかってインスタンスの立ち上げができなくなっていました。

サブネットの設定なんてそうそう変更しないので、すっかり忘れておりました。 皆さんもそらで言えますか?あなたのサブネットは第何オクテットまでマスクしていますか?

あ、ごめんなさい、そらで言える必要は全くありません。確認しに行けばいいだけです。 ネットワークに限らず「リソース枯渇」系の話は結構緊急時に人をイラつかせるものです。

例えばnlimit、inode、エフェメラルポート、帯域制限、もちろんHDD容量。そして今回忘れてならないIP。

これは激痛で、リカバリの速度がだいぶ遅れた原因の一つとなってしまいます。

対策点の増加

先に触れたマイクロ化の流れもそうですが、BFFなども積極的に導入した結果、スケール対象となるインスタンスの塊は結構増えておりました。

端的にALBの数が多いんですね。

これは本質的にはそこまで問題ではないことなのですが、少ない人数でこれらのスケールアップを管理するというのは中々ストレスがたまります。 事前準備はまぁいいんですが、障害中「やべぇ!」となったときのあっちゃこっちゃいじる千手観音っぷりは悲惨です。 いろいろな対策がありえますが、Lambdaなりでサーバレスに切り替えていくのは非常に良い選択かもしれません。

とにかくウォームアップなんていらない!というのは大正義です。

まとめ

非常に長くなってしまいましたが、簡単にまとめますと、今回の障害の大きな原因はやはり単一障害点の対策不備になります。

RDB周りは特に手厚く対策されると思いますが、(当たり前ですが)転置インデックスを扱うサーバや、DynamoやRedisといった領域についても、「手厚くするか、緊急時に被害を軽微にするような設計にする」など、最低でも波及を抑える設計にすべきでした。

また、外部APIを使うときの接地面の設計(リトライ制御やブレーカーなどを含む、一般にenvoyレイヤなどで処理される諸問題)も非常に繊細になされるべきだということを改めて実感しました。

これ以外にも気づけなかった問題は多々ありますが、本ブログではひとまず猛省しつつ「速報」という形での共有とさせていただきたいと思います。

また、これからバックエンドをマイクロ化していくプレーヤーは多数存在していると思いますが、ぜひ改めて「管理すべきドメイン」が増えていくことにより、それぞれの障害点への対策やスケーラビリティの保証などが手薄にならないよう、気をつけていただけるとよいかと思います。

色々と課題はたくさんありますが、日々精進し、これからも皆様の日常にある「創造性」を応援して参ります。 何卒末永くルームクリップをよろしくお願い申し上げます。 改めまして、この度の大規模な障害、大変申し訳ありませんでした。

そして、本ブログを読んで、 「しかたない、、わたしがなんとかしてあげよう」と思うエンジニアの方、ぜひ遊びに来てください!

フロントもサーバサイドも!生産性の高いサービス開発基盤を作りませんか?



この記事を書いた人:平山知宏

ルームクリップ株式会社のCTO。 インフラからサーバサイド全般担当。 インダストリアル系の家に住みたいと思うけど、子供もできちゃったし危ないから工具とかほっぽっとくのやめようと思っている1985年生まれ。


Amazon SageMakerとSageMaker Ground TruthでRoomClipの画像から物体を検出してみた

TL;DR

部屋の実例写真の中から特定の商品を機械学習により検出する実験をしてみた。
SageMakerの使い勝手を知るために、ラベリングにはSageMaker Ground Truthを、モデルはSageMakerビルトイン物体検出アルゴリズムを用いた。
その中で得られた知見のまとめ。

イントロ

RoomClipで機械学習周りの業務を中心に取り組んでおりますインターン生の松本です。

RooomClipにはユーザーの皆様から投稿していただいた素敵なお部屋の写真や、アイテム・商品の実例写真がたくさん蓄えられております。
お部屋の実例写真が360万枚以上、という世界的にもレアなデータセットを抱える弊社では、現在このデータセットから新しい示唆を得るためにここ数年話題の機械学習を導入するプロジェクトを進めています。
その第一段階として、分析環境をどうするのがよいかを検討するため、現在AWSが推しているサービスの一つであるAmazon SageMakerを試しに使ってみようということになりました。

今回はSageMakerを用いる題材として、ユーザーの皆様から投稿していただいた画像から特定の商品を検出するというお題に取り組んでみました。
機械学習における物体検出アルゴリズムを回すためには、「画像中のこの領域がこの物体である」という教師データを作成するアノテーションと呼ばれる作業がつきものですが、そのツールとしてAWSのマネージドサービスであるAmazon SageMaker Ground Truthを採用してみました。
物体検出アルゴリズムそのものは、SageMakerのビルトインアルゴリズム(SSD)を使用しました。
これらを連携させて使用していく中で、便利な点・不便な点などが見えてきたので、得られた知見についてここで共有していきたいと思います。
実際にサービスを使用する方の参考になればと思います。

本記事の情報はあくまで2019年5月末時点でのもので、AWSのサービス(特にSageMaker周り)は相当早いペースで顧客からのフィードバックを受けながらアップデートで強くなっていくので、常に最新の情報を追っていただく必要があるという点についてはここで一応言及させていただきます。

本記事で扱うこと

  • Amazon SageMakerの使い方の概略
  • Amazon SageMaker Ground Truthでラベリングジョブを作成
  • ラベリングの結果を用いてSageMakerの物体検出アルゴリズムを学習

Amazon SageMakerの使い方の概略

https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/whatis.html
サービスの詳しい説明自体は↑をご参照いただければと思います。
ざっくりとした理解としては機械学習のワークフローを

  1. ノートブックインスタンス
  2. トレーニングジョブ
  3. エンドポイント

という3つの形で提供するサービスです。
1つのサービスを使用するために残り2つも使わなければならないというわけではなく、どれか1つだけ使用するといった使い方にも対応しています。

ポイントとして、ノートブックインスタンスはその名の通りjupyter notebook(or jupyterlab)をクラウド上で使用するためのインスタンスになりますが、SageMakerの本来の使い方としては、このインスタンス上で学習を行うわけではありません。(もちろん、Pythonの実行できるのでノートブックインスタンス上で学習させることも可能)
実際の学習はSageMaker Python SDKなるライブラリを用いて、トレーニングジョブと呼ばれる「学習用コンテナ」をノートブックインスタンスとは別に立ち上げ、その中で行います。
その際の学習・推論データの置き場所はS3のPathを指定することになります。
また、学習したモデルをデプロイする手段として「エンドポイント」を立ち上げることができます。推論タスクはトレーニングジョブとは別の「推論用コンテナ」を立ち上げることで、その中で行います。
一般的に、推論には学習の時に必要となるほどのマシンリソースを要さないので、軽めのインスタンスで問題ありません。

Amazon SageMaker Ground Truthでラベリングジョブを作成

SageMakerについての簡単な説明を終えたところで、今回の実験の手順の話に入ります。
今回は画像に対する「物体検出アルゴリズム」を使用していくので、アノテーション作業を行う必要があります。そのアノテーション作業環境がAmazon SageMaker Ground Truthというサービスの一環としてAWSから提供されています。

Ground Truthでアノテーションを行う際には「ラベリングジョブ」を立ち上げることになります。
このジョブに「ワーカー」を招待することによってチームで分担してアノテーション作業を行なっていくことができます。
アノテーション作業を外注したいというケースにも対応しており、コンソール画面から直接ベンダーにアノテーション作業の発注を行うことができます。また、自動ラベリング機能も搭載されています。
確実性の面では全て手動で行うのが良いので今回は自動ラベリング機能を使用せず、自分たちで直接アノテーションを行いました。
使用して、良かった点と改善が望まれる点を整理してみました。

良かった点

  • 動作が軽快で作業を快適に進めることができる
  • 数人がかりで同時にアノテーションを行なってもデータが散らからない
  • コンソール画面からラベリングジョブの立ち上げがとても簡単に行える

改善が望まれる点

  • 出力の形式が.manifestに限られる
  • ラベリングジョブを終了しないとその時点までのアノテーション結果を、最終結果のフォーマットで出力できない
  • 一度ラベリングジョブを途中で終了するとジョブを再開できない
  • ある終了済みラベリングジョブAのoutput.manifest(出力結果)を入力として新たなラベリングジョブBを立ち上げる「チェーン」という項目があるが、Aにおけるアノテーションの情報はBに引き継がれない

吐き出される結果の.manifestファイルはjson形式のデータが改行区切りで並んだもので、
各行(各オブジェクト)は一枚の画像のアノテーション情報を含んでいます。
SageMakerビルトインアルゴリズムと相性がよいように作られていますが、一般的にgithubなどで公開されている物体検出アルゴリズムはVOCデータセットの形式(xml)かCOCOデータセット形式(json)のアノテーションファイルの存在を前提としている場合が多く、.manifestから変換するのは若干厄介です。
物体検出用によく使われるアノテーションファイルの書式に対応してもらえると大変助かるなと思いました。

ラベリングジョブが再開できない、といった仕様によって次のようなケースで困ることがあります。

「ある物体を検出するために300枚アノテーションすれば十分かな?と思ったが、足りなかった時のために多めに600枚の画像をプールしてラベリングジョブを立ち上げた。 ↓ 300枚分のアノテーションが終了したのでこの時点で学習を実行してみたい ↓ output.manifestラベリングジョブ終了する必要がある ↓ 枚数が足りなかった場合アノテーションを一からやり直し?」

このあたりは不便に思っているユーザーは多いと思うので、さほど遠くないうちにアップデートで解決されるのではないでしょうか。
細かい点での不便は感じつつも、チームでラベリングを行う場合にはGround Truthは非常に快適な環境を提供してくれるので、今後のアップデート次第ではスタンダードとして定着する可能性のあるサービスだと思います。

ラベリングの結果を用いてSageMakerの物体検出アルゴリズムを学習

トレーニングジョブを立ち上げるには

  1. SageMakerコンソール画面から設定
  2. SageMaker Python SDKを使う

という方法の選択肢がありますが、前者はあまりオススメしません。
その理由としては

  • 物体検出に限らず、GUIでトレーニングジョブ 立ち上げ〜エンドポイント立ち上げまで行ってしまうとデータの形式などが何もわからないまま進んでしまうため、立ち上げたエンドポイントにどんな形式でPOSTすれば求める出力が返ってくるのかが見えづらくなる。
  • ドキュメンテーションにたまに誤りがあり、GUIコンソールだとデバッグの手立てがない

ことが挙げられます。
というわけで、SageMaker Python SDKの使用がオススメです。
https://sagemaker.readthedocs.io/en/stable/
↑readthedocsのドキュメントはかなりわかりやすく書かれており、痒いところに手が届きます。

また、トレーニングジョブの立ち上げはノートブックインスタンスを使用する必要はなく、Python SDKとaws cliをインストールすればローカルマシンの実行環境から立ち上げることが可能です。
学習に使用するデータを細かく手元でいじりたいという場合にはローカルマシンを開発環境とすることをお勧めします。
(特に画像が絡む場合FinderなどのGUIファイル管理システムが使えた方が便利)

Python SDKを使用する場合、一からドキュメンテーションをみながらコードを書くのは骨が折れるので、AWSが用意している以下のリポジトリを最大限活用しましょう。
https://github.com/awslabs/amazon-sagemaker-examples
SageMakerのよくある使い方について、テンプレートとなるipynbファイルが豊富に用意されています。
これを適宜ご自身の目的に合わせてmodifyしながら使っていくのが一番早いかと思います。

今回のように、.manifestファイルから物体検出のトレーニングジョブを生成する場合↓の後半部分を参考にされると良いと思います。
https://github.com/awslabs/amazon-sagemaker-examples/tree/master/ground_truth_labeling_jobs/ground_truth_object_detection_tutorial

実際に検出してみた結果

今回は1種類の物体(電子ケトル)のみを検出する実験を行いました。
学習により作成したモデルを用いて、学習に使用していない画像約150枚について推論させてみたところ、結果は以下のようになりました。
なお、検出のconfidence threshold(閾値)は0.5としています。

正しく検出されたケトル ・・・ 111個
検出漏れしたケトル ・・・ 38個
誤検出 ・・・ 7個
ケトルが写っておらず、検出もされなかった枚数 ・・・ 19枚

写っている領域が小さい場合はなかなか難しそうだと思っておりましたが、小さく写っていても難なく検出できている画像も存在します。
↓検出成功画像例

逆に、検出できなかった写真の中にも、がっつりケトルの全体像が写っているものも存在しているのが不思議ですね。
↓検出漏れした画像の例

以上から、簡単に代表的なモデルの評価指標をサクッと計算してみます。
①Precision(適合率)
これは「モデルが検出した物体のうち、何割が本当にその物体であったか」を意味する指標になります。すなわち、
Precision = (正しく検出されたケトル)/(正しく検出されたケトル+誤検出) = 0.941
と計算できます。
モデルが検出した物体のうち94%は実際に検出を試みたケトルであったということになります。

②Recall(再現率)
これは「実際に写っていたケトルのうち、いくつが検出されたか」を意味する指標になります。すなわち、
Recall = (正しく検出されたケトル)/(正しく検出されたケトル+検出漏れ) = 0.745
74.5%が検出できたということになりますね。

③F値
これは適合率と再現率の調和平均になります。0~1の間の値をとり、1に近いほど良い値となります。(F値=1となるのは適合率も再現率も100%のとき)
すなわち、
F値 = 2 * (Presicion * Recall)/(Precision + Recall) = 0.831

(補足:本来は物体検出の場合mAPという平均適合率を計算することで評価するべきではあります)

このモデルの私たちの使用用途としては、「膨大な画像データセットの中から目的のものを見つけ出す」という使い方になるかと思います。
それを念頭においたとき、「見落とし」の多さはさほど重要ではなく、「モデルが検出した画像が間違った物体を認識していないこと」が私たちにとっては重要な指標となります。
それを考えると、SSDは現在最新・最良のアルゴリズムではないとはいえ私たちの目的には十分役立つものである可能性があります。
ただし、その評価には
 ・今回のデータセットは特定のケトルのタグがついた画像をクエリで取得することによって形成したものですが、条件を指定せずに適当に持ってきた画像の中から正しくケトルを検出できるか
・他社製の別のケトルとラベルを貼り分けて学習させることによって、「一般的なケトル」を検出せず「特定のケトル」を検出するに至るか
といった検証を追加で行う必要がありそうです。
今回はAWS SageMakerを用いたワークフローの確認がメインの目的であったため、簡素な検証に留めました。

まとめ

いかがでしたでしょうか。
SageMakerビルトインアルゴリズムはSSD(Single Shot MultiBox Detector)というアルゴリズムを基にしており、現在最良とされている物体アルゴリズムではありませんが、そこそこの精度で物体を検出できます。
そして、AWS上で提供されるモデルの学習およびデプロイを行うためのワークフローはとてもスムーズでした。
ただ、少し情報がまとまっていなかったり不足している部分があると感じていたので、本記事ではこれからSageMakerを使う人向けに補足情報をまとめてみました。
TensorFlowやPyTorchの書き方やその裏の原理を把握していなくても、サービスとしてある程度のAIシステムが導入できてしまうのは非常に画期的だと思います。
気になっている方がおりましたら試しに一度使用してみてはいかがでしょうか。


この記事を書いた人:まつもと

RoomClipで機械学習周りでインターンしてます。