21
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-02-24

fabricでplayアプロケーションのデプロイ自動化したり
fabric内でAmazon EC2, ELBを操作したりしたので、そこらへんをかいつまんで紹介したいと思います。

fabricって何?

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
スクリーンショット 2015-02-24 20.46.00.png

$ fab deploy

スクリーンショット 2015-02-24 20.55.23.png

スクリーンショット 2015-02-24 20.53.54.png

おわりに

今回 fabricも botoも初めて触りましたが学習コストが低い割に便益が高いと思いました。
デプロイサーバからリモートホストへのデプロイ版も作ったので、時間のあるときにそちらも
blogにおこしたいと思います。

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

21
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?