わんばんこ、しもむらです。
現在は、つぶやきGANMA!というサービスを開発・運用してます。
つぶやきGANMA!のデプロイ自動化するにあたって、
fabricでplayアプロケーションのデプロイ自動化したり
fabric内でAmazon EC2, ELBを操作したりしたので、そこらへんをかいつまんで紹介したいと思います。
fabricって何?
http://fabric-ja.readthedocs.org/en/latest/tutorial.html
Fabricとは:
> ・コマンドライン 経由で 任意の Python 関数 を実行するツールです。
> ・(低レベルライブラリの上に構築された)サブルーチンのライブラリで、
SSH経由で簡単に、かつPython風に シェルコマンドを実行します。
公式ドキュメントだとわかりにくく言ってますが、
この2つを組み合わせると、リモートサーバへの処理を自動化できますよってことです。
本番デプロイ手作業とか怖すぎるし、面倒なのでどんどん自動化しちゃいましょう。
Capistranoとの比較は下記の記事を参考にしました。
http://dekokun.github.io/posts/2013-05-21.html
前提
○ デプロイの流れはこんな感じ
ソースの更新
↓
ELBからEC2を外す
↓
play stop
↓
playデプロイ
↓
play start
↓
ELBにEC2をつける
↓
デプロイおわり
環境
Play framework 2.3.x
activator 1.2.10
デプロイ先のOSはいずれも Amazon Linux
Fabric 1.8.1
fabric インストール
centOS
sudo yum -y install python python-devel python-setuptools gcc
sudo easy_install pip
sudo pip install fabric==1.8.1
Mac
sudo easy_install fabric
事前に押さえておきたい内容
local -> ローカルのシェルコマンドを呼び出し
env -> env変数(グローバルなシングルトン)、タスク間の情報共有に使う
lcd -> ローカルのカレントディレクトリ指定
execute -> taskを実行
デコレータ
@task -> 有効なタスクとして読み込ませる
@runs_once -> ラップされた関数が複数回実行されないようにする
とりあえずfabfile をはっちゃいます。 下に部分的に説明書いてます。
fabfile
from fabric.api import * from fabric.decorators import runs_once import boto.ec2.elb env.hosts = ['localhost'] env.user = "username" env.project_root = "path/to/project" env.build_path = "target/universal/stage/bin/project_name" env.config_path = env.project_root + '/conf/application.conf' env.branch = 'master' env.region = "ap-northeast-1" env.elb_name = 'your_elb_name' env.aws_access_key = 'your_aws_access_key' env.aws_secret_key = 'your_aws_secret_key' ## ビルド @task @runs_once def build_play(): """ activator clean stage""" with lcd(env.project_root): local("activator clean stage") ## activator起動系 @task def play_start(): """ activatorを本番モードで起動""" with lcd(env.project_root): local("%(bin)s -Dconfig.file=%(conf)s &" % {"bin":env.build_path, "conf":env.config_path}) @task def play_stop(): """ 本番モードで起動中のプロセスをkill""" if local('ps -ef | grep "%(root)s/target/universal/stage" | grep -v grep | wc -l' % {"root":env.project_root}, capture=True) == "1": local("kill `cat %(root)s/target/universal/stage/RUNNING_PID`" % {"root":env.project_root}) @task def play_restart(): """ 本番モードでの再起動""" execute(play_stop) execute(play_start) ## AWS ELB操作 def get_ec2_id(): """ ec2のinstanceID取得""" env.ec2_id = local("curl http://169.254.169.254/latest/meta-data/instance-id", capture=True) def conn_elb(): env.conn_elb = boto.ec2.elb.connect_to_region( env.region, aws_access_key_id = env.aws_access_key, aws_secret_access_key = env.aws_secret_key) def get_elb(): execute(conn_elb) env.elb = env.conn_elb.get_all_load_balancers(env.elb_name)[0] def remove_ec2(): """ ec2(自分)をELBから外す""" execute(get_elb) execute(get_ec2_id) env.elb.deregister_instances(env.ec2_id) def add_ec2(): """ ec2(自分)をELBに追加する""" execute(get_elb) execute(get_ec2_id) env.elb.register_instances(env.ec2_id) @task @runs_once def update(branch=env.branch): """ env.branchに同期する。ブランチ指定場合-> fab update:branch=branch_name""" with lcd(env.project_root): local("git fetch") local("git reset --hard HEAD") local("git checkout " + branch) local("git pull") ## デプロイ @task @runs_once def deploy(branch=env.branch): """ ブランチ指定-> fab deploy:branch=branch_name""" execute(remove_ec2) execute(play_stop) execute(update,branch=branch) execute(build_play) execute(play_start) execute(add_ec2)
Playデプロイ
playのプロセスをバックグラウンドで起動
@task def play_start(): """ activatorを本番モードで起動""" with lcd(env.project_root): local("%(bin)s -Dconfig.file=%(conf)s &" % {"bin":env.build_path, "conf":env.config_path})
playのプロセス停止
playアプリケーションでプロセスを管理してるRUNNING_PID ファイルを使ってプロセス削除します。
@task def play_stop(): """ 本番モードで起動中のプロセスをkill""" if local('ps -ef | grep "%(root)s/target/universal/stage" | grep -v grep | wc -l' % {"root":env.project_root}, capture=True) == "1": local("kill `cat %(root)s/target/universal/stage/RUNNING_PID`" % {"root":env.project_root})
ELB操作
boto
fabricから ELBを操作するのに boto(AWS SDK for Python) というライブラリを使ってます。
API document
https://github.com/boto/boto
botoをインポート
import boto.ec2.elb
AWSに接続して、env.elb_nameの名前のELBを取得
def conn_elb(): env.conn_elb = boto.ec2.elb.connect_to_region( env.region, aws_access_key_id = env.aws_access_key, aws_secret_access_key = env.aws_secret_key) def get_elb(): execute(conn_elb) env.elb = env.conn_elb.get_all_load_balancers(env.elb_name)[0]
EC2(自分)のインスタンスIDを取得
EC2は色々と自分のメタ情報を取得できるみたいですね。知っとくとベンリ
http://d.hatena.ne.jp/rx7/20100605/p1
localの場合は、capture=Trueしないと変数に入らないので注意
def get_ec2_id(): """ ec2のinstanceID取得""" env.ec2_id = local("curl http://169.254.169.254/latest/meta-data/instance-id", capture=True)
ELBから EC2(自分)を外す
def remove_ec2(): """ ec2(自分)をELBから外す""" execute(get_elb) execute(get_ec2_id) env.elb.deregister_instances(env.ec2_id)
ELBから EC2(自分)を付ける
def add_ec2(): """ ec2(自分)をELBに追加する""" execute(get_elb) execute(get_ec2_id) env.elb.register_instances(env.ec2_id)
demo
fabfileを設置してるカレントディレクトリで fabコマンドを実行します。
ブロジェクルートに置いても、/home/user配下でもいいと思います。
@taskデコレータを付けてるものが listに表示されます。
$ fab --list
$ fab deploy
おわりに
今回 fabricも botoも初めて触りましたが学習コストが低い割に便益が高いと思いました。
デプロイサーバからリモートホストへのデプロイ版も作ったので、時間のあるときにそちらも
blogにおこしたいと思います。
最後まで読んでいただき、ありがとうございました。
Qiita原文
Qiitaの自己投稿の転載になります。
Qiitaの記事はこちら