この記事の問題はopsworks-agentが自動でアップデートされてしまうというのが原因で引き起こされていましたが、現在(2015/07/06)は手動で利用するバージョンを指定してアップデート出来るようになったのでこの記事の問題が置き無さそうです!!ヨカッタですね!!
http://blogs.aws.amazon.com/application-management/post/TxLZ972TREEZ4A/Manage-agent-updates-on-AWS-OpsWorks-managed-instances
OpsWorksを利用していて意図せず本番環境のRubyのバージョンが上がってエラーが出るという障害に遭遇してしまったので詳細を共有します。
TL;DR
OpsWorksは、こちらが意図しないタイミングでサーバー内に保存されているCustom Cookbooksを最新に更新してくるので、Stackに設定するCustom Cookbooksは常に本番反映されても良いものだけを指定しておくと良い。
opsworks-agent
OpsWorksで管理するEC2インスタンス上にはopsworks-agentが動いているが、たまにこいつがAWS側によって自動で更新される。更新されると、opsworks-agent自身がアップデートされるだけでなく、EC2インスタンス内にキャッシュされているcustom cookbookも最新に更新されてしまう。これが今回遭遇した障害のトリガーとなった。
updateのログは/var/log/aws/opsworks/以下に残っているので生のログが観たい人はこちらをどうぞ。↓ StackのInstancesから個別のインスタンスごとのログを見ると、update_agentコマンドが走っていることがわかる。
意図せず障害がおきるシナリオ
前提
- Railsアプリの本番環境Stackと開発環境Stackをそれぞれ別々にOpsWorksで構築している
- それぞれ同じCustom Cookbooksを利用していて、どちらもmasterブランチを使用している
- RailsアプリはCustom Layerで実現していて(こちらを参照)、RubyのバージョンはCustom Cookboosk内のROOT/ruby/attributes/customize.rbの中でベタ書きで指定していた
- configureライフサイクルには、rails::configureが指定されている
- Custom Cookbooksは、自分でOpsWorksのUpdate Custom Cookbooksコマンドを実行しないと更新されないものだと思っていた
障害発生までのシナリオ
以下の様な順で実際に障害が発生した
- 開発環境Stack向けにRuby2.2をインストールして検証するためCustom Cookbooksのruby/attributes/customize.rbに
normal[:opsworks][:ruby_version] = '2.2'
と書いた差分(その他実際にはbundler,rubygemsのバージョンも上げてる)をmasterにマージして開発環境Stack上で反映した - 本番環境Stackへの反映は、開発環境Stackで検証が終わってから暇な時にでも、と思ってそのままGWの休みに突入した
- GWが明けて突然update_agentが実行された
- なんかの拍子(オートスケールなどでEC2インスタンスが立ち上がるなど)にconfigureライフサイクルイベントが各本番のEC2インスタンス上で発火
- 2.2を指定したcustomize.rbのせいでRubyのバージョンがアップデートされ、2.1のコードが消える
- アップデートされてしまった理由は後ほど
- Ruby2.1の時に起動されたunicornが標準ライブラリのrexml/documentが消えたせいでLoadErrorエラーを発し始める !!!障害発生!!!
なぜ勝手にRubyがアップデートされた?
結論から言うとrails::configureレシピが間接的にRubyのバージョンを上げる処理を呼んでいた。
- update_agentが実行された時に開発環境Stack向けに作って本番環境Stackへの反映をしていなかったcookbooksが本番環境Stackに続々と配置された
- configureライフサイクルイベントのタイミングでrails::configureレシピが実行された
- rails::configure -> deploy::default -> dependencies::default -> ruby::default という順でinclude_recipeが連鎖し、ruby::default内でバージョンチェックが行われた結果、現在インストールされているRubyのバージョンが、chefのnodeオブジェクトで指定されているバージョンより古いため更新処理が走った
rails::configureという名前からは想像もつかない処理だぜ!!!!!!!!
まとめ
今回の学びとしてはOpsWorksは以下の様な挙動があるので、OpsWorksを利用している人たちは以下の点を気をつけるべき。
- OpsWorksはこちらが意図しないタイミングでcustom cookbooksの更新を行ってくることがある
- OpsWorksのconfigureライフサイクルイベントはこちらが意図しない時にも頻繁に起きうる
- RailsLayer標準のrails::configureはRubyのバージョン更新までも孕んだ割りと壮大なレシピ
自分たちの失敗としては、本番環境Stackと開発環境Stackで同じブランチを向いたCustom Cookbooksを利用していた点で、再発防止策としては本番環境Stackで利用するCustom Cookbooksの向き先をmaster以外の本番環境用ブランチに設定し、開発環境は元のままmasterを向いた状態にしたい。