トリプル・ダブリュー・ジャパン(DFree)開発者ブログ

排尿予測デバイスDFreeを開発しているトリプル・ダブリュー・ジャパンの開発者ブログです

2019/08/23のAWS大規模障害が40秒のAPIエラーで済んだ理由

トリプル・ダブリュー・ジャパン株式会社でソフトウェアアーキテクトをしています西田です。ネット上では主にkbigwheelの名前で活動しています。

この記事では先日のAWS大規模障害がトリプル・ダブリュー・ジャパンのシステムへどのような影響を与えたのか、またそれに絡めて監視・アラートのフローやシステム設計について紹介します。

AWS大規模障害

2019/08/23の午後、AWSの東京リージョンのEC2およびRDSにおいて大規模な障害が発生しました。

Summary of the Amazon EC2 Issues in the Asia Pacific (Tokyo) Region (AP-NORTHEAST-1)

トリプル・ダブリュー・ジャパンではDFreeサービスでAWSを利用しており、影響が懸念されたのですが幸いにも40秒程度APIがエラーを返す程度で済みました。

障害が発生したときに実際に発生したこと

日時 発生源 発生したこと
2019/08/23 12:59:29 Mackerel APIサーバが内部エラーにより500 Internal Server Errorを返したことがslackへ通知される
2019/08/23 13:00:12 Mackerel 上記のエラーが解消したことがslackへ通知される
2019/08/23 13:01:30 エンジニア(手動) APIサーバに装備された各DB・ミドルウェア・外部SaaSとの疎通を確認する専用のHealthCheck APIを叩いて正常に動作していることを確認
2019/08/23 13:07:52 エンジニア AWSコンソールおよびCloudwatch Logsからエラー原因がDBとのコネクションにあること、その後DBがフェイルオーバーしていることを確認

たまたま障害発生の瞬間にエンジニア(私)がslackを見ていたため、問題が発生した瞬間から障害対応と問題原因の調査をすることができました。 結果、8分ほどでDBのフェイルオーバーが発生していることを突き止めています。

監視・アラートのフロー

迅速に障害の発生を感知し、原因調査ができたのは監視・アラートのフローがきちんと設定されていたためでした。

DFreeサービスでは監視やアラートについては現在slackの特定チャンネルへ集約するポリシーを取っています。 監視にはmackerelとCloudwatch Alarmを使用しており、Cloudwatch Alarmの発火はchatbotを通じてslackへ集約しています(以前はSQS, Cloudwatch Events, lambdaと非常に手間でした)。 また、クリティカルなアラートとそれ以外のアラートを別々のslackチャンネルへ分けていたため、今回即座に重大な問題だと判断することができました。

なぜ障害発生の時間が40秒で済んだのか

DFreeサービスが利用しているAWSの構成

f:id:triplew-tech:20190828081140p:plain

DFreeサービスは以下のAWSサービスから構成されています。

私達のチームではリージョン内レベルでの冗長化を基本のポリシーとしています。 Kinesis, S3, DynamoDB, Lambda, ALB, SNSについてはマネージドサービスであるため責任共有モデルに従いAWS冗長化を任せており、 ECSでは適切にヘルスチェックとクラスタの最低保証台数を設定することでこれもまた復元をAWSへ任せていました。 RDSについてもMultiAZ設定を有効にすることで2つのリージョンで冗長化しています。

このようにサービスのすべての部分で複数のAvailability Zoneにまたがる冗長化を行っていました。 冒頭のAWSのレポートにも書かれていますが、今回の大規模障害は東京リージョンの2つのAZのうち1つのAZでのみ発生する障害であったため、DFreeサービスでは最小限の影響で済んだと言えます。

ポイント

今回の大規模障害とDFreeサービスでの対応からいくつかのポイントを挙げます。

  1. マルチAZ対応をしていたためAZレベルの障害へ対応できた
  2. 監視・アラートのフローはなるべくどこかへ一元化すること
  3. 重大なアラートとそれ以外のアラートは出力先を分けて、前者の場合は即座に気付けるようにすること
    • 両者を混同すると重大でないアラートに重大なアラートが紛れて対応が遅れるケースがよくあります
  4. AWSのサービスはすべてのログがAWSコンソール上で確認できるため、問題原因の調査が非常に迅速にできる
    • 今回の場合CloudWatch Logs(ECSコンテナのログ) → RDSの「ログ・イベント」確認 のフローでしたが、DBのフェイルオーバーが起こっていることを確信するまで8分程度でした
  5. 今回は特定AZレベルの障害だったが、リージョンレベルでの障害には対応できない

OneLoginとRedashのSAML連携方法

トリプル・ダブリュー・ジャパン株式会社でソフトウェアアーキテクトをしています西田です。ネット上では主にkbigwheelの名前で活動しています。

最近社内ではredashの重要性が増しており、同時にアカウント管理のコストが徐々に増えてきました。 すでに社内ではIDaaSとしてOneLoginが導入されており、redashとのSAML連携ができる環境ができていたのですが一方でSAML連携を一から設定することは難易度が高く、過去複数人が何度か設定に挑戦したものの今まで正しく設定できたことがありませんでした。 今回改めて時間を取って問題解決を図ったところ、どうすれば正しく連携できるかがわかりましたのでこちらで手順を残しておきます。

前提

以下の環境で作業を行いました。

  • 2019/06/19時点でのOneLogin
  • AMIから作成したredash入りのEC2インスタンス(redash.4.0.1.b4038)
  • OneLogin, redash共に管理権限を持っている

連携手順

1. OneLoginでredash用のAppを追加

  1. OneLoginの管理画面に行く
  2. 「Apps」を選択
  3. 「Add Apps」を選択
  4. キーワード「saml」で検索
  5. SAML Test Connector (Advanced)」を選択して追加
  6. Display Nameを入力(何でも良いです)
  7. 「Save」を選択

f:id:triplew-tech:20190619100050p:plain

2. SAML MetadataのURLを取得

  1. 先程作ったOneLogin上のredash用Appの設定画面を開く
  2. 「MORE ACTIONS」を選択
  3. SAML METADATA」を 左クリックせず右クリックからリンクをコピー
  4. コピーしたURLをどこかへ控えておく

3. redashにSAML MetadataのURLを設定

  1. redash管理画面のSettingsタブを開く ( https://{your_domain}/settings/organization )
  2. SAML enabled」をチェック
  3. SAML Metadata URL」へ先程控えたURLをペースト
  4. 右下に保存成功と出ることを確認

4. OneLogin上の自分自身のユーザーへOneLoginでredash用のApp実行権限を付与

以下のテストのため、自分自身のユーザーへ①で作成したAppの実行権限を付与します(付与方法は直接/Role経由/Group経由などどれでも良いです)。 付与したあとにoneloginのポータルに表示されていることを確認すると確実です。

4. OneLogin - Redash連携固有の問題を解決

ここまではマニュアル通りの手順でしたが、ここまで行った後にredashのログイン画面から「SAML Login」ボタンを押すとredashサーバ側で500 Internal Server Errorが発生しました。

f:id:triplew-tech:20190619100956p:plain f:id:triplew-tech:20190619102627p:plain

以下の手順はredashサーバのエラーログから特定した問題の解決法になります。

4.1 redash側から要求されているパラメータrecipientの設定

redash用Appの設定画面のrecipientに次の文字列を設定

http://{your_domain}/saml/callback?org_slug=default
4.2 pyyml2の want_assertions_signed オプションを無効化
ssh {redashのサーバ}
sudo vim /opt/redash/redash.4.0.1.b4038/redash/authentication/saml_auth.py
46行目の
'want_assertions_signed': True,
を
'want_assertions_signed': False,
 に書き換え
4.3 redash側から要求されている属性FirstName/LastNameの設定

RedashでSAML認証できるまで - Cybozu Inside Out | サイボウズエンジニアのブログにも書かれている通りredashは

認証応答メッセージ(Response)のSAMLアサーションの属性ステートメント(AttributeStatement)に、"FirstName"と"LastName"が含まれていることを期待している

です。 OneLoginはこのパラメータへユーザーごとに別の値を設定する仕組みを提供していますが、今回は動作確認のため私のFirstName/LastNameをそのまま固定値で書きました。

f:id:triplew-tech:20190619102837p:plain f:id:triplew-tech:20190619102843p:plain

5. 動作確認

以上を行い再びredashのトップ画面から「SAML login」ボタンを押したところ、正常にログインすることができました。

f:id:triplew-tech:20190619100956p:plain f:id:triplew-tech:20190619103218p:plain

6. ユースケースごとに適切な設定を行う

  1. redashへのアクセスを許可したい人に、OneLogin上のredash用のAppの実行権限を付与
  2. FirstName/LastNameへ適切な値を設定

RFC7807に沿ったエラーレスポンスの実践について

トリプル・ダブリュー・ジャパン株式会社でソフトウェアアーキテクトをしています西田です。ネット上では主にkbigwheelの名前で活動しています。

今回は最近私達のチームで行ったRFC7807に沿ったエラーレスポンスの実践について紹介します。

RFC7807とは

2016年にIETFから発行された、新しいHTTP APIのエラーレスポンス形式*1です。 日本語の解説記事は非常に少ないですが、こちらのブログで非常にわかりやすく要所を日本語に翻訳しつつ説明されています。

この標準に沿うことで新しい開発メンバーへ仕様を共有することが簡単になります。 また過去にこの仕様を見たことがあれば、新たに勉強することは最小限で済みます。

サーバサイドチームの取り組み

私が所属するサーバサイドチームでは11月にAPIサービスの大幅なバージョンアップを行いました。 しかし、エラーレスポンスの仕様(特にレスポンスボディの仕様)が曖昧なままでアプリチームがハンドリングしたい粒度でエラー原因を返せていませんでした。 そこでエラーケースを再定義するとともにアプリがハンドリングするために必要十分な情報を持たせるよう改修を行うことになったのですが、どうせエラーレスポンス仕様を変更するならより標準的なフォーマットに寄せたいと考えた結果採用したのがRFC7807です。

仕様定義

RFC7807が定義する仕様にはいくつかの選択肢があります。 レスポンスフォーマットをJSONXMLのどちらにするか、いくつかあるプロパティのうちどれを利用するか、などです。 それらについて私達のサービスではどれを使用するかを決めてgithub wiki上のページへ以下のようにまとめました(実際のエラーコード・URLとは変更しています)。

f:id:triplew-tech:20190204160053p:plain:h500
github wiki上のエラーレスポンス仕様ページ例

上に書かれていますが基本的に以下のように仕様を定めました。

ヘッダ

  • Content-Type: application/problem+json

プロパティ

  • type, titleのみを利用する。 またそれらのプロパティは必須であり、レスポンスによって特定のプロパティが欠けるようなことはない。

サンプル

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
  "type": "/api/v5/errors/invalid-device-id",
  "title": "Invalid deviceId."
}
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "about:blank",
  "title": "The supplied authentication is not authorized to access this resource"
}

また私達のAPIOpenAPI Specificationで書かれてswagger-uiからいつでも参照できるようになっています。 こちらには各APIごとのエラーレスポンスの仕様が細かく書かれていましたので、こちらも合わせて修正しました。

f:id:triplew-tech:20190204161516p:plain:h500
OpenAPI エラーレスポンス仕様例

この仕様決定とドキュメントの作成・更新に比較的時間がかかり、だいたい合計5人日ぐらいかかったイメージです。

実装について

APIサーバはScala言語で書かれておりakka-httpフレームワークで作られています。 今までもRFC7807準拠ではなかったもののJSONフォーマットでエラーレスポンスを返していたので、だいたい実装期間は3人日程度でした。

リリースについて

修正内容に既存のAPI利用を阻害するような破壊的な内容がなかったため、通常通りのリリース手順でリリースしました。

API利用側の反応

2019/02/01現在アプリ側で増強されたエラーレスポンス情報を利用するコードが書かれておらず、まだフィードバックは特段ない状態です。

やってみた感想

エラーレスポンス仕様はまだ大手の公開APIでも完全にバラバラな状態で(参考: WebAPIでエラーをどう表現すべき?15のサービスを調査してみた - Qiita)各種WebフレームワークのRFC7807サポートもほとんどない状態ですが、個人の嗜好や趣味によって行き当たりばったりに決めるよりRFC準拠で実装することは様々なメリットがあります。 毎回APIの仕様を決めるときは数ある選択肢からどういったものにするか非常に悩むのですが、今回RFCという明確な指針に従ったことで非常に楽に仕様を決めることができました。今回決めた仕様は将来もっと良いものに拡張する可能性もありますが、今回はこちらでリリースしています。

フレームワークも徐々にRFC7807をサポートしつつあるので導入はこれからもっと楽になると思います。

Add Media types for RFC 7807 by bigwheel · Pull Request #2323 · akka/akka-http*2

みなさんもっとRFC7807に従うことで、つまらない詳細の仕様に悩まずより重要なビジネスロジックに集中できるようにしましょう!

*1:new error response formats for HTTP APIs

*2:この修正は実は僕が送ったものなので自作自演ですが^^;

AWS re:invent 2018 社内で活用できそうなアップデートまとめ

こんにちは! トリプル・ダブリュー・ジャパン株式会社でサーバサイドリードエンジニアをしています西田です。ネット上では主にkbigwheelの名前で活動しています。

先日、私達の会社でもフル活用しているAWSの新機能・新サービスを発表するre:inventというイベントがありました。

reinvent.awseventsjapan.com

こちらの記事では発表された中から社内で活用できそうなものをリストアップしたいと思います。 アップデートの一覧は以下の公式ブログの記事からいただきました。

目次

すぐに使いたいもの

詳細によっては使えそうなもの

覚えておきたいもの

*1:毎度お世話になっています

AWS re:Invent 2018 ダイジェスト 特別講演 レポート

こんにちは! トリプル・ダブリュー・ジャパン株式会社でサーバサイドリードエンジニアをしています西田です。ネット上では主にkbigwheelの名前で活動しています。

本日業務の一貫としてこちらのイベントに参加してきました。

aws.amazon.com

その中からおそらくキーノートに相当する午前中の特別講演について非常に簡単にですがレポートしたいと思います。会場のルールとして発表中の撮影が禁止されていたため画像が皆無ですがご了承ください*1

特別講演 re:Invent 2018 サマリーと新サービス概要

特別講演
re:Invent 2018 サマリーと新サービス概要
〜午後のテクニカルセッションをよりお楽しみいただくために〜

亀田 治伸
アマゾン ウェブ サービス ジャパン株式会社 プロダクトマーケティング エバンジェリスト

話の流れとしては題にある通りre:inventというイベント全体の流れを説明して午後のテクニカルセッションを理解しやすくするためのものでした。 全5日に渡るre:inventについて各曜日に何が行われたか、発表されたサービスなどの概要が説明されます。

以下特に記憶に残っている点を書き出しました。

人工衛星との連携や全世界規模の海底ケーブルプロジェクトへの参入

awsクラウド分野を開拓してからまだ12年ですが、いよいよ話が大きくなってきたなと思いました。 この人工衛星との連携というのは受信側の話だそうですが、この分だとawsが自ら人工衛星を打ち上げる(そしてそれがサービスになる)のもそう遠くなさそうです。

「開発者」改め「ビルダー」という呼称

一部で物議を醸した開発者の代わりとなるビルダーという呼び方についての説明がありました。 弁明めいて聞こえたのは僕の気のせいかもしれませんが、やはりビルダーという呼称はイマイチしっくり来ないなと感じました。

新機能/サービス公開と顧客数が加速的に増加していることをアピール

去年のre:inventと比べて今年はより多くの新機能を公開しているということでした*2。 また顧客数の増加もより加速しているそうです。でもその測定方法や詳しい値は一切公開されていなかった点は留意しましょう。

DBに関してはOracle社にやり返す

DBに関してはかなり面白い一幕がありました。 Aurora, RDS for MySQL/PostgreSQLで競合しながら同時にRDS for Oracleでは顧客であるAmazonOracleの関係は、最近Amazonが社内DBをOracleからRedshiftへ変更したことにより一気に冷え込みつつあるそうです。

Amazonプライムデーのサーバ障害、原因は「OracleからAurora DBに乗り換えた」ではない――CTOがCNBCの報道を否定 (1/3) - ITmedia エンタープライズ

今回の発表の中でも「ビルダーはプロダクトに不当に縛られるべきではない、どこの会社とは言わないが」という趣旨の発言がありました。DBについて話している途中出会ったことからもOracle社のことだったのは間違いないでしょう。 前職でOracle Databaseに振り回された身としては個人的には大いに同意するところです。

まとめ

以上になります。短くはありますが軽く雰囲気だけでも掴んで頂ければ幸いです。

この記事は2.5%ルールに従い業務時間を利用して書かれました。

*1:帰ってから外観くらいは撮ってくきたらよかったと後悔

*2:記憶が正しければ去年40, 今年100とのことでした