5
3

More than 5 years have passed since last update.

Play2アプリケーションのビルド・デプロイをFabricで自動化する

Last updated at Posted at 2015-02-12

まだ手動でリリースしていた頃、”リリース職人”みたいな扱いになって作業押し付けられてばかりでカッとなって自動化した時の遺物を載せます。
実際使っているものを所々端折って書いていますが、だいたいこんな感じ。
パスは適宜読み替えてください。

前置き

準備するもの

  • Fabric ビルド・デプロイの自動化に使う。
  • daemontools プロセス管理に使う。ここではdaemontoolsの設定については特に触れません。

ディレクトリ構成

/[root]
   ├ /service
   |  └ run
   └ /product
      ├ current
      └ /versions
         └ [version_number]
            ├ /bin
            ├ /conf
            └ /lib
  • /service/run
    daemontoolsのサービス起動スクリプト

  • /product/current
    任意の /product/versions/[version_number] へのシンボリックリンク

  • /procduct/versions/[version_number]
    Play2アプリケーションが展開される場所
    play stageコマンドで生成されるものが展開されると思ってもらえればOK

サービス起動スクリプト

daemontoolsのサービス起動スクリプトはこんな感じ。

exec [PATH_TO_ROOT]/product/current/bin/[MY_APP_NAME] -Dconfig.file=[PATH_TO_ROOT]/product/current/conf/application.conf

ビルドの自動化

以前はJenkinsのシェルで実行していたものを、gitで管理できるようFabricのスクリプトにしています。
実際のスクリプトはこんな感じ。(これも所々端折ってます)

from fabric.api import local, lcd, settings
from datetime import datetime


def build():
    play_stage()
    remove_non_deploy_files()
    make_tar_file()


# play stageコマンドを実行。Jenkins環境ならsbtを、開発環境ならローカルのplayコマンドを直で使う。
def play_stage():
    build_command = ("java -Dsbt.log.noformat=true -XX:MaxPermSize=256M "
                     "-jar [PATH_TO_SBT]/sbt-launch.jar "
                     "clean compile scalastyle stage") if is_jenkins() is True \
        else "play stage"
    local(build_command)


# 実行環境の判定
def is_jenkins():
    return '[JENKINS_HOSTNAME]' in local('hostname')


# READMEとshareは使わないので削除
def remove_non_deploy_files():
    with settings(warn_only=True):
        local('rm -r ./target/universal/stage/README*')
        local('rm -r ./target/universal/stage/share')


# stageで生成されたファイル郡を.tar.gz化。Jenkins環境なら、ビルド番号名に、それ以外なら日付をファイル名にする。
def make_tar_file():
    with lcd("./target/universal"):
        with settings(warn_only=True):
            local('pwd')
            build_number = get_build_number()
            local('rm -r ./%s' % build_number)
            local('mv ./stage ./%s' % build_number)
            local('tar cvzf %s.tar.gz ./%s' % (build_number, build_number))


# Jenkinsのビルド番号を取得。無ければ日付文字列を返す。
def get_build_number():
    with settings(warn_only=True):
        build_number = local('printenv BUILD_NUMBER', capture=True)
        if build_number.succeeded is True:
            return build_number
        else:
            return datetime.now().strftime('%Y%m%d')

これを fab_build.pyとして、以下のコマンドで実行できます。

fab -f fab_build.py build

デプロイの自動化

ビルドと同じようにFabricスクリプト化してます。

from fabric.api import run, put, env, cd, sudo


def release(file_path):
    env.hosts = ["xxx.xxx.xxx.xxx", "yyy.yyy.yyy.yyy"]
    env.user = '[USER]'
    env.key_filename = '[PATH_TO_PRIVATE_KEY]'
    deploy(file_path)


# デプロイの実行
def deploy(file_path):
    env.use_ssh_config = True
    build_number = file_name.split('.')[0]
    upload(file_path)
    expand(file_name)
    update_symbolic_link(target, build_number)
    restart_service(get_service(target))


# .tar.gzファイルをアップロード
def upload(file_path):
    put(file_path, "./product/versions/")


# .tar.gzファイルを展開
def expand(file_name):
    with cd("~/product/versions"):
        run("tar zxvf %s" % file_name)


# シンボリックリンクを更新
def update_symbolic_link(number):
    with cd("~/product"):
        run("ln -sfn [PATH_TO_ROOT]/product/versions/%s current" % (number))


# daemontoolsでプロセスを再起動
def restart_service():
    stop_service()
    start_service()


# プロセス停止
def stop_service():
    manage_service("down")


# プロセス起動
def start_service():
    manage_service("up")


# daemontoolsの実行
def manage_service(command):
    svc_opt = "-u" if command == "up" else "-d"
    sudo("[PATH_TO_DAEMONTOOLS]/command/svc %s [PATH_TO_DAEMONTOOLS]/service/[MY_APP_NAME]" % (svc_opt), pty=True, shell=False)

これも fab_deploy.pyとして、以下のコマンドで実行できます。

fab -f fab_deploy.py release:"./target/universal/[BUILD_NUMBER].tar.gz"

Jenkinsの設定

ビルド・デプロイ用のジョブを作ったら、シェルでFabricのスクリプトを実行するようにします。
基本的には上のコマンドを並べただけですが、ビルド番号をJenkinsの環境変数から取得するようになっています。

fab -f fab_build.py build
fab -f fab_deploy.py release:"../target/universal/${BUILD_NUMBER}.tar.gz"

最後に

実際はもっと細かい制御も入れているのですが、以下略。

さらに、Slack + HubotでJenkinsのジョブを叩くコマンドを定義して、Slack経由でビルド・デプロイするようにしています。これで、Slackが使えれば、どこでも誰でもリリースができる!

もう「誰にも”リリース職人”なんて呼ばせないッ!!」

5
3
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
5
3