こんにちは、馬場です。
これは、TECHSCORE Advent Calendar 2016 の1日目の記事です。
この1-2年、マイクロサービス・サブシステムに分割し、AWSとオンプレミスを使い分けてシステムを構築しています。システムを運用していく中で、オンプレミス環境/モノリシックデザインのシステムにはない経験をしました。それはネットワークの不安定さによる障害です。
通信障害は発生する前提でシステムを構築する。 AWS関連のさまざまな記事や、このブログでも書かれていることです。
わかっています。わかっているつもりだったのですが、1年間運用してみて、本当に定期的にネットワーク断が発生し、夜中に起こされ、休日に調査したりして、やっぱり1年前の私はわかっていなかったのだな、と感じました。
この記事ではどうやって乗り越えたか、シェアしたいと思います。
リトライする
基本は「リトライ」
ネットワーク断は残念ながら発生します。以前は発生するたびにAWSに問い合わせしてましたが、そこはあきらめましょう。とにかく「他のサーバと通信する場合は、かならずリトライする」ことです。
AWS のクライアントライブラリを利用していればデフォルトの設定でリトライしてくれます。ですが、このデフォルトの設定では乗り越えられないネットワーク断がこの1年間で数回発生しています。経験的に長くても5分耐えればネットワークは復旧するので、サービスの特性上許されるのであれば、5分リトライしつづけるように設定すると夜もぐっすり眠れます。
もうひとつ気をつけなければいけないのは、ネットワーク断はKinesis や DynamoDB などのマネージドサービスだけでなく、RDS や ElastiCache などの VPC 内のサーバや ELB/ALB - EC2間でも発生する、ということです。オンプレミス環境ではさすがにネットワーク的に近いRDB や Redis との通信断を意識することはなかったのですが、AWS (クラウド)である以上近くのサーバが近くにあるとは限らないので、VPC内の通信にもリトライ処理を行うことをおすすめします。
でもそんなに簡単じゃない... その「リトライ」大丈夫?
じゃあ、リトライするようにしておけば大丈夫かというと、システムはそんな単純ではありません。例えば、データの登録処理を行う場合です。ネットワークの不安定さを検知しリトライ処理を行ったものの、実は最初のデータも正しく登録されていたとしたら、データが重複して登録されてしまいます。
つまり、ネットワーク断を見越してリトライ処理を実装するなら、リトライ処理を見越した機能設計をしないといけないのです。
重複実行を見越してサービス設計をする
リトライの仕組みをいれる以上、重複実行は避けられません。重複実行による副作用が問題になる場合、サービスを実行する・リトライするシステムだけでなく、サービスを提供する側も重複実行を見越した設計をしなくてはいけません。
例えば、重複登録を防ぎたいのであれば、以下のようにします。
- サービスを実行する側 : データがシステム上に発生した段階でシステム全体でユニークなID(UUIDなど)をデータに割り振り、そのデータを他のサービスに送信します。
- サービスを提供する側 : 登録処理をしたデータのIDは記録しておきます。既に登録済みのIDをもつデータを受信した場合は登録処理を行いません。
ローカルを見直す
そんなに待っていられない
ネットワーク断は基本的に「リトライ」で乗り切れ。それはそうなんですが、サービスによってはそんなに待ってられません。例えば、Webページなど他のサービスを呼び出して取得したデータを表示するサービスでは、数分のリトライ後にレスポンスが返ってきたとしても遅いのです。
「ネットワークが怖いなら、ローカルに置けばいいじゃない」
ネットワーク通信の瞬断は発生しますが、ローカルディスクやメモリへのアクセスは信頼できます。ですので、データ読み取り系のサービスでは、ネットワーク経由でデータを取得せずにローカルのメモリやディスクの情報を返すような実装にできないか、考えてみましょう。
例えば、マスター系のデータ。10年前ならRDBMSにマスターテーブルを作成し、システム全体がそのテーブルを参照するように設計していました。ただ、分散環境では、都度ネットワーク越しにデータを参照するのは時間がかかります。ネットワーク断のリスクもあります。データ量がすくなく更新頻度も低いのであれば、各サービスごとのローカル環境にデータを保持するのもありです。
また、データ量が多い場合でも、一度問い合わせて得たデータをキャッシュしメモリやローカルディスクに保持することは有効です。ネットワーク越しにデータを取得する回数を減らすことができますし、もしネットワーク断が発生しデータが取得できない場合でも、キャッシュにデータが存在すればサービスを提供し続けることができます。
最後に
この1年で、モノリシックシステムでの「正しい」やりかたが通じず、かなり試行錯誤しました。当初は機能を実装してから耐障害性を考慮するような有様でしたが、ようやく設計当初から「ネットワーク断に強い構成はなにか」考慮することに慣れてきました。また、うれしいこともあります。実は、障害発生を見越したシステムは計画停止にも強いシステムなので、数分の機器メンテナンスではシステム側は何もすることはありません(それでも動作確認のために出社するのですが)。これを読んで、面倒くさそうと思った人もいるかもしれませんが、運用の手間が少なくなっていっている実感はあるので、ぜひチャレンジしてみてください。