AWSでAZ障害が起きたので困ったことを書いておく
前にも似たようなこと書いたなと思ったけどもう一年半も前のことになるのか
ご存知の通り昨日 2021/02/19 23:20頃 AWSにて東京リージョンの一つ apne-az1
にて大規模な障害が発生。多くのAWSを利用していたサービスで影響があった。
そんな私はいつものように アラストリリィ アサルトリリィ ラストバレット というゲームを呑気にプレイしていたのだけど、23:25 から緊急メンテに入ってしまった。
どうしたんだろうと思っていたら、社内SlackにてAWSを利用しているサービスがたまに応答しなくなる、Elasticacheが切り替わったなどなどの報告が入り、もしかすると面倒ごとかなと思いながら対応することになった。
起きていたこと
既にAWSからも公開されていることであるが、今回は2019年8月に起きた障害と類似するタイプの障害だった。
東京のAZの一つ、apne-az1
にてサーバ冷却装置の電力供給にトラブルがあり、EC2の機能が縮退したというもので、影響を受けたのはEC2とEC2に乗っている多くのサービスであった。
前回、ブログにてAZ障害が起きた場合のことについて呑気に書いていたが、今回の対応ではこのタイプのAZ障害では困ったことが起きることがわかった。
一番困ったのは、EC2が縮退しているのに、アラートが上がってこないことだった。 AWSで構築しているサービスはEC2やEC2を使って組まれたサービスに乗っているものが多く、例えば最近だとECSをFargateで動かして、ALBを使ってサービス提供する、みたいな構成をとっている。
Fargateは自動的にサービスを起動する仕組みを持っていたり、ALBもマネージドなロードバランサーでよしなに動いてくれると思っていたが、今回のトラブルではAWS側の仕組みではうまく切り離しができていなかったように思える。要はAZは死んでいるにも関わらず、サービスとしてはまだ「生きている」判定がされており、切り離しがうまくいっていないような動作をしていた。
これは大変困った。普段利用しているCloudwatchなどでは死んだサーバやサービスが判別ができず、ALBについては自分たちの管理下ではないので、死んだALBに当たることを避けるのは容易ではなかった。
仕方がないので、 apne-az1
に属しているすべてのEC2ベースで動いている、と思われるサービスを使わないようにしていくしかなかった。
しかしこれは全くもって簡単ではなかった。というのはAZ障害の場合は当然綺麗に自動切り離しが行われる前提で設計をしていたので、今回みたいに自動的に切り離しができないことを想定していなかった。
これはTerraformやCloudFormationを使っていたとしても容易ではないと思う。切り離し方がサービスによってかなり違うので多くのリソースを使っているほどきり替えが難しかったはず。 ALBなどはSubnetを指定する設定があるし、Fargate は placement constraints を設定する必要があった。また、ENIなどもZoneに依存している。RDSはMaster昇格をする?それは大変だ。 もちろん一気にすべてのリソースがパタパタ切り替わったらそれはそれで困ったかもしれないが、死んでいるのか生きてるのか判別かつかないというのは相当参った。中途半端に生きているサービスを綺麗に切り替えるは相当難しい。
今回の反省
サービスは綺麗に死ぬとは限らないことを見落としていたこと
AWSで構築するときにはMulti-AZ構成を取っていたし、自動で切り替える仕組みも設定していたが、自動で切り替わらない場合にどう切り離すかまで考慮していなかった。 Terraformで構築は自動化していたが、AZの一つだけを切り離すにはどうすればよいか、俯瞰して見ていたわけでもなく、実際の切り離し作業にはかなり手間取ってしまった。 というより、AZの一つが死んだ場合にTerraformなどで隔離できるように書いておくというのはかなり難しい気がしている。 これはまだ考えていないが、多分今更書き直すのは難しい気がしている。最初から切り離せるように書いておかないと難しいが、そんなところまで考えてはなかったというのが正直なところだ。
マイクロサービス化を進める過程でアカウントを分割したことで対応が煩雑になった
これはそれはそうだろうという話だが、権限やコスト分割の観点からアカウント分割をしてきたことで、分割したアカウントの数だけ同じことをする羽目になってしまった。 アカウント単位で閉じない、広範囲のトラブルに対しては複数アカウントに分けたことで対応箇所がばらけてしまい、設定反映に手間取ることになった。 実際このようなケースを想定してすべてのアカウントに対してAZを切り替えられるように設定を書くのはかなり困難と思われるが、毎年起こるような問題であれば対応しなければならない。
終わりに
前回書いた記事でいうと「問題がおきたマシンの切り離しの仕組みがうまく動かない」ケースに該当して大変な目にあった。 こう一年半に一回くらいの確率で起きる問題だと、対処できるようにしておかないといけないし、今回の件を踏まえて自動切り離しとは別の観点で手動でAZ切り離しができるように仕組み化していく必要があるかもしれない。
今回AZ障害だからMultiAZだから影響がないっていうのは間違いで、中途半端に死んだ結果切り離しがうまく行かず、MultiAZ構成を取っていても影響が出てしまうというトラブルだったということだけ書いておきたい。
死ぬときは綺麗に死んでくれないってのは物理時代もスイッチやNICでよくあったし、こういうのが一番困るね。やれやれ
おまけ
プリンセス・プリンシパル Crown Handler 超良作だから観てね
ISUCON10 本戦に鍋部2人前のインフラ枠で参加しました
諸事情でISUCON10の本戦に並行チーム枠として参加させていただきました。本当に疲れたのでざっくりとまとめだけ。
続きを読むJenkins Configrations as Code と Groovy サンドボックス
サマリー
- JenkinsにはGroovyを実行する機能があるが、任意のコードの実行を防ぐため、実行できるメソッドを制限する Groovy Sandbox という仕組みがある。
- Groovy Sandbox を有効にするには、Authorize Project plugin で特定のユーザ権限で動作させる必要がある
- cronのように自動起動するジョブは SYSTEM として実行され Sandbox で実行できないため
- Sandboxで実行する場合、許可するメソッドを事前に設定しておき、意図しないメソッドの呼び出しに気付けるようにする運用になる
- JCasC には Authorize Project plugin と Script Security Plugin の scriptApproval を設定する方法がない
- とはいえ、この設定は一度設定したら変更することもないはずなので、Jenkins起動前に事前にXMLを入れておけばいいよという話
Authorize Project plugin
設定例
<?xml version='1.1' encoding='UTF-8'?> <jenkins.security.QueueItemAuthenticatorConfiguration> <authenticators> <org.jenkinsci.plugins.authorizeproject.ProjectQueueItemAuthenticator plugin="authorize-project@1.3.0"> <strategyEnabledMap> <entry> <string>org.jenkinsci.plugins.authorizeproject.strategy.SpecificUsersAuthorizationStrategy</string> <boolean>true</boolean> </entry> <entry> <string>org.jenkinsci.plugins.authorizeproject.strategy.TriggeringUsersAuthorizationStrategy</string> <boolean>false</boolean> </entry> <entry> <string>org.jenkinsci.plugins.authorizeproject.strategy.SystemAuthorizationStrategy</string> <boolean>false</boolean> </entry> <entry> <string>org.jenkinsci.plugins.authorizeproject.strategy.AnonymousAuthorizationStrategy</string> <boolean>false</boolean> </entry> </strategyEnabledMap> </org.jenkinsci.plugins.authorizeproject.ProjectQueueItemAuthenticator> </authenticators> </jenkins.security.QueueItemAuthenticatorConfiguration>
Script Security Plugin
- Jenkinsで実行されるGroovyスクリプトを任意実行できないように呼び出せるメソッドの制限ができるプラグイン
- Job DSL plugin などでGroovyを実行する際にも適用されるため、このプラグインを入れている場合、Groovy Sandbox で実行する必要が出てくる
- 許可するメソッドは scriptApproval のWebUI上で設定できるが、Administrator権限が必要。
- Administrator権限を持っているとJenkinsを任意に操作できるため、scriptApprovalのためだけにAdministrator権限を与えたくないと思う
- scriptApprovalは単なるXMLの設定であり、事前にどのメソッドの呼び出しを許すかわかっているのであれば、同じくJENKINS_HOMEにおいておけば良い。
<?xml version='1.1' encoding='UTF-8'?> <scriptApproval plugin="script-security@1.66"> <approvedScriptHashes/> <approvedSignatures> <string>staticField groovy.io.FileType DIRECTORIES</string> </approvedSignatures> <aclApprovedSignatures> <string>method groovy.lang.GroovyObject getProperty java.lang.String</string> <string>method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object</string> </aclApprovedSignatures> <approvedClasspathEntries/> <pendingScripts/> <pendingSignatures/> <pendingClasspathEntries/> </scriptApproval>