読者です 読者をやめる 読者になる 読者になる

VPS上でpythonで書かれたwebアプリケーションを運用する

初心者向け

サーバーに関する知識が乏しいので,記述が間違っている可能性があります.

対象とする方
  • サーバーについてあまり詳しくない方(apacheとnginxは聞いたことあるレベル)
  • pythonでwebアプリケーション作ってみたい方(今回はWAFにtornadoを利用します)
    • ruby製のアプリケーション(sinatraとか..)とかもほとんど同じ方法で動作すると思います
今回行う作業
  • nginxをリバースプロキシとして導入する
  • python環境を整える
  • tornadoでサンプルアプリを書く
  • supervisorでアプリケーション・サーバーを管理する
環境
  • さくらVPS上でCentOS release 6.4 (Final) , CentOS 5系でも動作を確認しました

1.nginxを導入する

一般的なサーバーは, ポートの80番でapacheが待っててhttpリクエストが来るとapacheが受け取って何らかの処理をしてレスポンスを返すって感じだと思うのですが, リバースプロキシを導入すると ポート80番でリバースプロキシの役割をするアプリケーションが待ってて,httpリクエストが来ると,そいつがリクエストの内容に応じてapacheや他のアプリケーション・サーバーに処理を投げることになります.

f:id:kazy1991:20131001211725p:plain

nginxのインストール

nginxの設定

もしapacheが80番で動いているなら止めましょう

sudo apachectl stop #apachectl使えるならこれ
sudo /etc/rc.d/init.d/httpd stop # ダメならこれで

nginxがインストールできたら

sudo vim /etc/nginx/conf.d/default.conf

を開き

server{
  listen 80;
  server_name your.domain.com; #運用したいドメイン名 or localhostにしとけばIPからのアクセスにも対応できる
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
  proxy_set_header X-Forwarded-Host $host;
  proxy_set_header X-Forwarded-Server $host;
  proxy_set_header X-Real-IP $remote_addr;
  location / {
    proxy_pass http://127.0.0.1:8000;  # tornado(アプリケーション・サーバー)が8000でlistenしているとする
    break;
  }
}

を追加する. これでアプリケーションサーバーまでリクエストが伝播するようになりました.

ここでnginxをリスタートします.

sudo /etc/init.d/nginx   #起動してるならnginx restartで再起動します

もしすでにapache上でサービスが運営されているような場合は,上記のserverの設定をもう一つ追加して proxy_passを

location / {
    proxy_pass http://127.0.0.1:8080;  #apacheが8080でlistenしているとする
    break;
  }

みたいに適当にポートを変えて

sudo vim /etc/httpd/conf/httpd.conf

apacheの設定を開き136行目辺りの listen 80を listen8080に変更して,

sudo apachectl start #apachectl使えるならこれ
sudo /etc/rc.d/init.d/httpd  # ダメならこれで

apacheを再起動すると普段通りつかえるようになります.
しかしひとつ問題があってこのままだとIPを127.0.0.1だと誤認識してしまうので,mod_rpafというのを利用してapacheの機能を拡張する必要があります.
導入に関してはこちらを参考にしてください Nginxでリバースプロキシを設定する

2. pyhton環境を整える

pythonはyumとかで入れてもいいんですが,異なるバージョンを頻繁に使い分けたいときに面倒です.なので*env系のpyhton用バージョン管理ツールのpyenvを使うのが便利です.

pyenvの導入

ホームディレクトリで

git clone git://github.com/yyuu/pyenv.git .pyenv
echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
exec $SHELL -l

でpyenvコマンドが使えるか試してみてください zsh使ってる人は .bash_profileが.zprofileですね

もし複数人で使えるようにした場合は この記事がおすすめです (rbenvですが大体一緒だと思います) 全ユーザーで共通のrbenv+ruby-buildを使用する環境を構築する

例えばpython2.7.4をインストールして使いたい場合は

pyenv install 2.7.4
pyenv global 2.7.4
pyenv rehash

で使えるようになります.

さらに同じ作者の方が pyenv-virtualenvというのを作っていて,これがかなり快適なので導入しましょう.

git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
exec $SHELL -l

これを導入すると例えば今回のサンプルアプリ用の仮想環境作るときは

pyenv virtualenv 2.7.5 sample_app

でsample_appという仮想環境が出来上がるので

pyenv global sample_app
pyenv rehash

で仮想環境が利用できます.ディレクトリに依存しないので普通にvirtualenvコマンドを使うより便利です.

3. tornadoでサンプルアプリを書く

tornadoの公式サイトからhelloworldをコピへしましょう.

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

これを適当なディレクトリにserver.pyみたいな感じで保存して

python server.py

で起動しましょう. 依存モジュールとかはとりあえず手動でpipで入れましょう.

この段階で自分のドメインにアクセスしてhelloworldが表示されていたらとりあえず完成です.

4. supervisorでアプリケーション・サーバーを管理する

はじめのうちは毎回

python server.py

でtornadoを起動すればいいんですが,実際運用しようと思うとプロセスがいつ死ぬかわからないし 分散させるために5つのプロセスを同時に立ち上げるみたいなことをしたくなった場合に管理ツールがほしくなります. 最近ではsupervisorが人気でこれを使うと複数のプロセスを監視でき,設定するとプロセスが死んだ時にリスタートしてくれるようになります.

supervisorの導入

supervisorはpython製なのでpipでインストールできます.

pip install supervisor
supervisorの設定ファイルを作成する
echo_supervisord_conf > /etc/supervisord.conf

でsupervisorの設定ファイルを作成します.

これを以下のように変更します(一応補足すると -が削除 +が追加した行です)

-;[inet_http_server]         ; inet (TCP) server disabled by default
-;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
+[inet_http_server]         ; inet (TCP) server disabled by default
+port=127.0.0.1:9001           ; (ip_address:port specifier, *:port for all iface)

 [supervisorctl]
-serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
-;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
+;serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
+serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket

-;[include]
-;files = relative/directory/*.ini
+[include]
+files = /etc/supervisord.d/*.conf

これでsupervisorの本体の設定は完了です.

サンプルアプリ用の設定ファイルを作成する
[group:tornadoes]
programs=tornado-8000

[program:tornado-8000]
command=python server.py
directory=/path/to/your/directory
user=username
autorestart=true
redirect_stderr=true
stdout_logfile=/path/to/your/log_file
loglevel=info

アプリケーションごとにアプリケーションごとに上記のような設定ファイルを作成し,/etc/supervisord.d/以下に配置するとsupervisorが読み込んでくれます.

supervisorの使い方

sudo supervisord -c /etc/supervisord.conf

でsupervisorが起動します オプションはいらないはずですが一応 これで9001番にsupervisorが起動します. もし9001番のポートが公開されてたりがmacのローカルで起動してる場合はhttpサーバーが立っているのでブラウザからアクセスできます.

f:id:kazy1991:20131009133637p:plain

ブラウザから制御してもいいですが,cuiのインターフェースも用意されていて,

sudo supervisorctl
Password:
tornadoes:tornado-8030           RUNNING    pid 19939, uptime 8:21:21
supervisor> stop tornadoes:tornado-8030
tornadoes:tornado-8030: stopped
supervisor> start tornadoes:tornado-8030
tornadoes:tornado-8030: started

こんなかんじで制御することができます.もちろんsupervisorはpython製以外のアプリケーションも制御することができます.

以上で終了です 怒涛の勢いで書いたので後で細かいところは修正します.