Edited at

fabric で playをスタンドアロンデプロイしてみた   【botoでAWS操作】【Playデプロイ】

More than 3 years have passed since last update.

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

# -*- coding: utf-8 -*-

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: http://boto.readthedocs.org/en/latest/

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におこしたいと思います。

最後まで読んでいただき、ありがとうございました。