1.5.0 は受付済みと表示したが、倉庫に注文はなかった
目標
releases/<バージョン>とcurrentのシンボリックリンクでデプロイし、ヘルスチェックとスモークの試験を経て、失敗したら直前のバージョンに戻して記録する、スクリプトを作ります。保存の整理で、currentと直前のバージョンを守ります。
なぜ重要なのか
ヘルスチェックは、プロセスが生きているかを、スモークは、業務が1件最後まで通るかを尋ねます。顧客の前回のデプロイは、前者の質問だけをして終わり、一晩中、注文を失いました。 バージョンをディレクトリで分け、シンボリックリンク1つで切り替えれば、ロールバックも同じ1つの動作になります。ただし、切り替える瞬間にcurrentが消えたり、古いプロセスが代わりに緑のランプをつけたり、整理のスクリプトが戻る先のバージョンを消したりすれば、構造があっても事故が起きます。 採点ツールは、毎回異なるバージョン番号で、正常なビルド・スモークでだけ壊れるビルド・起動した途端に落ちるビルド・起動が遅いビルドを作って、あなたのスクリプトを実行し、ログの文言ではなく、実際に応答しているバージョンとシンボリックリンクを、測り直します。
想定所要時間は60分です。デフォルトのセッションが終わる前に、+時間で延長してください(最大180分)。セッションが終わると、/rootのファイルは消えるので、スクリプトは別に保管してください。
ステップ
- ビルドorderapp-1.4.0.tar.gzを、/root/site/releases/1.4.0に展開し、currentのシンボリックリンクが、そのディレクトリを指すようにします(リンク: /root/site/current)。
- 切り替えのスクリプトを作ります(引数はROOTとバージョン、ファイル: /root/release/switch.sh)。存在しないバージョンは拒否し、currentが消える瞬間なしに、一度に切り替えます。
- デプロイのスクリプトを作ります(引数はROOT・ビルド・PORT、ファイル: /root/release/deploy.sh)。展開・切り替え・古いプロセスの入れ替え・ヘルスチェック(version確認)・スモークの注文・deploy.logの記録まで、正常なビルドで通します。
- deploy.shが、スモークに失敗したら、currentを直前のバージョンに戻し、そのバージョンを再び起動してから、rolled_back・reason smokeを記録し、終了コード2で終わるようにします。
- deploy.shのヘルスチェックで、起動した途端に落ちるバージョンはhealthで戻し、2.5秒遅れて起動する正常なバージョンは、待ってデプロイするようにします。待機の上限は10秒です。
- 整理のスクリプトを作ります(引数はROOTとKEEP、ファイル: /root/release/prune.sh)。mtimeが最新のKEEP個を残しますが、currentと直前のバージョンは、削除する一覧から除きます。
- 4回のデプロイ(最後はスモークの失敗)と、保存1の整理を続けて行っても、記録・残ったバージョン・応答しているバージョンが合っているかを確認します。
- 顧客の1.5.0を、deploy.shで/root/site(ポート8480)にデプロイして結果を見て、事件を記録します(ファイル: /root/release/incident.json)。
参考
- 用意するもの: 実行契約 /opt/lab/p1a-release/CONTRACT.md、顧客メモ /opt/lab/p1a-release/README.md、ビルド /opt/lab/p1a-release/builds/
- ビルドの中を見る: tar -tzf <ビルドファイル>、tar -xzOf <ビルドファイル> VERSION
- シンボリックリンクの確認: readlink /root/site/current、ls -la /root/site
- 直接の試験: bash /root/release/deploy.sh /tmp/try /opt/lab/p1a-release/builds/orderapp-1.4.0.tar.gz 18480; cat /tmp/try/deploy.log
- よくある間違い: ln -sfで-nを抜かす、アプリを起動するときに出力をファイルにリダイレクトしない(スクリプトが終わらない)、ヘルスチェックでバージョンを確認しない、最新順だけで整理する。
- 試験用に起動したアプリは、終わったら、そのデプロイのルートのshared/app.pidで選んで停止します。
最初のバージョンを手で展開してcurrentを設定する
ビルド1.4.0を/root/site/releases/1.4.0に展開し、currentのシンボリックリンクが、そこを指すようにしてください(リンク: /root/site/current)。
tarの-Cで、展開する場所を決めます。currentは、ディレクトリのコピーではなく、ln -sで作ったリンクでなければなりません。相対パスのリンク(releases/バージョン)は、デプロイのルートをまるごと移しても壊れません。
currentを一度に切り替える、切り替えのスクリプト
引数がROOTとバージョンの切り替えスクリプトを作ってください(ファイル: /root/release/switch.sh)。存在しないバージョンは、0以外のコードで拒否し、currentは、消える瞬間なしに切り替わる必要があります。
rename(2)は、既存の名前を原子的に置き換えます。同じディレクトリに一時的なリンクを作り、currentの上に移してください。mvには、対象がディレクトリを指していても、その中に入れないようにするオプションがあります。lnを使うなら、-nを抜かさないでください。
展開して、切り替えて、起動して、スモークまで行う
引数がROOT・ビルド・PORTのデプロイスクリプトを作って(ファイル: /root/release/deploy.sh)、正常なビルドをデプロイし、deploy.logにdeployedの1行を残してください。
バージョンは、tarの中のVERSIONから読みます。古いプロセスは、shared/app.pidで停止し、新しいアプリは、nohupと出力のリダイレクトで、バックグラウンドに起動します。ヘルスチェックの応答のversionが新バージョンか、スモークの注文(SMOKE-の接頭辞)がGETで読み直せるかを見ます。記録は、jq -cnで作ると、引用符の間違いがありません。
スモークが壊れたら、直前のバージョンに戻す
スモークの失敗時に、直前のバージョンへ切り替えて再起動し、rolled_back(reason smoke)を記録したあと、終了コード2で終わるようにしてください(ファイル: /root/release/deploy.sh)。
切り替えの前に、currentが指していたバージョンをpreviousとして覚えておかないと、戻る先がわかりません。ロールバックも、switch.shで行います。失敗したreleases/バージョンは、調査用に残します。
落ちたバージョンは素早く、遅いバージョンは待って
ヘルスチェックが、最大10秒待ちつつ、プロセスが落ちたら、すぐにhealthで戻すようにしてください(ファイル: /root/release/deploy.sh)。
1回失敗しただけで諦めると、起動が遅いだけの正常なバージョンを戻してしまいます。逆に、すでに落ちたプロセスを、10秒待つ理由はありません。kill -0 PIDは、シグナルを送らずに、プロセスがあるかどうかだけを確認します。
整理していて、戻る先のバージョンを消さない
引数がROOTとKEEPの整理スクリプトを作ってください(ファイル: /root/release/prune.sh)。mtimeが最新のKEEP個以外は消しますが、currentと直前のバージョンは残します。
ロールバックのあとは、currentが最新ではありません。直前のバージョンは、deploy.logで、resultがdeployedで、versionがcurrentである最後の行のpreviousです。ls -tは、更新時刻の新しい順に並べます。
4回デプロイして整理しても、合っている記録
4回のデプロイ(最後はスモークの失敗)と、保存1の整理を続けて行っても、記録と実際の状態が合うようにしてください(ファイル: /root/release/deploy.sh、/root/release/prune.sh)。
ロールバックしたデプロイのpreviousは、戻った先のバージョンです。そのあとで整理すると、最新の1つ(失敗したバージョン)と、currentと直前のバージョンが残る必要があります。一時的なデプロイのルートで、続けて自分で実行してみてください。
顧客の1.5.0をデプロイして、事件の記録を残す
deploy.shで1.5.0を/root/site(ポート8480)にデプロイし、結果を、failed_version・restored_version・reason・deploy_log_line・health_passedとして記録してください(ファイル: /root/release/incident.json)。
deploy_log_lineは、/root/site/deploy.logで、ロールバックの記録が何行目か(1から数えます)です。health_passedは、このバージョンがヘルスチェックを通過したかを書きます。app.logと記録のreasonから判断してください。採点ツールは、incident.jsonを、deploy.log・current・ビルドの元データと照合します。