pip、buildout などを使うとデプロイする時に Python ライブラリの依存関係はややこしいことがあります。 普段はデプロイスクリプトで、 pip に requirements.txt を指定して、もしくは、 buildoutを実行して、 依存ライブラリを落としてインストールしますが、 PYPI がダウンしている場合、環境によって、PYPIにアクセス 出来ない場合もありますので、デプロイが止まってしまって困ります。 PYPIはダウンしている時に pip は PYPI のミラーを使うことができますが、ミラーに必要がパッケージバージョンが入っていない、 ミラーの最後のIDのDNS が ちゃんと動いていないときに、 pip は当然ちゃんと動かない場合も。 bitbucket や、 github からのリポジトリに依存している場合、 接続できなかったら、ミラーがないので、当然インストールできます。
つもり、デプロイは外部サイトに依存していて、デプロイを邪魔する問題が出てくる可能性が高いです。
ローカルで必要なライブラリは既にインストールしているので、それを使えばいいじゃん!と自然に思います。
実は、情報がなくて、あまり使われてないみたいですが、
pip はバンドルを作成する機能があります。バンドルは requirements.txt
の依存ライブラリを zip に固めて、そして、
install
コマンドで、バンドルから
ライブラリをインストールしてくれる機能です。つもり、バンドルさえあれば、PYPIにアクセスしたくても良い。
やったぜ! これを使おう
バンドルを作成するのが簡単:
pip bundle -r requirements.txt mybundle.pybundle
インストールも簡単:
pip install mybundle.pybundle
もちろん、 virtualenv と組み合わせて使えます。
注意: ファイルの拡張子は pybundle じゃないと install コマンドがバンドルを認識してくれない
バンドルは単の zip ファイル:
$ unzip mybundle.pybundle
...
$ ls
build/ pip-manifest.txt mybundle.pybundle src/
$ cat pip-manifest.txt
# This is a pip bundle file, that contains many source packages
# that can be installed as a group. You can install this like:
# pip this_file.zip
# The rest of the file contains a list of all the packages included:
# These packages were installed to satisfy the above requirements:
Django==1.3
django-debug-toolbar==0.8.4
South==0.7.3
...
デプロイ
デプロイは Fabric を使います。ローカルで、バンドルを一回作っておけば、依存ライブラリを修正しない限り、 そのまま使えます。
まずは、 バンドルを作成するコマンドを作る。 runs_once()
デコレータで一回しか実行しないようにします。 local()
メソッドでローカルコマンドを叩きます。
from fabric.decorators import runs_once
from fabric.api import local
@runs_once
def create_bundle():
u""" 依存ライブラリのバンドルを作り直す """
local('pip bundle mybundle.pybundle -r requirements.txt')
print 'Created bundle mybundle.pybundle'
次は、コード自体をアップするコマンドを作ります。下記は、 mercurial を ssh で push しています。
from fabric.api import sudo, cd, local
def _hg_pull():
local("hg push -r %(revision)s ssh://%(host_string)s/%(base_path)s" % env
def _hg_update():
with cd(env.base_path):
sudo("hg update -C -r %(revision)s" % env, user=env.deploy_user)
def push():
u""" 最新バージョンに更新 """
_hg_pull(rev)
_hg_update(rev)
次は依存関係の更新
import os
from fabric.api import sudo, cd, put, local
def update_deps():
if not os.path.exists('mybundle.pybundle'):
create_bundle()
put('mybundle.pybundle', '%(base_path)s/mybundle.pybundle' % env, use_sudo=True)
with cd(env.base_path):
sudo('chown %(deploy_user)s:%(deploy_user)s mybundle.pybundle' % env)
sudo('pip install -E %(venv_path)s mybundle.pybundle' % env)
mercurial の代わりに rsync を使う場合はこんな感じで、一発でできる。
from fabric.contrib.project import rsync_project
RSYNC_EXCLUDE=[".hg"]
def push():
if not os.path.exists('mybundle.pybundle'):
create_bundle()
rsync_project(
env.base_path,
exclude=RSYNC_EXCLUDE,
delete=True,
)
with cd(env.base_path):
sudo('chown %(deploy_user)s:%(deploy_user)s mybundle.pybundle' % env)
sudo('pip install -E %(venv_path)s mybundle.pybundle' % env)
最後は、deploy コマンドを作る
def deploy():
push()
update_deps()
# DB を更新
migrate_db()
# ウェブサーバーを再起動
reboot_server()
こういう感じで、ローカルとサーバーの接続さえできれば、デプロイできます。 外部サイトに依存したいのが楽過ぎて、逆にいい意味で困ります。