アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

お手軽にdeployしたかったのでFabricを試してみました

ohataです。

最近子供がバスケットボールを始めて、運動不足解消がてら一緒にバスケしたら全然ついていけず。。。
これからはダンコたる決意で一生懸命バスケの練習をしようと思います!

さて本題ですが、今回はプライベートでちょっとイジったdeployツール fabricについてです。

Fabricって?

Pythonで記述できるデプロイツールです.
Fabric

Rubyで言えばCapistranoと同じような役割なのですが、
今回は以下の条件で、Fabricを使ってみようと思いました。

  • そこまで大したことはしない
  • 構成自体をAnsibleを使っている (Pythonを使っている)
  • アプリケーション自体がjs (Rubyを使ってない)

fabric と言えば 真っ先に思いつくのが こちらだと思います。
アプリ運用するには結構有益なツールですよね。
こちらで言うとfastlaneの役割に近いです。
話逸れましたね、今回はこっちではないです。

準備

Pythonのインストール

Pythonが必要なので、インストールします。
version管理を楽にしたいので pyenv で管理します。

$ brew install pyenv

現在のインストール可能リストから3.7.0を入れます

$ pyenv install --list
$ pyenv install 3.7.0

xcodeが入ってないとエラーになる場合があるので、 その場合は xcode-select --install してください

※ Mojaveの場合に zipimport.ZipImportError: can't decompress data; zlib not available' が出てしまいました。
解消時にこちらを参考にさせていただきました。(感謝です) https://qiita.com/zreactor/items/c3fd04417e0d61af0afe

xcodeの対応状況の問題みたいなので versionアップで解消されるとは思います 現時点(2018/10/26)で最新なのでglobalに設定しました。

$ pyenv global 3.7.0
$ python --version

system内のpythonから切り替わらない事もあるので ~/.bash_profileに以下を追記します

eval "$(pyenv init -)"
$ python --version
Python 3.7.0

これでPythonはOKです

fabricのインストール

前まではPython3で動作するFabricがなくforkされたFabric3というプロジェクトを利用する必要があったのですが、現在は対応しているようなので、本家のプロジェクトを利用します
(ドキドキ)

$ pip install fabric

無事インストールできるのですが、以前はfabric.apiが実行時に動かないという状態があったのでここではまだ安心できません。 自分の環境だけだかもしれませんが、実行してみるとやはりエラーが出てしまいました。 f:id:k-ohata:20181028200536p:plain

今回は、fabric3を利用します

$ pip install fabric3

アプリのビルド

今回は Vue.jsのアプリをビルド&デプロイする場合の設定をしてみました
以下のようなフローで動くように設定していきます。

  1. local環境にdeploy用のディレクトリを作成
  2. githubからclone or pull してくる
  3. vueのbuildを行う
  4. buildファイルをサーバーにあげる

ファイル構成

fabfile
  |- __init__.py
  |- enviroments.py
  |- deploy.py

中身はPythonなので、もっと凝った作りにすることも簡単にできます。

init.py

これは通常のPythonと同じ、利用するモジュールを宣言する箇所です

from fabfile.enviroments import prod, dev
from fabfile.deploy import build, deploy

enviroments.py

環境ごとの設定をここに定義します

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

repo_name = 'hoge' #githubのリポジトリ名
github_url = "git@github.com:hoge/hogehoge.git" #githubのリポジトリ

@task
def prod():
    env.repo_name = repo_name
    env.environment = "prod"
    env.github = github_url
    env.branch = 'master'
    env.deploy_user = 'deploy_user'
    env.deploy_hosts = ['xxx.xxx.xxx.xxx']
    env.work_dir = '/tmp/hoge/prod/'
    env.app_name = 'hoge_vue'

@task
def dev():
    env.repo_name = repo_name
    env.environment = "dev"
    env.github = github_url
    env.branch = 'develop'
    env.deploy_user = 'deploy_user'
    env.deploy_hosts = ['xxx.xxx.xxx.xxx']
    env.work_dir = '/tmp/hoge/dev/'
    env.app_name = 'hoge_vue'    

deploy.py

実行する処理を記載します

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

class Build(object):

    def web(self):
        app_name = env.app_name
        self.__work_dir()
        with lcd(env.work_dir):
            self.__pull_github()
            with lcd(env.repo_name):
                self.__build_apps()

    def __work_dir(self):
        local('mkdir -p {}'.format(env.work_dir))

    def __pull_github(self):
        with settings(warn_only=True):
            if local('ls -la {}'.format(env.repo_name)).failed:
                local('git clone {}'.format(env.github))
            with lcd(env.repo_name):
                local('git checkout {}'.format(env.branch))
                local('git pull')

    def __build_apps(self):
        with lcd(env.repo_name):
            local('npm install')
            local('npm run build')

class Deploy(object):

    def web(self):
        app_name = env.app_name
        put("{0}/{1}/{2}/dist/*".format(env.work_dir, env.repo_name, app_name), "/var/www/{}/".format(app_name))

    def restart_server(self):
        with cd('/var/www/{}/'.format(env.app_name)):
            run('forever start')

@task #@task ここに記載したものがコマンド実行時のタスク
def build():
    local = Build()
    local.web()
    print("build success!!")

@task
def deploy():
    local = Build()
    local.web()
    remote = Deploy()
    remote.web()
    remote.restart_server()
    print("deploy success!!")

実行

build実行

fab dev build

deploy実行

fab dev deploy

使ってみた感想

ほとんどシェル的な感覚で作れるので学習コストはかなり低いと思います。 Ansibleみたいな冪等性がないので、ちょっと面倒な箇所もありましたが、 結局Pythonなので、抵抗なくすんなり受け入れられました。

軽くサクッとやりたいみたいなときにいいかもしれません

最後に

アクトインディには [バスケ部(非公式)] もあるので、
[先生、コードが書きたいです。。。]
のエンジニアの方、お待ちしています!