POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook
Dan Luu

本記事は、原著者の許諾のもとに翻訳・掲載しております。

私はポストモーテム(事後分析)の記録を読むのが大好きです。ポストモーテムを読むと勉強になりますが、大抵の教材的資料とは違って、興味深いストーリーが含まれているのです。相当な時間をかけてGoogleとMicrosoftのポストモーテムを読みました。大きな障害を招く最大の原因について、私は(まだ)きちんと分析していませんが、何度も繰り返し目にするポストモーテムのパターンがいくつかあります。

エラーハンドリング

適切なエラーハンドリングのコードを書くのは難しいものです。エラーハンドリングのコードに含まれるバグは、 大きな 問題を引き起こす主な原因となっています。つまり、エラーによってバグのあるエラーハンドリングのコードが実行されるということは、単に個々のエラーが重なるだけという事態にはとどまらないのです。障害が重なって重大なシステム停止につながることはよくあります。それはある意味明らかなことで、エラーハンドリングは一般的に難しいものと考えられていますが、その話をすると相手からこう言われます。「深刻なポストモーテムのほとんどは、不適切なエラーハンドリングで障害が重なり、その対応にもミスが続いたため書かれている。そんなのはいかにも明らかなことだ」と。ただ、”明らかな”とは言っても、エラーハンドリングが適切か確認するためにテストや静的解析が十分に行われるほど、重大性が認識されているわけではありません。

このテーマを詳しく扱っているのが、Ding Yuanらの優れた論文と講演 「Simple Testing Can Prevent Most Critical Failures: An Analysis of Production Failures in Distributed Data-Intensive Systems」 です。この論文はまさにタイトルどおりの内容です。著者は、クラスタ全体を破壊したりデータの破損を引き起こしたりする状態のことを重大な障害と定義し、Cassandra、HBase、HDFS、MapReduce、Redisで見られた約200個のバグを調べて、48件の重大な障害を特定しました。その原因を調査した結果、実に92%は不適切なエラーハンドリングが原因であったことが分かったのです。


注釈:
Initial faults:最初の障害(例:ハードウェア障害、バグ、設定ミス)
Incorrect handling of errors explicitly signaled in s.w.:ソフトウェアで明示されるエラーに対する不適切なエラーハンドリング
Latent error:潜在的なエラー
Trivial mistakes:軽微なミス
System-specific:システム特有のもの
Errors ignored:エラーの無視
Abort in over-caught exceptions:過大な例外処理による強制終了
“TODO” in handler:ハンドラ内の”TODO”
Easily detectable:容易に検出可能なもの
Complex bugs:複合的なバグ
Catastrophic failures:重大な障害

更に内訳を見ると、バグの25%は単純にエラーを無視していたこと、8%は誤った例外処理、2%は不完全な”TODO”が原因でした。そして23%は「容易に検出可能なもの」、つまり「致命的でないエラーに対するエラーハンドリングのロジックに誤りがあり、開発者が命令網羅のテストやもっと慎重なコードレビューを行っていれば発見できたバグ」でした。ちなみに、私がGoスタイルのエラーハンドリングも嫌いではない理由の1つがこれです。エラーチェックコードのせいでメインコードの流れが分かりにくくなるという不満の声も多いようですが。エラーに強いシステムを構築したいなら、エラーチェックコードこそがメインコードです!

この論文 には、ここで書ききれない貴重な情報がたくさん含まれています。例えば、 Jepsen が大変有効な理由が説明されています(重大な障害の98%は3ノードクラスタで再現可能)。また、非決定性の障害の割合(論文のサンプルでは26%)やその原因、障害を引き起こしがちなエラーを検出できる静的解析ツールの作成についても述べられています。

設定

コードではなく設定のバグは、重大なシステム停止の原因として私が最もよく見てきたものです。公開されているポストモーテムで大規模なシステム停止について調べてみたところ、約50%は設定の変更が原因でした。公開されているポストモーテムでシステム停止の全体的な状況を把握することはできませんが、ポストモーテムのデータベースからランダムに抽出したデータを見ても、設定変更は重大なシステム停止の原因として圧倒的割合を占めていることが分かります。エラーハンドリングの場合と同様に、設定変更がリスクを伴うのは明らかだという声もよく聞きますが、多くの企業がコード変更時と同様に設定変更のテストやステージングを行うほどには、重大性が認識されていないのです。

非常に緊急なケースを除いて、リスクを伴うコード変更は、企業全体のサービス停止につながる恐れがあるため、全てのマシンに一斉配信されることは基本的にありません。ですがどの企業も、軽微に見える設定変更でも企業全体のサービス停止を引き起こす場合があると、苦い経験から学ぶ必要がありそうです。例えば、2014年11月にAzureのサービスが停止したことはよく知られていますが、これも設定変更が原因でした。ここでMicrosoftを責めようとしているわけではありません。なぜなら、大手の競合サービスも似たような原因で停止したことがあり、同様の事態が再び発生するリスクを減らすため、いずれも適切な手順を踏むようになったからです。

クラウドサービスの大企業を責めようとしているわけでもありません。むしろ、多くのスタートアップや巨額の資金を有するスタートアップと比べても、そうした大企業のサービスの方が望ましい状況にあります。私の知る限り、”ユニコーン”と呼ばれるスタートアップの多くは、リスクを伴う設定変更を検証できる適切なテスト環境やステージング環境を持っていません。その理由は分かっていて、設定変更が検証できるほど十分に本番環境を反映したQA環境を用意するのは難しいことが多く、シートベルトをしないで車を運転するのと同様に、ほとんどの場合はそれで何の問題も起きないからです。もし運転する前に自分でシートベルトを作らなければならないとしたら、私もシートベルトをして運転することはなくなるかもしれません。とはいっても、シートベルトなしでの運転が設定変更と同じぐらい危険なら、シートベルトを作ることを考えるかもしれません。

かつて1985年にJim Gray は「オペレータの操作、システム設定、そしてシステムメンテナンスが障害の主な原因であり、42%を占める」と報告しており、それ以降、類似の結果を示す様々な論文が発表されています。例えば、 RabkinとKatz は、障害の原因として以下を報告しています。


注釈:
misconfiguration:設定ミス
bug:バグ
operational:操作
system problem:システムの問題
install:インストール
user bug:ユーザによるバグ
hardware problem:ハードウェアの問題

ハードウェア

基本的に、マシンのあらゆるパーツは故障する可能性があります。多くのコンポーネントも、宣伝されているよりはるかに高い確率でデータの破損を引き起こすかもしれません。例えば、 Schroeder、Pinheiro、Weber は、DRAMのエラー率は伝えられていた数値よりも10倍以上高かったと報告しています。サイレントエラーの数は膨大なもので、実際にGoogleでもECC RAMに切り替える前は、そのことが問題になっていました。ハードウェアがエラーを検出できる場合でも、問題が起きる可能性があります。例えば、 データをエラーから保護するためのイーサネットのチェックサムも安心できるものではなく 、私自身、不正なパケットが有効なパケットとして通過するのを見たことがあります。もっと大きなレベルの話でいえば、ハードウェア検査でデータの破損がチェックできると思っていても、検出されていなかったエラーが想像以上の頻度で発生することもあり得ます。

コンポーネントに異常が発生した時のフェイルオーバーにも、障害が起きる可能性があります。 こちらのAWSの障害は典型的な事例です 。発電機の電力のフェイルオーバー処理は定期的にテストされるよう、妥当と思われる対策がとられていたにもかかわらず、暴風で停電になったところバックアップの発電機が負荷時にきちんと電力を供給できず、AWS US Eastリージョンの相当な部分のサービスが停止したのです。

人間

以下の内容はヒューマンエラーではなく、プロセスエラーの話だと言った方がいいでしょう。というのも私は、誤って大惨事を引き起こす可能性がある状況に人間を置くこと自体が、プロセス上の問題だと思っているからです。一般的に認識されていることですが、大規模なシステムを運用する場合、ハードウェアの故障に耐え得るシステムにする必要があります。マシンの故障頻度を計算してみると、ハードウェアの故障に弱いシステムは間違いなく信頼性が低いことが分かります。そして、マシンよりも更に多くのエラーを引き起こしているのが、我々人間です。誤解がないように言っておきますが、私は人間嫌いではありませんし、人間の親友もいますよ。でも、大規模障害を起こし得る環境にずっと置かれた人間は、いつか大惨事を起こすものです。それにもかかわらず、以下のようなケースが後を絶ちません。

いいか、今からリスクの高い作業に取り掛かるぞ! 危険な作業をする時は、”超慎重な”人間になるんだ。ヤバい! システム全体が停止した…。

ポストモーテムの中には、「作業リスクが高いため、某ハイリスク用手順を採用した」というくだりで始まるものが大量にあります。こういったものを見ると、ヒューマンリスクを軽減するために人間が講じる特別な措置こそが、オペレーションシステムの問題点のように感じられます。いくつかの一般的な手順書には、複数の人間に作業の監視や確認をさせる、大規模障害に備えてシステムオペレータを待機させる、という内容が含まれています。もちろん合理的な内容ですし、これによって、ある程度はリスクを低減できます。しかし私が読んだポストモーテムには、自動化をしていればリスクを大幅に削減できた、あるいは完全に排除できたはずだ、というものが多くありました。一連の指示を完璧に実行しなくてはいけない人間がミスを犯し、その結果システムが停止した事例は多くあります。このミスなく処理を実行するということは、まさにプログラムの得意分野です! 一方、人間が手作業でエラーを確認しなければならないケースもあります。そのような場合、自動化が難しいこともあるので、人間とプログラムのどちらが勝っているかは明確ではありません(プログラムが見逃したエラーケースを、人間が発見することもありますから)。それでも、私が見てきたケースにおいては、ほぼ自動化する方に軍配が上がっています。


注釈:
Problems in the Datacenter:データセンターにおける不具合
Downtime due to human error:ヒューマンエラーによるダウンタイム
Downtime due to system failure:システム障害によるダウンタイム
Run out of IP addresses:IPアドレスの枯渇
Downtime due to natural disasters:自然災害によるダウンタイム
Security breaches:セキュリティ違反
Regulatory or compliance issues:規定や順守の問題
Insufficient bandwidth:バンド幅不足
Latency issues:潜在的な問題
% of respondents:全回答者数に対する割合
IDC’s Enterprise Datacenter Survey, December 2013(N=410):
2013年実施のIDCによるデータセンターの調査結果(回答件数 410)

IDCの調査では、データセンターで発生する問題において、一番厄介な原因はヒューマンエラーだという回答結果が出ています。

私は公開されたポストモーテムに載っているヒューマンエラーの件数が、実際よりもどれだけ少ないかという点に関心を持っています。私の知る限り、GoogleとMicrosoftは共に、他の企業に比べて、かなり自動化が進んでいます。なので、この2社のポストモーテム用のデータベースの中身と、他社が公開しているポストモーテムとでは、前者の方がシステム停止の原因におけるヒューマンエラーの割合がずっと少ないだろうと推測していました。ところが実際は逆でした。私が推測するに、障害の根本原因が、リスクの高い手作業によるヒューマンエラーであった場合、どの企業も公開用のポストモーテムを書きたがらないのだと思います。そして彼らは、「技術的改善を行った結果、人的要因による軽微な問題の割合が増加した」という感じで、もっともらしい理由を持ち出すのです。これは航空業界などでは、あり得ることでしょう。でも多くの企業が、数えきれないほどの手作業を行っているIT業界では、通用しない話です。とはいえ、多くの企業のポストモーテムのデータベースにアクセスできない限り、断言はできません。もしこの分析(や他の分析)に協力していただける企業があれば(可能な限り匿名にしますので)、ご連絡ください。

モニタリングと警告

適切にモニタリングできていないことだけが原因で、問題が発生することはあり得ません。しかし、それが重大な要因になるケースは多くあります。ヒューマンエラーと同様に、モニタリングの問題もポストモーテム上では、実際の件数より少なく報告されているように思います。他社で働いている人に、それぞれの企業で起きた最悪の障害ついて尋ねたところ、原因の大半は適切な警告設定ができていないことでした。つまり公開用のポストモーテムを書かざるを得ない深刻な障害から、幾度となく人々を救ってきたのは、システムオペレータたちの勇敢な行動なのです。しかし彼らの行為は、拡張性のあるソリューションではありません。

時には、わずかなコーディングのバグが、大規模障害を引き起こすこともあるでしょう。しかし、ほとんどの場合、原因は一目で分かるプロセス上の問題です。例えば、障害全般に関するエスカレーションパスが明確になっていない、というような問題です。これにより、無関係なチームが半日もデバッグをしている、もしくはオンコールのバックアップ体制ができていないという状況が生まれます。そして誰かが気付くまで、何時間もシステム上でデータ損失やデータ破壊が発生するという事態に陥ります。(もちろん)オンコールで呼び出された人々は、事態が悪化していることに気付いていません。

2003年の北アメリカ大停電 は、この典型的な例です。これは小規模なシステム停止か、もっと言えば若干のサービス品質の低下で済むはずのことでした。しかし警告を何度も無視したことが(主な)原因で、史上最悪レベルの大停電が発生してしまいました。

結論ではありません

本来は、そろそろ結論を書くべきなのでしょう。でも結論を書いて、読者に行動を呼びかける前に、私はどうしても本格的なデータ分析をしておきたいのです。私は一体何を求めているのでしょう? 主にどの分野で、頻繁に起こるエラーについて検討すべきなのでしょう? 自問しているのではありませんよ。私は、これから検討すべき他分野について、心から皆さんの意見を聞きたいのです。 よければTwitterで気軽に連絡してくださいGitHub上で、公開されたポストモーテム も集めています。

そして、いつか時間を見つけて、徹底的に分析をしようと考えています。でも、すでに多くのポストモーテムを読んでいるので、更に膨大な数の資料を読んで分析をしなくても、今までとはちょっと違った分析ができる気がします。これからはコードをレビューする時に、エラーやエラーハンドリングのコードにより多くの時間を割くので、あまり楽しい時間を過ごせなくなるでしょう。更に、”明らかな”プロセス上のバグをチェックして、それを修正するよう人を説得する作業に、より時間を取られることになりそうです。

ここで、故障モードに関して私が不思議に感じていることを1つお話ししましょう。それは、自分が気付いたことを誰かに話すと、少なくとも1人からは、私が見つけたプロセス上の問題はどれも明らかだと言われるということです。でも、その”明らかな”問題が、いまだに多くの障害を引き起こしています。また、ある人からは「君の指摘は実に”明らか”だ。僕の会社ではちょうど、君と話したことが原因でシステムが全面的に停止して、数十億ドル単位の損害が出た」と言われたこともあります。つまり、明らかだからと言って、それが実施されているとは限らないのです。

その他の参考資料

Richard Cookの 「How Complex Systems Fail」 という記事では、より一般的なアプローチを取っています。この記事から着想を得た 『The Checklist Manifesto』 という本もあります。

AllspawとRobbinsの 『Web Operations: Keeping the Data on Time』 は、Webアプリの分野でこのテーマを扱っています。Allspawの記事でも、 他の分野の関連資料を紹介しています

私が慣れ親しんでいる分野に近いところでは、長きにわたり障害の原因が調査されています。以下に、主要なものを挙げましょう。 Jim Grayの「Why Do Computers Stop and What Can Be Done About It?」 (1985)、 Oppenheimer らの「Why Do Internet Services Fail, and What Can Be Done About It?」 (2003)、 Nagarajaらの「Understanding and Dealing with Operator Mistakes in Internet Services」 (2004)、 Barrosoらの「The Datacenter as a Computer」 (2009)の一部、 RabkinとKatzの「How Hadoop Clusters Break」 (2013)、 Xuらの「Do Not Blame Users for Misconfigurations」

航空機においても、その信頼性が理解されるまでには長い歴史がありました。 何十年にもわたってプロセスがどのように変化を遂げたのかという話 は、この教訓を一般化する方法は置いておくとして、興味をそそられる内容です。

余談ですが、アップタイムと信頼性を向上させるのが、どれほど困難であったかを知るのも面白いかと思います。1974年に RitchieとThompson は、アップタイムが98%で、”わずか4万ドル(当時の為替レートで約1,000万円)”のシステムについて論文を書いています。更にその10年後にはJim Grayが、99.6%のアップタイムを妥当なベンチマークとして用いました。現在、さらにアップタイムを改善することは可能ですが、それにはシステムを相当複雑にする必要があります。

謝辞

この記事の草案に対して意見をくれたLeah Hanson、Anonymous、Marek Majkowski、Nat Welch、Julia Hansbroughに深く感謝します。匿名の方の中で、名前を出してもいいという方がいれば、zulipでご連絡ください。念のため書きますが、該当するのはGoogleの方3名と、Cloudflareの方1名、そしてコメントをくれた匿名の読者が1名です。なお、この記事に関するコメントや批評はいつでも受け付けています。特に、小規模な企業で働いている方からのご意見をお待ちしています。改めて、ご協力いただいた皆様に感謝します。

最後に、本件を引き受け、記事の誤りを指摘してくれたgwernとDan Reifに心から感謝いたします。

監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。