恥かどうかはともかく、そもそも逃げられない障害対応のお話

[システムエンジニア Advent Calendar 2016](http://qiita.com/advent-calendar/2016/se "システムエンジニア Advent Calendar 2016")の20日目の記事だよ！
昨日は[@sh-ogawa](http://qiita.com/sh-ogawa)さんの「[SIerが実践する分散開発とバージョンコントロール](http://qiita.com/sh-ogawa/items/fbc5a0c942029f83bfcf)」でした！！

# システム障害のお話

は〜い、こんにちは！いよいよクリスマス間近ですね！
この時期になると、キャッキャウフフの予定も盛りだくさんだと思います！[^1]
そんな大事な日に限って起こるのが**あれ**です。
そう、みなさんもよくご経験されているだろう、**システム障害**です[^2]。

**システム障害**

それはツラく長く険しい道のりを告げるゴングです。
今回は、その障害対応のお話をしたいと思います。

この記事のアジェンダはだいたいこんな感じです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/101517/7fa739fb-d28f-8f77-697c-db3785b86531.png" width="100%">


# 報告する

はい、システム障害が起きました〜。

ｷﾀ━━━(ﾟ∀ﾟ)━━━!!

「マジでか…(´；ω；｀)ﾌﾞﾜｯ」
「なんで今日なんだよ〜( ；∀；)」

障害発生時の想いは人それぞれだと思いますが、まず最初にするのは報告です。

**上長、保守チームに報告しましょう。**

情報共有、とても大事なことです[^3]。

報告内容として次の点を明確にしましょう。

* いつ発生したのか
* どのシステムで発生したのか
* どのような契機で発見したのか（エスカレーションメール、お客さまからの電話など）
* 障害内容
* 業務影響（影響範囲・影響度）をできるだけ推測して

ここで重要なのは、

* 事実と推測を明確に分けて
* 原因探求は後回しにして

報告することです。

なお、お客さまへの報告は不具合対応者が行うのではなく、上長や保守チームに任せたほうがいいでしょう。
障害対応だけでも大変な状況です。
お客さまとのやりとりに使うリソースは障害収束に向けたいものです。
受け答えに時間が取られ、対応が遅れるのは双方にとってもデメリットですので。

あ！あと、家族や彼女への報告を忘れずに行いましょう〜！[^4]

<img src="https://qiita-image-store.s3.amazonaws.com/0/101517/a7305368-2f1c-7f39-8732-5de8d0d12c90.png" width="30%">

# コーヒーブレイク

報告をおえたら、**まずは少し休みましょう**。ほんの数分でいいです。
今から十分に堪能できるだろう綺麗な夜景でも見ながら、コーヒーでも飲むといいんじゃないかな？[^5]

<img src="https://qiita-image-store.s3.amazonaws.com/0/101517/d37258fc-f359-d63f-81da-3b066cecb36f.png" width="20%">

障害時は気持ちが高揚しています。
通常時にはありえないオペミスも起こしがちです。
まずは気持ちを落ちつけてから対応に挑みましょう〜。

# インシデント対応

業務への影響範囲や影響度によって、インシデント対応を急ぎます。
インシデント対応とは、

『障害による被害を最小限にし、速やかに通常状態までもどす』

対応のことです。

『障害原因を特定し、恒久的な対策を実施する』

トラブル対応とは分けて考えましょう。

一般的なインシデント対応として、

* アプリケーションやOSの再起動
* データのリカバリ
* バッチの再実行[^6]
* データパッチ
* 運用でカバー

などがあると思います。

なお、インシデント対応を実施すると、不具合解析に必要な情報がなくなることもあります。
対応実施前に、

* ログの退避
* スレッドダンプやヒープダンプなどの取得
* データの退避

など、時間が許す限り**出来るだけの情報を収集する**よう心がけましょう〜。

障害対応時は焦っているので、ついついこれらの情報収集を忘れがちです。
この情報が不具合特定の決め手になることも多いです。
前もって、**障害発生時の取得情報と取得手順を確立しておく**ほうがいいかと思います。

# 障害対応その前に

さて、インシデント対応が完了（もしくは必要なかった、インシデント対応ができない）しました。
本格的に障害対応です。

「俺の左目がうずく、写輪眼！! (`Фω✇') ｶｯ!」

と血継限界を発動して調査に入るまえに、次の点について考えてみましょう。[^7]

### 直近の変化

**直近でなにかシステムやシステム周辺に変化がなかったか**を確かめましょう。

* システム設定変更
* 機能リリース
* OSやミドルウェアなどのパッチ
* 業務イベント（月末・年末・リリース後初のイベントなど）

システム障害の原因が直近の変化にあることは少なくないです。
この観点で、不具合の発生原因になりそうなものがないかを確認しましょう。

### 過去の障害事例

**過去の障害事例で類似のものがないか**を思い出しましょう。

どんなシステムにも「弱い部分」があるものです。
不具合の多くはある特定箇所で集中して起こるものです。
過去事例を参考に、発生原因が特定できないかを確認しましょう。

# 障害対応手順

さて、いよいよ障害対応です。

**いまこそ血継限界を発動させるときです！**[^8]

ここからは、障害対応時に僕がいつも実施している手順を紹介したいと思います。

### ログをみる

**ログを見ます。**
当たり前のことですが、出来ていないエンジニアも多いです。

大抵の場合、これだけで不具合原因を特定できることが多いです。
きちんとログを読むのです。**英語に折れない心！**大事です。

「なんか変なエラーが出てる！」

と騒がず、日本語・英語に関わらずメッセージを読みましょう。
Google先生は便利です。

ログに出力されているエラーメッセージやキーワードでググりましょう。
Google先生は偉大です。

その際、英語のサイトが検索でたくさんヒットしても、英語に折れない心、大事です。
英語のサイトを読みましょう。
繰り返しますが、**Google先生は便利です。**

その他、収集したスレッドダンプやヒープダンプ、データなども観測します。
スレッドダンプなどの読み方は、先輩に教わって勉強しておきましょう〜。

### 観測する

本番環境のアプリケーションやCPU、メモリ、プロセスの状況などを観測します。
（インシデント対応が実施されなかったなどの理由で）本番環境に障害発生時の状況が残っている場合は特に有効です。

* 不明なプロセスが立ち上がっていないか？
* CPUが異様に上昇していないか？
* ヒープメモリに異常はないか？
* RDBMSにおかしなロックはないか？

これらの情報から、不具合要因を推測していきます。

### 再現させる

不具合が特定、もしくは推測できた場合、テスト環境での再現を試みます。
不具合原因の確証を得るためにも、再現は大事です。
その後に続く不具合修正の確認にも、再現環境が必要となります。

# 不具合解析方法

ログなどの情報から不具合が特定できなかった場合、腰を据えて不具合解析に乗り出しましょう〜。
ここでは、僕が有効だったと考えている、いくつかの不具合解析方法を紹介したいと思います。

### 調査範囲を絞り込む・小さくする

**出来るだけ調査範囲を絞り込みましょう〜。**

調査範囲の広さと原因特定の困難さは比例関係にあります。
出来るだけ調査範囲を絞り込んで小さくしましょう。

例えば、1万件のデータ処理でエラーが発生している場合、5000件、2500件・・とデータを減らしていきます。
エラーが発生しなくなるまでデータを減らします。いわゆる二分探索です。
データを削って不具合がでなくなった場合、削った方に不具合要因のデータが存在するはずです。
どちらを削っても不具合が出なくなった場合、

「データ値ではなくデータ量に問題があるかも」
「データの組み合わせに問題があるのかも」

といった推測もできます。

プログラムコードについても同様です。
不具合が出なくなるまで余分なコードを削ります。
そうして不具合が発生する最小のコードを作成します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/101517/f7ef5400-5fe2-94e8-adef-34fbe5f1e32f.png" width="80%">

このように、調査範囲を絞り込んで小さくすることで、ノイズが減って調査が行いやすくなります。
また、他チームやサポートへの報告や協力依頼も行いやすくなります。

### ログを埋め込む

調査箇所にログを埋め込んで、不具合特定のための情報を増やします。

この場合、本番環境にログを埋め込むのは難しいので、再現環境ができていることが重要となります。
デバッガを使わないのは、ログ埋め込みのほうが比較的スキルが不要で簡単にできるからです。
怪しい箇所にログを埋め込み、原因を絞り込みましょう。

### ツールを探す

適切なツールがないかを探しましょう。
メモリリークのような調査範囲が絞りにくい不具合の場合、ツールの利用が役立ちます。

昔、C++のライブラリでメモリリークが発生した際は、メモリリークを検出するOSSライブラリが役に立ちました。
JVM上で稼働するシステムの場合、VisualVMなどの優れたツールが無料で利用できます。
このように、利用出来るツールがないかを探しましょう。

### デバッガを使う

可能であればデバッガを使いましょう。

例えばブラウザ動作の不具合については、本番環境でもChromeのディベロッパーツールを用いたデバッグが出来ます。

### ライブラリやフレームワークのソースを読む

ライブラリやフレームワークの動作がどうもおかしい。ドキュメントを読んでも検討がつかない。
そういう時はライブラリやフレームワークの該当箇所のソースを読みましょう。

ソースを読むまで気合をいれなくても、なんとなく眺めるだけでもいいです。
案外不具合のヒントが判明したりしますから。
眺めるだけなら気も楽ですしね〜。

### PDCA

不具合が特定しない、長期化しそうだと判断した時は、**PDCAを意識した行動**をとりましょう。

PDCAとは、

```
Plan（計画）→ Do（実行）→ Check（評価）→ Act（改善）
```

を繰り返すことです。

**Plan**

不具合原因を特定するための調査内容を計画します。
計画内容は文章に起こし、関係各位に（メールなどで）報告します。

**Do**

計画を実施します。
実施に用いた環境やソースは、後で再現できるよう**必ず**全て残しておいてください。

**Check**

実施結果を検証します。

* 想定どおりの動作をしているか
* 仮説と異なる不明な動作はないか

を確認します。
検証に用いたログなどについても全て残してください。

**Act**

これらについて考察し、この時点で不具合が特定できていなければ、次の計画に移ります。
実施結果と考察内容を文章に起こし、関係各位に報告します。

**Rest**

PDCAにはないですが、休息重要です。

**適切に休息をとりましょう。**

経験的に、休憩時間にふと不具合の原因や解決方法を思いつくことが多かったです[^9]。

このようにPDCAを意識した行動をとることで、

* 客観的に自分の行動を把握できる
* 調査内容を中間報告として、関係各位に報告できる

ようになります。

不具合解析が長引くと、どうしてもまわりが不安になります。
その結果、過剰な報告を求められたり、時には「報連相」が不足していると注意されることもあるでしょう。

PDCAを意識して行動し、その結果をまわりへ共有することで、周囲の不安を和らげることが出来ます。
結果として不具合解析に集中することができるようになります。

また、体系だった情報をまわりと共有することで、思わぬ気づきを得られることもあります。
手間だとは思いますが、長期化すると判断した時点でこのような動きをしたほうがいいでしょう。

### サポートに問い合わせをする

長期化しそうだと判断した時は、並行してサポートへの問い合わせを実施しましょう。
サポートは膨大な不具合データベースをもっています。これらを有効活用しない手はありません。
手間だと思わずに、必要事項を揃えて連絡しましょう。

上長やお客さまから進捗を問われたら、

「現在サポートからの回答待ちです！(｀･ω･´)キリッ」

結果として不具合解析に集中することができるようになります。

### 悩んだ時のアドバイス

不具合調査をしていて悩んだ時のアドバイスです。

#### おかしな動作は並行処理を疑う

ログを見ると、「システムがありえない動作をしている」というケースがあります。
いくらソースを追っても、

「こんな動作には決してならないはずなんだが〜」

と頭を抱える場合があります。

**そのような時は並行処理を疑いましょう〜。**[^10]

コードを読む時、自然と単独処理としてシーケンシャルに処理を追ってしまいます。
しかし、ほとんどのアプリケーションは複数の処理が同時並行して動いているものです。
排他制御の不具合によりおかしな挙動を引き起こしている場合、ソースを追うだけでは不具合原因になかなか気づかないものです。

「どうもおかしいぞ？」

と感じたら、並行処理を疑ってみてください。

この場合、操作ログやシステムの動作ログを確認することが有効です。
例えば、違うユーザが同じ処理を同時実行していないか？などを操作ログから読み取ります。

もちろん、ここら辺のログ情報がしっかり取れていることが重要ですね。

#### 複合の不具合を疑う

複数の不具合が重なって発生する不具合もあります。
特に安定稼働に入ったシステムでは、複合原因による不具合も疑ったほうがいいでしょう。

#### それでも分からない場合

**休憩をとりましょう〜。**

なんども言いますが、適度な休憩は大事です。

**本番システムに罠を仕掛ける検討をしましょう。**

罠とは、不具合原因と疑わしい箇所に不具合解析のための仕掛け（ログ出力など）を埋め込むことです。
罠を仕掛けて~~お茶を濁す~~様子をみる、という対策手段もあることを覚えておいてください。

# 不具合修正方法

さて、不具合原因も特定できました！

やった、やったぞ僕は( ；∀；)

嬉しさもこみ上げますが、まだまだ気を抜けません。修正方法を検討しましょう。

### 影響の少ない修正方法を

**出来るだけ影響の少ない修正方法を選びましょう〜。**

データ構造やロジックなどに「そもそも」な、大きく広範囲な不整合があるかもしれません。
しかし、大きな変更や広範囲にまたぐ修正は「隠れた新たな不具合を引き起こす可能性」が高いです。
出来るだけ避けたほうがいいでしょう。

**暫定対応と恒久対応を分けて考えましょう。**

まずは障害収束が第一です。

「そもそも論」はトラブルチケットとして管理し、余裕がある時にじっくり検討するよう心掛けましょう。

### 横並び確認

**類似の不具合が他にもないか、横並び確認をしましょう。**

他の類似処理や他システムにも同様の不具合が潜んでいる可能性があります。

ただ、他所に同様の不具合があっても、修正するかどうかは別です。
障害収束を第一として、修正を後回しにできるのであれば、余裕がある時にしたほうがいいでしょう。

# 障害対応後の作業

修正を施してリリースも完了〜！！。

ほっと一息です。

でも、障害対応後にもやるべき作業はまだ残っています。
もう一踏ん張り頑張りましょう〜。

### データのリカバリ

不具合によっては、データ汚染が発生している場合があります。
忘れずにデータの復旧を考えてください。

* バッグアップデータからの復旧
* データパッチによる復旧

障害内容により復旧方法は異なるかと思いますが、不具合修正以上に大変な場合が多いです。

### 再発防止策を考える

再発防止策を考えて、次に備えましょう〜。

* そもそもなぜ障害を作り込んでしまったのか
* どうすれば再発を防げるのか

チームとして考える必要がありますし、大抵はお客さまからも求められます。


# 最後に

みなさん、障害対応お疲れさまでした〜！！(　；∀；) 　ｵﾂｶﾚｻﾏ ｰ

最後に障害対応に対する僕の所感を述べたいと思います。

### 達成感はあるが

障害対応を経験した方はご存知かもしれませんが、障害対応にはちょっとした達成感があります。
なんかこう、

「仕事した！(*ﾟ∀ﾟ)」

って気分になります。
難しい不具合原因を特定した場合、チームから英雄として持ち上げられるかもしれません。
また、一丸となって困難を乗り切ったことで、チーム（時にはお客さまとも）と一体感が生まれたりもします。[^11]

**でも、それって１円にもなってませんから〜！！**

むしろマイナスです。
本来しなくていい作業でコストが掛かっていますし、お客さまからの信頼貯金も（普通は）減っています。

**障害は発生させないことが大事です。**

このひたすら長い記事からもわかるように[^12]、システム障害が発生すると様々な作業がうまれます。
技術者として、まずは**システム障害が発生しない設計や開発、そしてテスト**を心がけましょう〜。

### 休憩大事

なんども文中に出しましたが、休憩大事です。

僕も幾度と障害対応を経験してきましたが、休憩中や休息後の朝に不具合原因やナイスな対策方法を閃くことが多かったです。

「[アイデアのつくり方](https://www.amazon.co.jp/%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2%E3%81%AE%E3%81%A4%E3%81%8F%E3%82%8A%E6%96%B9-%E3%82%B8%E3%82%A7%E3%83%BC%E3%83%A0%E3%82%B9-W-%E3%83%A4%E3%83%B3%E3%82%B0/dp/4484881047)」という有名な本がありますが、その本の中にもこうあります。

> それは、諸君がその到来を最も期待していない時ーーー
> ひげを剃っている時とか風呂に入っている時、あるいはもっと多く、朝まだ眼がすっかりさめきっていないうちに諸君を訪れてくる。
> それはまた真夜中に諸君の眼をさますかも知れない。

シャーロック・ホームズが事件を途中で放り出して音楽会にいった後、事件を見事解決に導くように、休憩には閃きを促す効果があるみたいです。

**適切に休憩をとるよう心掛けましょう。**

そして現場のマネージャーさんは、エンジニアが適度な休憩をとれるよう促してあげましょう。

### 準備大事

システム障害は突然やってきます。その時になってジタバタしても手遅れです。
事前に障害対応のための準備をしておきましょう。

* 適切なログ（操作ログ、アプリケーションログ、システムログ）の出力と収集
* システム監視
* 予防保守
* 検証環境の確保
* サポートラインの確立

いずれも大事です。

例えば、

* Android端末の操作ログをどう収集するか
* 実行中のJVMの状態をVisualVMで参照する方式
* 検証環境と本番環境の差異をいかに少なくするか
* ミドルウェアで不具合が発生した場合のサポート連絡先と連絡手段

などなど、システム障害時の対応を考慮した設計を予め盛り込んでおきましょう！

### 知識大事

障害対応には高度な技術知識が求められます。

* SQLのパフォーマンスチューニング方法
* プロセスとスレッドの特性
* 並行処理プログラミングの知識

などなど。
エンジニアとしての地力が試されるシーンでもあります。
普段からしっかり勉強して備えましょう！！

### 乾杯大事

障害対応が無事終わった後のビール、おいしいです、本当に。
大事なことですね。(\*´ω｀\*)

**みなさん、障害対応、本当にお疲れさまでした〜！！🍻(´∀｀*v)**

[^1]: 若いっていいな〜\*･゜ﾟ･\*:.｡..｡.:\*･'(\*ﾟ▽ﾟ\*)'･\*:.｡. .｡.:\*･゜ﾟ･\*
[^2]: キャッキャウフフの予定はなかったですが、システム障害はよく経験してきましたよ。:-P
[^3]: おい待て、おまえらも帰るんじゃない。
[^4]: 「今夜は（会社が俺を）帰さないよ」と甘い言葉を告げましょう〜( ；∀；)
[^5]: ビルから眺める京都の夜景、それはそれは綺麗でしたよ（遠い目）
[^6]: 不具合のデータなどが特定されている場合、そのデータを除いての実行など
[^7]: 障害時は気持ちが高揚しています。
[^8]: 障害時はry)
[^9]: 僕は風景を眺めて黄昏れているときによく思いつきます。
[^10]: 「コンパイラのバグだ！」「OSのバグだ！」とか言い出したときです。
[^11]: 「リビジョンXを乗り切ったやつら」といった称号も生まれたりしました。
[^12]: すいません、すいません。