異なるバージョンのPythonやモジュールを一つのWEBサーバー内で共存させる

お金もないし、そんなにPV数もないので、一つのVPSで複数のサイトを構築するなんてことはよくあると思うんですが（私だけ？）、その際にpyenvやvenvでそれぞれ個別に指定したpythonのバージョンで動くのか気になります。これができれば、昔作った古いWEBアプリを気にせず、最新のpythonやモジュール類を使ってサイトを構築することができます。ググり方が悪いのか、常識過ぎて見つからないのか、一つのVPSで複数のサイトを構築するなんてケチくさいことしてる人がいないのかはわかりませんが、答えがみつからなかったので自分で試してみます。

いきなりVPS上でやってみてもいいですが、アップするのもめんどくさいし自分の開発環境（Mac）でどうなるか試してみました。

# テスト環境

Mac（Catalina）上に立ち上げたNginxをWEBサーバーとします。2つのディレクトリ（hello1, hello2）を作成して、pyenvで異なるバージョンのpythonをインストールし、venvでそれぞれ仮想環境を構築しnumpyをインストールしてバージョンをWEBページに表示するということをしてみます。Nginx, pyenv, venvについてはqiitaにいっぱい記事があるのでここでは書きません。

[system]
 python 3.8.2
 numpy  1.18.4
 uWSGI  2.0.19.1

[hello1]
 python 3.8.3
 numpy  1.19.0
 uwsgi  2.0.19.1

[hello2]
 python 3.7.7
 numpy  1.18.5
 uwsgi  2.0.19.1

# ディレクトリの作成から仮想環境の構築まで。

~~~bash:hello1
$ mkdir hello1
$ cd hello1
$ pyenv local 3.8.3
$ pyenv version
3.8.3 (set by /usr/local/var/www/hello1/.python-version)
$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install numpy==1.19.0
(venv)$ pip install uwsgi
~~~

~~~bash:hello2
$ mkdir hello2
$ cd hello2
$ pyenv local 3.7.7
$ pyenv version
3.7.7 (set by /usr/local/var/www/hello2/.python-version)
$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install numpy==1.18.5
(venv)$ pip install uwsgi
~~~

# index.pyを作成

どちらのディレクトリにもindex.pyを下記のとおり作成します。

~~~python:index.py
import sys
import numpy as np

def application(env, start_response):
    start_response('200 OK', [('content-Type', 'text/html')])
    version = sys.version + "<br>"
    version += "numpy: " + np.__version__
    return [version.encode('utf-8')]
~~~

# uwsgi.iniを作成

各ディレクトリにuwsgi.iniを作成します。

~~~uwsgi:hello1/hello1_uwsgi.ini
[uwsgi]
base = /usr/local/var/www/hello1
chdir = %(base)
virtualenv = %(base)/venv
module = index
master = true
socket = /tmp/%n.sock
chmod-socket = 666
wsgi-file = %(base)/index.py
pidfile = /tmp/%n.pid
logto = /usr/local/var/log/nginx/%n.log
~~~

~~~uwsgi:hello2/hello2_uwsgi.ini
[uwsgi]
base = /usr/local/var/www/hello2
chdir = %(base)
virtualenv = %(base)/venv
module = index
master = true
socket = /tmp/%n.sock
chmod-socket = 666
wsgi-file = %(base)/index.py
pidfile = /tmp/%n.pid
logto = /usr/local/var/log/nginx/%n.log
~~~

uwsgiをそれぞれ立ち上げます。（当然ながらuWSGI Emperorでまとめて立ち上げようとするとシステムのpythonをつかうことになるのでうまくいきません。チョットハマった。）

~~~bash:hello1
/usr/local/var/www/hello1/venv/bin/uwsgi --ini /usr/local/var/www/hello1/hello1_uwsgi.ini
~~~

~~~bash:hello2
/usr/local/var/www/hello2/venv/bin/uwsgi --ini /usr/local/var/www/hello2/hello2_uwsgi.ini
~~~

# Nginxの設定

下記のとおりNginxの設定をします。

~~~vim:/usr/local/etc/nginx/servers/hello1.conf
server {
    listen 80;
    server_name hello1.localhost;

    root /usr/local/var/www/hello1;

    client_max_body_size 20M;

	access_log /usr/local/var/log/nginx/hello1_access.log;
	error_log  /usr/local/var/log/nginx/hello1_error.log;

	location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/hello1_uwsgi.sock;
    }
}
~~~

~~~vim:/usr/local/etc/nginx/servers/hello2.conf
server {
    listen 80;
    server_name hello2.localhost;

    root /usr/local/var/www/hello2;

    client_max_body_size 20M;

	access_log /usr/local/var/log/nginx/hello2_access.log;
	error_log  /usr/local/var/log/nginx/hello2_error.log;

	location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/hello2_uwsgi.sock;
    }
}
~~~

Nginxのリロード。

~~~bash
$ nginx -s reload
~~~

# 結果

[hello1]
3.8.3 (default, Jul 11 2020, 10:08:47) [Clang 11.0.0 (clang-1100.0.33.16)]
numpy: 1.19.0

[hello2]
3.7.7 (default, Jul 11 2020, 10:11:24) [Clang 11.0.0 (clang-1100.0.33.16)]
numpy: 1.18.5

と無事別環境で立ち上げることができました。まさかこんなにうまくいくとは思わなかった。

# 参考

https://qiita.com/koyoru1214/items/57461b920dfc11f67683
https://qiita.com/sti320a/items/f20b8cbc06bf70425d33
https://qiita.com/5t111111/items/3f2065699fbc4b43c4a8
https://qiita.com/11ohina017/items/da2ae5b039257752e558
https://qiita.com/trustbank_kei/items/52a0e4233200ac03b4d1
https://qiita.com/yasunori/items/64606e63b36b396cf695
https://www.ravness.com/2019/09/flasknginx/