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

c-bata web

@c_bata_ のメモ。python多め

PythonのWebアプリ動かす時にやったこととかメモ

Python Webアプリ Nginx

はじめに

ISUCONとかコンテスト系のイベントでPythonのWebアプリを出来るだけ早くセットアップしたい時があるので自分なりの手順とかメモ。自分が後で見返す用なのでまとまってないです。データベースに関しては別の記事に分けました。

nwpct1.hatenablog.com


ログインしてまずやること

Dropboxのpublicフォルダにセットアップ用のシェルスクリプトを置いておく。

こういうセットアップはAnsibleを使ってもいいかなって思ったけどチューニング系のイベントだと、複数台のサーバを使うこともないし何度もデプロイするから冪等性が保証されてほしいとかもない気がするのでシェルスクリプトを選んだ。

Python3のインストール

下記のファイルをDropboxのPublicフォルダに保存して、 curl dl.dropboxusercontent.com/..../install_python.txt | sh みたいにする。

# install_python.txt
yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel
yum install -y python-devel gcc

mkdir python && cd python
wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tgz
tar zxvf Python-3.4.3.tgz
cd Python-3.4.3
./configure --prefix=/opt/python-3.4.3
make && make install

mkdir $HOME/bin; cd $HOME/bin
ln -s /opt/python-3.4.3/bin/python3.4 .
cd $HOME
source $HOME/.bashrc
virtualenv -p python3.4 venv
source venv/bin/activate

pip install flask uwsgi redis sqlalchemy ipython

dotfilesとか

dotfilesもDropboxのpublicフォルダに置いておいて、とってくる。vimrcに関しては普段のものと分けている。プラグインは不要だったり、Macの今のマシンに依存した設定とかも混ざってたりするので。

  • .bashrc
  • .gitconfig
  • .tmux.conf
  • .vimrc
# download_dotfiles.txt
yum -y install tmux git
curl https://dl.dropboxusercontent.com/..../dotfiles/bashrc > ~/.bashrc
curl https://dl.dropboxusercontent.com/..../dotfiles/gitconfig > ~/.gitconfig
curl https://dl.dropboxusercontent.com/..../dotfiles/tmux.conf > ~/.tmux.conf
curl https://dl.dropboxusercontent.com/..../dotfiles/vimrc > ~/.vimrc

/etc/ 以下をgitで管理

# .gitignore
*
*/

コンテスト中はnginxとかmysqlの設定ファイルを結構変更するので、.gitignore は全てignoreするように設定しておいて、管理したいものだけ git add -f nginx/nginx.conf みたいにする。


uWSGIを使う

Pythonで実装されてるgunicornよりも、Cで実装されてるuWSGI使ったほうが基本的に速いらしい。

$ uwsgi --http :9090 --wsgi-file app.py --callable app

UNIXドメインソケットを使うなら↓のように書き換える

$ uwsgi --socket /tmp/uwsgi.sock --wsgi-file app.py --callable app


Nginxを使う

nginx.confを以下のようにする。syntaxがおかしいかは、 nginx -t と入力すればチェックしてくれる。

worker_processes  8;

events {
  worker_connections  1024;
}

http {
  upstream app {
    server 127.0.0.1:8080;
  }

  server {
    location / {
      proxy_set_header Host $host;
      proxy_pass http://app;
    }
  }
}

UNIXドメインソケットを使う

最初動かなかったのでerror logを確認したら、Permission deniedだった。userをrootに変えた。

user  root;
worker_processes  16;

error_log  /var/log/nginx/error.log;

pid        /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    log_format with_time '$remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent" $request_time';
    access_log /var/log/nginx/access.log with_time;

    server {
        location / {
            proxy_set_header Host $host;
            uwsgi_pass unix:///tmp/uwsgi.sock;
            include uwsgi_params;
        }
    }

    include /etc/nginx/conf.d/*.conf;
}

ログを調べる

ログのフォーマットを上のコードのように指定しておくと kataribe というツールが使える。インストールの方法はgithubのreleasesからlinux版のURLをコピーしてから、wgetとunzipするだけ。Goの良さが出てますね。

$ cat /var/log/nginx/access.log | ./kataribe [-f kataribe.toml]

Nginxのアクセスログawkとかで処理しやすいので、コマンド繋げて調べてみても良いかもしれない。

# cat /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -n


Redisを使う

Pythonでは redis-py を使えばいい。pip install redis で入る。 更新の無いデータとかはRedisに乗せていった。

uWSGIとRedisをUNIXドメインソケットでつなぐ

/etc/redis/redis.conf のbindをコメントアウトして、unixsocketとunixsocketpermのコメントアウトを外す。あとは redisモジュールの接続を以下のように書き換える。

r = redis.Redis(host='localhost', port=6379, db=0)

r = redis.Redis(unix_socket_path='/tmp/redis.sock') 

ただ、permission deniedと怒られたから設定ファイルの unixsocketpermをとりあえず777にした。セキュリティ的に問題あるかもだけどコンテストなので無視。


Pythonのコードのプロファイリング

この記事が簡潔でよかった。cProfileでもPyCallGraphでも良さそう。 Webアプリならリクエストオブジェクトがいるけど、bottleのコードなら↓みたいにすればcProfileがつかえる。

bottle.request.user = {
    "id": 3657,
    "account_name": "edwardo3657",
    "nick_name": "タダハル",
    "email": "edwardo3657@isucon.net",
}
bottle.TEMPLATE_PATH.insert(0,'/home/isucon/webapp/python/views/')
cProfile.run("app.get_index()", sort='tottime')

ちなみにPYTHONPATH指定して起動するのは↓みたいにやればよかった

$ PYTHONPATH=/home/isucon/webapp/python/ bin/ipython


サービスの管理

色々あって混乱したので使ったものをいくつかメモ。まだあまり詳しくないけど、supervisordとかもよさそう。

systemd

  • 一覧: systemctl list-units
  • 起動停止: start, stop
  • 再起動: restart, reload (reloadした方が出来るだけrequestさばいてからやってくれるとか)
  • 自動起動: enable, disable
  • 自動起動か確認: is-enabled

service

System V系

$ service --status-all
$ service nginx status  # /etc/init.d/nginx status と一緒
$ service nginx restart

runits

runitsはsvコマンドで制御するっぽいけど、パスが通ってなかったのとどこにあるのか分からなかった。 このページみたいに、.../supervice/status に文字を送ればいいっぽい。 停止は d

[root@samlab gitlab]# echo d > service/nginx/supervise/control
[root@samlab gitlab]# netstat -apn | grep :80
tcp        0      0 127.0.0.1:8080              0.0.0.0:*                   LISTEN      21075/unicorn maste


知っとくと便利だったコマンド

雑にメモ

  • ポートを占有しているプロセスを特定。
[root@samlab etc]# netstat -apn | grep :80
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      18583/nginx
tcp        0      0 127.0.0.1:8080              0.0.0.0:*                   LISTEN      21075/unicorn maste
  • ps auxf で親子関係まで分かる
  • find . -name nginx.conf
  • scp -i 秘密鍵 <username>@<hostname>:<file_path> .

マスタリングNginx

マスタリングNginx