どうも、cloudpack の 夜の Player かっぱ(@inokara)です。
はじめに
昨晩「Play2 アプリケーションをデプロイを fabric でやろうと思ったのでメモ」の続きです。リモートホストへのデプロイをやりたいと思います。
参考
リモートホストへのデプロイ(1)
fab ファイルの改修
以下、手抜きの全文掲載。
from fabric.api import local, run, env, cd, execute, put, sudo def default_path(): env.app = "app_name" env.app_repo = "https://github.com/user/app_name.git" env.snap_shot = "%(app)s-1.0-SNAPSHOT" % { 'app':env.app } env.base_dir = "/path/to/prod" env.deploy_dir = "/path/to/deploy" env.current_path = "%(base_dir)s/current" % { 'base_dir':env.base_dir } env.releases_path = "%(base_dir)s/releases" % { 'base_dir':env.base_dir } def set_path(): execute(default_path) env.releases = sorted(run('ls -x %(releases_path)s' % { 'releases_path':env.releases_path }).split()) if len(env.releases) >= 1: env.current_revision = env.releases[-1] env.current_release = "%(releases_path)s/%(current_revision)s" % { 'releases_path':env.releases_path, 'current_revision':env.current_revision } if len(env.releases) > 1: env.previous_revision = env.releases[-2] env.previous_release = "%(releases_path)s/%(previous_revision)s" % { 'releases_path':env.releases_path, 'previous_revision':env.previous_revision } def setup(): execute(default_path) run('mkdir -p %(base_dir)s/releases' % { 'base_dir':env.base_dir} ) run('mkdir -p %(deploy_dir)s' % { 'deploy_dir':env.deploy_dir }) def dist_package(): execute(set_path) with cd('%(deploy_dir)s/' % { 'deploy_dir':env.deploy_dir }): run('if [ -d %(app)s ]; then rm -rf %(app)s ; fi' % { 'app':env.app }) run('git clone %(app_repo)s' % { 'app_repo': env.app_repo }) with cd('%(deploy_dir)s/%(app)s' % { 'deploy_dir':env.deploy_dir, 'app':env.app }): run('play dist') def deploy(): execute(set_path) execute(dist_package) from time import time env.current_release = "%(releases_path)s/%(time).0f" % { 'releases_path':env.releases_path, 'time':time() } run('cp %(deploy_dir)s/%(app)s/target/universal/%(snap_shot)s.zip %(base_dir)s/' % { 'deploy_dir':env.deploy_dir, 'app':env.app, 'base_dir':env.base_dir, 'snap_shot':env.snap_shot }) with cd ("%(base_dir)s/" % { 'base_dir':env.base_dir }): run('unzip %(snap_shot)s.zip -d %(current_release)s/' % { 'current_release':env.current_release, 'snap_shot':env.snap_shot }) run('ln -nfs %(current_release)s %(current_path)s' % { 'current_release':env.current_release , 'current_path':env.current_path }) def app_status(): execute(set_path) sudo('supervisorctl status %(app)s' % { 'app':env.app }, pty=True, shell=False) def app_start(): execute(set_path) with cd('%(current_path)s/%(snap_shot)s/' % { 'current_path':env.current_path, 'snap_shot':env.snap_shot }): run('if [ -f RUNNING_PID ]; then rm RUNNING_PID ; fi') sudo('supervisorctl start %(app)s' % { 'app':env.app }, pty=True, shell=False) def app_stop(): execute(set_path) sudo('supervisorctl stop %(app)s' % { 'app':env.app }, pty=True, shell=False)
def default_path()
だけ弄れば良いようになっています。
動作イメージ(処理の流れ)
図を書きながら気付いたのは play dist
までの処理はデプロイ用のホストで実行、生成された zip ファイルを put するという流れでも良かったのかなってこと…。まあ、いいや。
ポイント
def default_path()
だけ弄れば良いようになっています(デプロイ先のパス、git のリポジトリ URL 等)- fabric を介して
sudo
を実行させる場合にはpty=True, shell=False
を付ける必要がある - アプリケーションの起動(
app_start
)、停止(app_stop
)、ステータス確認(app_status
)は後述の通り supervisord 依存です(すいません)
リモートホストへのデプロイ(2)〜リモートホスト側の準備〜
すいません
自分の実力では fabric 単体では実現することは出来ませんでした。
当初はバックグラウンドでアプリケーションを起動させる為に以下のように書いていましたがリモートホストでは正常に起動しませんでした。
def app_start(): execute(set_path) with cd('%(current_path)s/%(snap_shot)s/' % { 'current_path':env.current_path, 'snap_shot':env.snap_shot }): run('if [ -f RUNNING_PID ]; then rm RUNNING_PID ; fi') with lcd('%(current_path)s/%(snap_shot)s/bin/' % { 'current_path':env.current_path, 'snap_shot':env.snap_shot }): run('./hoge &')
Fabric ドキュメントでも以下のように言及されています。
Fabric はリモート側で run や sudo (see also) の呼び出しでそれぞれシェルを起動します。run or sudo (see also)。シェルを通してのバックグランドプロセスは期待通りに動きません。バックグランドプロセスが終了するまで呼び出されたシェルから抜けられないかもしれません。これでは Fabric が順番に実行することができません。
うう、そうなんですな…ってことで、supervisord 等のプロセスマネージャを利用を推奨されているので supervisord を利用してみたいと思います。
supervisor の設定
sudo pip install supervisor
以前「朝の 3 分ハッキング(1) Supervisor で Sinatra アプリケーションをデーモン閣下する」に supervisor の設定については書いているのでそちらを参考にします。アプリケーションの起動設定については以下のように設定します。
[program:play-test-app] command = /path/to/current/play-test-app-1.0-SNAPSHOT/bin/play-test-app process_name = play-test-app user = app-user autostart = true autorestart = true
設定したら /etc/init.d/supervisord start
を実行して supervisord を起動しておきましょう。
sudo の設定
アプリケーション用のユーザーが supervisorctl を実行する必要があるので /etc/sudoers.d/ の下に以下のように設定したファイルを置きます。
app-user ALL=(ALL) NOPASSWD:/usr/bin/supervisorctl
デモ
とりあえず静止画で…。
deploy
fab -H ${APP_HOST} -u ${APP_USER} -f remote_deploy.py deploy
play dist
が終わったら zip を展開して最新のリリースディレクトリを current ディレクトリとして設定(シンボリックリンクを設定)。
app_start
fab -H ${APP_HOST} -u ${APP_USER} -f remote_deploy.py app_start
app_status
fab -H ${APP_HOST} -u ${APP_USER} -f remote_deploy.py app_status
app_stop
fab -H ${APP_HOST} -u ${APP_USER} -f remote_deploy.py app_stop
お疲れ様でした
fabric を利用して play2 のアプリケーションをリモートホストへのデプロイをひと通り試してみました。基本的にはコマンドの羅列なので悩むことはなかったのですが、アプリケーションの起動に関してはバックグラウンドで起動させるのが fabric 単体ではちょっと実現出来なかった(実現方法を見つけることが出来なかった)のがちょい残念でしたが、実際の運用を想定するとアプリケーションのプロセス管理を何らかの方法で必要となるので、supervisord を fabric から利用するのも悪くはないのかなって考えています。
ということで、おやすみなさい。
元記事はこちらです。
「Play2 アプリケーションをデプロイを fabric でやろうと思ったのでメモ(2)」