ゆーかん

徒然日記

fabricでデプロイ自動化

独学マンなので、書き方等ご指摘頂けますと幸いです。

背景

までできていた。デプロイ作業を失敗しそうなので、自動化した時の備忘録。自動デプロイツールとしてfabricを利用。

ちなみに現状、fabric本家はpython3系をサポートいません。参考: FabricのPython3への対応についてそのためforkされたprojectから、インストールする必要があります。

pip3 install fabric3

ディレクトリ構成はこんな感じ。シンプル。

...
├── fabfile
│   ├── __init__.py  # module読み込み
│   ├── deploy.py    # 基本処理の記述
│   ├── envs.py      # 基本設定を記述
...

基本処理の記述

from fabric.api import run, abort, env, cd, local
from fabric.decorators import task


class LocalHandler(object):
    """localでのデプロイ周りの処理"""

    def push(self):
        """localの変更をgithubへpushする
        commitまではされている想定
        """
        self.__check_has_commited()
        self.__check_on_master()
        self.__update_requirements()
        local('git push origin master')

    def __update_requirements(self):
        """requirements.txtをupdateするスクリプト"""
        req_txt = 'requirements.txt'
        local('pip freeze > {}'.format(req_txt))
        res = local('git ls-files -m', capture=True)
        if res == req_txt:
            local('git add {}'.format(req_txt))
            local('git commit -m "update requirements.txt"')

    def __check_has_commited(self):
        """変更がちゃんとcommitされているかを確認"""
        res = local('git ls-files -m', capture=True)
        if res:
            abort('there are some diff on git supervised files')

    def __check_on_master(self):
        """master branchにいるか確認"""
        branch = local('git rev-parse --abbrev-ref HEAD', capture=True)
        if branch != 'master':
            abort('you should call this on master branch')


class RemoteHandler(object):

    def pull(self):
        """master branchに移動してソースコードの更新"""
        with cd(env.app_path):
            self.__check_no_change()
            run('git checkout master')
            run("git pull origin master")

    def restart_daemon(self):
        """svcを使って、restartさせる"""
        self.__install_required_package()
        res = run('supervisorctl restart {}'.format(env.daemon_name))
        if res.failed:
            abort('REMOTE: fail restart daemon {}'.format(env.daemon_name))
        return res

    def __install_required_package(self):
        req_txt = 'requirements.txt'
        with cd(env.app_path):
            run('pip3 install -r {}'.format(req_txt))

    def __check_no_change(self):
        """変更がちゃんとcommitされているかを確認"""
        res = run('git ls-files -m')
        if res:
            abort('REMOTE: there are some diff on git supervised files')

@task
def deploy():
    LocalHandler().push()
    rh = RemoteHandler()
    rh.pull()
    res = rh.restart_daemon()
    if res.succeeded:
        print("SUCCESS!! WELL DONE")
    else:
        print("TRY ONcE MORE :D")

基本設定を記述

from fabric.api import env
from fabric.decorators import task

@task
def production():
    """ 本番 """
    env.environment = "production"
    env.app_path = '/path/to/project_root'
    env.daemon_name = 'supervisor daemon_name'
    env.user = 'ec2-user'
    env.hosts = ['host_name']
    env.key_filename = '~/.ssh/key.pem'

module読み込み

このままだと、プロジェクトルートからfabricのタスクを呼べない。init.pyにimport書いてモジュール化する。

from fabfile.envs import production
from fabfile.deploy import deploy

実際に自動デプロイをする

下記のコマンドをローカルで叩く。

$ fab production deploy