PythonのWebアプリ動かす時にやったこととかメモ
はじめに
ISUCONとかコンテスト系のイベントでPythonのWebアプリを出来るだけ早くセットアップしたい時があるので自分なりの手順とかメモ。自分が後で見返す用なのでまとまってないです。データベースに関しては別の記事に分けました。
ログインしてまずやること
Gistにセットアップ用のシェルスクリプトを置いておく。
こういうセットアップはAnsibleを使ってもいいかなって思ったけどチューニング系のイベントだと、複数台のサーバを使うこともないし何度もデプロイするから冪等性が保証されてほしいとかもない気がするのでシェルスクリプトを選択。これから紹介するコマンド群をGistに保存して、 curl GIST_URL | sh
みたいに実行する。それも面倒なときはシェル上にコピペしてください。
事前準備
# for centos sudo yum install -y wget vim git htop tmux unzip # for ubuntu sudo apt-get install -y wget vim git htop tmux unzip # common cat << 'EOF' > $HOME/.bashrc alias ls='ls -G' alias la="ls -a" alias ll="ls -al" if [ -d "$HOME/bin" ] ; then PATH="$HOME/bin:$PATH" fi # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi EOF
dotfilesもDropboxのpublicフォルダに置いておいて、とってくる。vimrcに関しては普段のものと分けている。プラグインは不要だったり、Macの今のマシンに依存した設定とかも混ざってたりするので。
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
Python3のインストール
参考: * https://github.com/pyenv/pyenv/wiki/common-build-problems * https://devguide.python.org/setup/
# for centos sudo yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel sudo yum install -y python-devel gcc # for ubuntu sudo apt-get install -y python3-dev gcc build-essential sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev # common mkdir python && cd python wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz tar zxvf Python-3.7.0.tgz cd Python-3.7.0 sudo ./configure --enable-optimizations --prefix=/opt/python-3.7.0 make sudo make install
mkdir $HOME/bin; cd $HOME/bin ln -s /opt/python-3.7.0/bin/python3.7 . cd $HOME source $HOME/.bashrc python3.7 -m venv venv source venv/bin/activate
/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
- http://uwsgi-docs.readthedocs.org/en/latest/WSGIquickstart.html
- https://uwsgi-docs.readthedocs.org/en/latest/tutorials/Django_and_nginx.html#using-unix-sockets-instead-of-ports
$ 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> .
- 作者: Dimitri Aivaliotis,高橋基信
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/10/26
- メディア: 大型本
- この商品を含むブログ (7件) を見る