[Python]CnetOS7 + Apache + mod_wsgi + hug で簡単APIサーバ作成
簡単なAPIサーバを作りたいなーと思い、やってみました。
以前はPHPを少ししか書けなかったけど、もう少し色々な言語を使いたいと思いPythonで実装してみることにしました。
色々と調べてみるとPythonにもフレームワークは色々とあるようで、DjangoとかFlaskとかありましたが、簡単に実装できそうなhugを使うことにしました。
PHPだとWebサーバ上で動かすのは簡単だったのですが、Pythonだと割と大変だったので備忘録として残しておきます。
wsgiサーバはpythonだとgunicornとかuwsgiとかがポピュラーみたいでしたが、何やら設定が難しそうな印象を受けたので、なんとなく簡単そうなmod_wsgiを使うことにしました。
最終的にやりたかったBASIC認証までを追加することは出来ませんでしたが。。。
知っている方がいたら教えてほしいです。。
※20170914 追記 gunicorn使った方が簡単でした。
Vagrantを使ってCentOSのVMを作成
Vagrantfileはこんな感じです。
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.network "private_network", ip: "192.168.33.10" "192.168.0.100" , :bridge => "en0: Wi-Fi (AirPort)" config.vm.synced_folder "./", "/vagrant", type: "virtualbox", :owner => "vagrant", :groupe => "vagrant", :mount_options => ['dmode=755', 'fmode=655'] config.vm.provision "shell", run: "always", inline: "systemctl restart network.service" end
「Vagrant+Laravelでパーミッションで詰まった」とか、「vagrant + centos7 でprivate_networkで設定したIPに接続ができない」とかで躓いた経緯があったから、
config.vm.synced_folder "./", "/vagrant", type: "virtualbox", :owner => "vagrant", :groupe => "vagrant", :mount_options => ['dmode=755', 'fmode=655'] config.vm.provision "shell", run: "always", inline: "systemctl restart network.service"
とかを記載しています。
Python3系とApacheのインストール
hugはPython3系でしか動かないので、まずはPython3系をインストールします。
Apacheはyumでインストールすればいいのですが、Pythonに関してはソースからコンパイルしたりyumで入れたりとか色々やり方はあったのですが、最終的に「Python 3 を CentOS 7 に yum でインストールする手順」のやり方を参考にしたら上手くいきました。
[Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)
このあたりを参考にソースからコンパイルする方法も試してみましたが、何故かpipでmod_wsgiをインストールするところでつまずいてしまいました。Cent0S6系と7系の違い?
とりあえず下記コマンドでインストールします。
# 色々インストール $ yum groupinstall -y "Development tools" $ yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel # Apacheのインストール $ yum install -y httpd httpd-devel # OS起動時にApacheの起動を有効化 $ systemctl enable httpd.service # Apacheの起動 $ systemctl start httpd.service # Pyton3.6.xのインストール $ yum install -y https://centos7.iuscommunity.org/ius-release.rpm $ yum install -y python36u python36u-libs python36u-devel python36u-pip $ python3.6 -V Python 3.6.2
無事Python3.6とApacheがインストール出来ました。
mod_wsgiのインストール
こちらもソースからインストールしたりpip経由でのインストールがあるみたいですが、あえてソースからインストールするメリットも分からなかったので、pip経由でインストールすることにしました。
先程の[Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)の後半辺りを参考にしています。
$ pip3.6 install mod_wsgi $ python3.6 -c "import sys; print(sys.path)" ['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/lib64/python3.6/site-packages', '/usr/lib/python3.6/site-packages']
インストールが完了し、パケージのインストール先が/usr/lib64/python3.6/site-packages
だと分かります。
$ cd /usr/lib64/python3.6/site-packages $ ll total 8 drwxr-xr-x. 6 root root 4096 Sep 5 13:19 lxml drwxr-xr-x. 2 root root 131 Sep 5 13:19 lxml-3.8.0.dist-info drwxr-xr-x. 6 root root 84 Sep 5 09:15 mod_wsgi drwxr-xr-x. 2 root root 141 Sep 5 09:15 mod_wsgi-4.5.18-py3.6.egg-info drwxr-xr-x. 2 root root 6 Jul 19 04:01 __pycache__ -rw-r--r--. 1 root root 119 Jul 8 03:33 README.txt $ cd mod_wsgi/server $ ll total 1048 -rw-r--r--. 1 root root 1558 Sep 5 09:15 apxs_config.py -rw-r--r--. 1 root root 3563 Mar 31 2016 environ.py -rw-r--r--. 1 root root 128159 Aug 29 09:28 __init__.py drwxr-xr-x. 4 root root 60 Sep 5 09:15 management -rwxr-xr-x. 1 root root 932872 Sep 5 09:15 mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so drwxr-xr-x. 2 root root 101 Sep 5 09:15 __pycache__
ここにある、mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
を使います。
パスは/usr/lib64/python3.6/site-packages/mod_wsgi/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.soserver
です。
Apacheの設定
これで必要なものはインストール出来たので、Apacheの設定をしていきます。
/etc/httpd/conf.d/
配下にwsgi.conf
設定ファイルを配置し、wsgiの設定を記述します。
LoadModule wsgi_module /usr/lib64/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so WSGIDaemonProcess myapp user=apache group=apache WSGIProcessGroup myapp WSGISocketPrefix /var/run/wsgi WSGIScriptAlias / /var/www/app/app.py <Directory /var/www/app/> Options ExecCGI MultiViews Indexes MultiViewsMatch Handlers AddHandler wsgi-script .py AddHandler wsgi-script .wsgi DirectoryIndex index.html index.py app.py Order allow,deny Allow from all </Directory>
これを記述したらApacheを再起動しておきます。
$ apachectl restart
これで、ルートディレクトリ/
を開いたときに、/var/www/app/app.py
を動作させるという設定になりました。
hugでapp.pyの作成
とりあえずこんな感じで書きました。
import sys,json,os import hug import util @hug.get("/hoge") def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list})
今回はGETで受け取ったパラメータを他の関数に渡して処理してもらい、その結果を返すAPIを想定しているので、この様な書き方にしています。
実際に処理する関数はutil.pyに記載しています。
以下コマンドで、開発用サーバを起動します。
$ hug -f app.py /#######################################################################\ `.----``..-------..``.----. :/:::::--:---------:--::::://. .+::::----##/-/oo+:-##----::::// `//::-------/oosoo-------::://. ## ## ## ## ##### .-:------./++o/o-.------::-` ``` ## ## ## ## ## `----.-./+o+:..----. `.:///. ######## ## ## ## ``` `----.-::::::------ `.-:::://. ## ## ## ## ## #### ://::--.``` -:``...-----...` `:--::::::-.` ## ## ## ## ## ## :/:::::::::-:- ````` .:::::-.` ## ## #### ###### ``.--:::::::. .:::.` ``..::. .:: EMBRACE THE APIs OF THE FUTURE ::- .:- -::` ::- VERSION 2.3.1 `::- -::` -::-` -::- \########################################################################/ Copyright (C) 2016 Timothy Edmund Crosley Under the MIT License Serving on port 8000...
開発用のサーバがポート8000番で立ち上がるので、
$ curl http://localhost:8000/hoge?text=hoge
で結果が帰ってくればOKです。
また、http://192.168.33.10:8000/hoge?text=hoge
にアクセスでも見れます。
開発用サーバではなく、mod_wsgi
経由でApacheでも動作しているか確認したいので、mod_wsgiでも動くようにします。
mod_wsgiで動作させるには、mod_wsgiに対応したapplicationを作る必要があります。
mod_wsgiでhugを使い、APIを作るやPython hug with apache mod_wsgiを参照。
app.py
の最下部に以下の一文を追記します。
application = __hug_wsgi__
ブラウザで、http://192.168.33.10/hoge?text=hoge
にアクセスして同じ結果が帰ってくればOK。
・・・なのですが、何やら「Internal Server Error」のエラーが。
Apacheのエラーログを見てみると、
[Sat Sep 09 13:28:00.511142 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] mod_wsgi (pid=7361): Target WSGI script '/var/www/app/app.py' cannot be loaded as Python module. [Sat Sep 09 13:28:00.511233 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] mod_wsgi (pid=7361): Exception occurred processing WSGI script '/var/www/app/app.py'. [Sat Sep 09 13:28:00.511368 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] Traceback (most recent call last): [Sat Sep 09 13:28:00.511434 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] File "/var/www/app/app.py", line 4, in <module> [Sat Sep 09 13:28:00.511441 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] import util [Sat Sep 09 13:28:00.511469 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] ModuleNotFoundError: No module named 'util'
なにやらutil.pyが読み込めていない模様。
Pythonで「ImportError: No module named …」が出た時の3つの対処法を参考に、sys.path.append('/path/to/dir')
をapp.py
に追記すると直りました。
mod_wsgiでは若干挙動が違う?
Basic認証を追加
The guiding thought behind the architectureを参照すると、getの引数にhug.authentication.verify("userid","password")
を与えるとBasic認証が出来るようになるみたいです。
そのままユーザーID、パスワードを記述するのも良くないので、.env
ファイルを作成してそこに環境変数を書くとします。
【GitHub】に載せたくない環境変数の書き方 Python
こちらにdotenv
の使い方が載っています。
$ pip3.6 install python-dotenv
.env
の中身は↓にしました。
HUG_USER_NAME=uname HUG_PASSWORD=pass
この中の環境変数は、以下のように取り出します。
import os from os.path import join, dirname from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD")
これらの変数を用いて以下のようにBasic認証の設定を記述します。
import sys,json,os sys.path.append('/var/www/app') import hug import util from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD") authentication = hug.authentication.basic(hug.authentication.verify(HUG_USER_NAME, HUG_PASSWORD)) @hug.get("/hoge",,requires=authentication) def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list}) application = __hug_wsgi__
開発用サーバを立ち上げて、ブラウザからhttp://192.168.33.10:8000/hoge?text=hoge
に認証画面が出て認証できればOKです。
Apacheからの接続はどうかというと、http://192.168.33.10/hoge?text=hoge
に接続すると、無事認証画面が出ました!!!
が、、IDとパスワードを入れても認証出来ません。。。。
いくら調べても原因が分からない。。なんでなんだ~。。。。
今回はここで断念しました。。。
まとめ
mod_wsgiとhugを使って、簡単なAPIサーバを立ち上げることが出来ました。
ただ、最終的にやりたかったBasic認証はなぜかmod_wsgiだと正常に動作しませんでした。
hugに組み込まれている開発用のサーバとmod_wsgiとでは若干挙動が違うようです。
解決策を知っている方が居ましたら教えて下さい。
参考ページ
- hug:Embrace the APIs of the future
- Vagrant+Laravelでパーミッションで詰まった
- vagrant + centos7 でprivate_networkで設定したIPに接続ができない
- Python 3 を CentOS 7 に yum でインストールする手順
- [Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)
- mod_wsgiでhugを使い、APIを作る
- Python hug with apache mod_wsgi
- Pythonで「ImportError: No module named …」が出た時の3つの対処法
- The guiding thought behind the architecture
- 【GitHub】に載せたくない環境変数の書き方 Python