fabric を使って、capistranoのdeploy.rb風のデプロイをする

今やってるプロジェクト(Web アプリケーションフレームワークは、CakePHP2)で、デプロイの方法をどうしようかと色々悩んだ結果、fabricというのを使ってみた。

Welcome to Fabric! ? Fabric documentation

元々rails用のデプロイツールであるcapistranoを使ってた(その時もWeb アプリはperlだったりphpだったりしたけど、デプロイのためにrubyをインストールしてた)ので、capistranoのようなデプロイを、fabricでもやりたい。

デプロイで満たしたいこと

  • 動作環境のセットアップができる
  • 複数の環境にデプロイできる
  • マイグレーションと統合されている
  • ソースコードの取得からWebサーバの再起動までがワンコマンドで完了する

という感じなのだけど、fabricでは特に、deploy.rbに相当するようなものは提供されていない様子。 capistranoのdeploy.rbが何をやっているかを知らないといけないけど、結構考えられていて複雑だったりするので、 だいぶ日和って以下のことだけするfabfile.pyを用意した。

  • セットアップ: サーバ上の動作ディレクトリを作成する。
  • ソース準備: リポジトリからソースを取得してtarで固めて、サーバに送る。
  • リリース作成: releases ディレクトリに配置してシンボリックリンクなどを貼る。
  • マイグレーション: 適応してないマイグレーションがあれば適応する。
  • リリース: currentのリンク先を作成したreleasesに変更する。
  • サーバ再起動: apache再起動

簡単にするため、毎回上記の処理を行なうようにした。 TODO: リビジョンを指定してデプロイができるようにする。

用意したfabfile.pyです。

fabfile.py

# -*- coding: utf-8 -*-
import time
from fabric.api import run, local, cd, lcd, env, sudo, put
 
app_dir = '/var/vhosts/hoge'
shares = ['webapp/app/tmp']
version = time.time()
version_dir = '%s/releases/%s' % (app_dir, version)
cake_env = ''
 
# ENV #
env.use_ssh_config = True
 
def staging():
  global cake_env
  cake_env = 'staging'
  env.hosts = ['hoge-staging-web']
 
def production():
  global cake_env
  cake_env = 'prod'
  env.hosts = ['hoge-prod-web']
 
# tasks #
 
def directory_setup():
  """
  自動デプロイのためのディレクトリ構成を作成する
  """
  sudo('mkdir -p %s' % app_dir, shell=False)
  sudo('chown %s:%s %s' % (env.user, env.user, app_dir), shell=False)
  run('mkdir -p %s/{releases,shared}' % app_dir)
  with cd('%s/shared' % app_dir):
    for d in shares:
      run('mkdir -p %s' % d)
 
def prepare_files():
  """
  ローカルでsvnからソースを取得し、tarで固めてから、scpでサーバに転送する。
  """
  tmp_dir = '/tmp/%s' % version
  package_file = '/tmp/%s.tar.gz' % version
  local('mkdir %s' % tmp_dir)
  with lcd(tmp_dir):
    local('svn export https://svn.hoge.jp/hoge/trunk/src/webapp > /dev/null')
  with lcd('/tmp'):
    local('tar zcf %s %s' % (package_file, version))
    local('rm -rf %s' % version)
  put(package_file, '/tmp')
  return package_file
 
def create_release(package_file):
  """
  ソースをサーバ上のリリースファイル用ディレクトリに格納する。
  """
  with cd('/var/vhosts/hoge/releases'):
    run('tar zxf %s' % package_file)
    run('rm %s' % package_file)
 
def symlink_shared():
  """
  各バージョンで共有すべきディレクトリをシンボリックリンクで復元する。
  """
  for d in shares:
    run('rm -rf %s/%s' % (version_dir, d))
    run('ln -s %s/shared/%s %s/%s' % (app_dir, d, version_dir, d))
    sudo('chown -R apache:apache %s/shared/%s' % (app_dir, d), shell=False)
    sudo('chmod -R 777 %s/shared/%s' % (app_dir, d), shell=False)
 
def migrate():
  """
  DBのマイグレーションを実行する。
  """
  with cd(version_dir):
    run('CAKE_ENV=%s webapp/app/Console/cake Migrations.migration run all -p' % cake_env)
 
def symlink_release():
  """
  currentのシンボリックリンクを貼り替えて、新バージョンを公開する。
  """
  with cd(app_dir):
    run('rm -f current && ln -s %s current' % version_dir)
 
def restart():
  """
  apacheの再起動
  """
  sudo('/etc/init.d/httpd restart', shell=False)
 
def deploy():
  directory_setup()
  package_file = prepare_files()
  create_release(package_file)
  symlink_shared()
  migrate()
  symlink_release()
  restart()
 
# vim:set ts=8 sts=2 sw=2 tw=0:

まとめ

fabric が、pythonのコードで記述できるから、アクロバチックなこともやろうと思えばできるという安心感もありつつ、元がシンプルだから更に良いです。 fabricを気に入ると同時に、capistranoのdeploy.rbは優れたものだなと再認識した。deploy.rbは、rails文化が生んだデプロイの概念が形になったものなんだろうな。(rails触ったことないけど)

One Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.