uWSGIで複数バージョンのPythonを動かす

今のところuWSGIはvirtualenvで作ったアプリ別のモジュールをロードすることは出来るものの、システムのlibpython.soを直接使っているため、そのままではビルド時のPythonしか動かない。

異なるバージョンのpythonを含むvirutalenvに入ろうとすると、PYTHONPATHが違うためimport siteの時点でモジュールのロードに失敗する。

Python version: 2.6.6 (r266:84292, Nov 22 2013, 12:16:22)  [GCC 4.4.7 20120313 (Red Hat 4.4.7-4)]
...
Set PythonHome to /path/to/py27env
'import site' failed; use -v for traceback

他のバージョンのpythonを使うには、対応するpythonpythonプラグイン対応のuWSGIをビルドし直す必要がある。

ビルド手順

デフォルトのままpipなどでインストールすると、python他プラグイン全部入りのuwsgiバイナリが出来ているはず。 (pipで入れていれば/tmp/pip-build-root/uwsgiに必要なファイルが展開されているので、これを元にビルド)

uwsgiソースのbuildconfディレクトリには、uwsgi本体ビルド用の設定ファイルが含まれている。 pythonを含まないバイナリを作るには、buildconf/nolang.iniを使ってビルドするか、main_plugin=からpythonとgeventを抜いておく必要がある。(geventもpythonに依存しているためビルド出来ない)

python uwsgiconfig.py --build nolang

次にプラグインをビルドするのだが、uwsgiconfig.pyはリンクするlibpython.soをビルド時のPython環境から探しているらしく、ほしいバージョンのsharedなpythonとlibpythonがセットで必要。 さらに面倒なことにpythonビルドツールのpythonzはデフォルトではsharedなビルドを作ってくれない。(OSXは別?)

一応configureオプションを渡すことでビルドは可能で、

pythonz install 2.7.6 --verbose --configure='--enable-shared LDFLAGS="-Wl,-rpath -Wl,\\$$ORIGIN/../lib"'

あるいは手動でインストール

./configure --prefix=/usr/local/python27 --enable-shared LDFLAGS="-Wl,-rpath /usr/local/python27/lib"
make
sudo make altinstall

必要なバージョンのsharedなpythonバイナリでプラグインをビルド。pluginsディレクトリにプラグインのテンプレートが入っている。

python2.7 uwsgiconfig.py --plugin plugins/python nolang python27
python2.7 uwsgiconfig.py --plugin plugins/gevent nolang gevent27

これでuwsgiバイナリと*_plugin.soが出来るのでバイナリを置き換え。

sudo cp uwsgi /usr/bin/uwsgi
sudo mkdir -p /usr/lib/uwsgi
sudo cp *.so /usr/lib/uwsgi

アプリの設定ファイルから

[uwsgi]
plugins-dir=/usr/lib/uwsgi
plugin=python27_plugin.so
virtualenv=/path/to/py27env
chdir=/path/to/py27app

とロードさせればpython2.7を利用できる。

同様にphp-embeddedの入った環境で

python uwsgiconfig.py --plugin plugins/php nolang php

phpプラグインもビルドできた。

ものすごく面倒なのでuwsgi側でvirtualenvから自動ロードするか、libpython.soをplugins-dirに持ってくるようにパッチを当てたい……

参考:http://projects.unbit.it/uwsgi/wiki/MultiPython