exxoで1ファイルバイナリを作ろう

exxo というツールを使ってPythonアプリケーションを 1ファイルの実行バイナリにパックして簡単に配布できるようにしてみよう!

ターゲット環境

現在のアウトプットが動作する環境は以下のみです。

  • Linux amd64

ビルドに必要な環境と依存パッケージ

以下の通りですが、 dockerを利用することでさらに広範囲の環境でビルドすることもできます。

  • Linux amd64
  • Python3.4系またはPython2.7系のどちらか
  • UPX(オプショナル)

インストール

exxo 自身が1ファイルバイナリとしてビルド済みのものがダウンロードできます。

curl -L https://bintray.com/artifact/download/mbachry/exxo/exxo-0.0.5.tar.xz | tar xJvf - -C /usr/local/bin

最後のパスは$HOME/binなどPATH環境変数に列挙されたパスならどこでも構いません。

exxo の使い方

virtualenv環境準備

for Python3.4

exxo venv /tmp/myenv
source /tmp/myenv/bin/activate

for Python2.7

exxo venv -p 2.7 /tmp/myenv
source /tmp/myenv/bin/activate

注釈

この時、exxoはvirtualenv環境上にあるパッチを当てています。 なので通常のvirtualenvで作った環境は使えません。

コードのフォルダ構成

以下のようにファイルツリーを構築します。

- setup.py
- pkg/
    - __init__.py (空ファイル)
    - sample.py

setup.py

import os
from setuptools import setup, find_packages

VERSION='0.1.0'
ROOT = os.path.realpath(os.path.dirname(__file__))

install_requires = [
    'pytz>=2015.7',
]

setup(
    name='sample',
    version=VERSION,
    author='accense',
    author_email='sample@sample.org',
    url='http://sample.org',
    description='sample application.',
    long_description='''sample''',
    packages=find_packages(),
    zip_safe=False,
    install_requires=install_requires,
    extras_require={},
    include_package_data=True,
    entry_points={
        'console_scripts': [
            'main = pkg.sample:main',
        ],
    },
)

sample.py

def main():
    print('hello world!')

ビルド

setup.pyのあるフォルダにて

exxo build

以上でsetup.pyにてビルド後、virtualenv環境に投入されたパッケージや バンドル指定をしたパッケージやその他のファイル群をzip圧縮したものを 起動用バイナリのリソースにくっつけた1ファイル実行ファイルを出力します。

exxo のパッチの仕組み

パッケージパスを参照するファイルアクセスをzipリソースに振り向ける仕組み。 zipに含まれるパスを参照するとzip内容を読み出す。 それ以外の読み書きは実際のファイルにアクセスする。

トラブルシューティング

以外とハマりやすいのが必要なファイルがバンドルされていないパターン。

確認方法は

(myenv)> python setup.py sdist

と実行するとtarアーカイブが出力されます。そのtarに必要なファイルが揃っているか よく確認しましょう。ここに入っていないコードやデータは 1ファイルバイナリには含まれていませんので ファイルが見つからないエラーが出ます。

また、Djangoを使う場合2つ注意点があります。

  • templates配下がパッケージングされるようにする(MANIFEST.inを書くのが簡単)
  • 「manage.py runserver」以外のサーバーを使う場合はstaticファイルサーブをお忘れなく (「manage.py collectstatic」してそのフォルダをMANIFEST.inに加えるのがオススメ。)

Djangoサンプル

https://github.com/nobonobo/1binary-sample

dockerベースでビルドするようにしてあるので、 nativeのdockerまたはdocker−machineのある環境でもビルドできます。 (実行はdocker内でしかできないかも。)

まとめ

golang+go-bindataみたいに1ファイルでアプリケーションが動きますー。

成果物は1ファイルだけで対象にインストール可能なのでデプロイ作業が簡単です。 ただ、デバッグはちょとやりにくくなるかもねー。