読者です 読者をやめる 読者になる 読者になる

Drafts

@cm3 の草稿置場 / 少々Wikiっぽく使っているので中身は適宜追記修正されます。

forever(Node.js) × nginx で 0-Downtime

Blue-Green Deployment の 簡易版。Docker とかで切り替えるのではなく、フォルダを空にして毎回そこに環境を構築するだけで、サーバ環境自体は使いまわしているので、Immutable ではない。

私は心配性なので(?)

  • app8001
  • app8002
  • app8003

ディレクトリの3運用。それぞれを次のラベルが行き来する。

  • prev
  • (無印)メイン
  • next

だって、Blue-Green だけだったら、

  1. Green で検証して、Live の Blue を Green に切り替え
  2. Blue で次期バージョンの検証を始める
  3. Green で致命的なバグが見つかる、もう一つ前のバージョンでは大丈夫だったのに…

ってなったら戻せないじゃん?まあ、そんなことは滅多にないのかもしれないんだけどさ。

そういうわけで、nodejs アプリの deploy でそういう環境を作っている。後々 Docker に入れるんだけど、今は fossil ってリポジトリも実行ファイルもそれぞれ 1ファイルで持ち運べる git 的なのを使っている。1から環境構築を繰り返すことにこだわらないならば、3つのディレクトリに加えて、親の fossil リポジトリを持っておけば、next になったフォルダでそこから pull するだけで済む。

settings.jsondeploy.sh の実行で上書き生成されるようになっていて、ポート番号とかをインタラクティブに答える(prev に使っていたディレクトリの中身を next 用に再構築する時に毎回この作業が起こる。ポート番号は変わらないが、1から環境構築を繰り返すのが Blue-Green Deployment っぽいと思ってそういう仕組みにしてみた)。

read -p "port: " port
port=${port:-8000}
echo -e "{\n \"port\": ${port}\n}" > settings.json

てな具合で、デフォ値を 8000 にしてユーザの入力を受け付けることができる。

参考:

それを本体で

var settings = require('./settings.json');

で呼び出して使っている。お気づきのとおり、初めのディレクトリ名はポート番号をそのまま指している。appの部分はアプリケーション名。参照先のポートを nginx で切り替えるのが切り替えになっている。

location ~ ^/app-prev($|/.*$) {
        allow xxx.xxx.xxx.xxx;  
        deny all;
    proxy_pass http://127.0.0.1:8001$1;
}

location ~ ^/app($|/.*$) {
    proxy_pass http://127.0.0.1:8002$1;
}

location ~ ^/app-next($|/.*$) {
        allow xxx.xxx.xxx.xxx;
        deny all;
    proxy_pass http://127.0.0.1:8003$1;
}

の数字部分がくるくる。allow, deny は prev と next を IP でアクセス制限してます。

deploy ごとのモジュールのインストール

package.json の dependencies が書いてあれば、npm install だけで行ける。

参考:

forever で uid にフォルダ名を付ける

forever start --uid ${PWD##*/} server.js

参考:

ユーザデータとかどうするか

データを受け取ってセーブするだけのサーバも切り分けた上で Blue-Green Deployment するのが最強なんだろうけど、Cloud FormationによるBlue-Green Deployment - Dev io mtup11 003 見てもRDSは妥協って書いてたり。僕もそこは妥協してます。あと、一応データ更新に失敗した通知はするようにしている。できるなら lichess.org みたいに、アップデート10秒前に "Brace for Impact." って表示するのはカッコいいと思ってるし、そういうの実装しようかなw