Flaskアプリをgunicornで動かし、nginxの裏で動かしたい。これをDockerコンテナとしてHerokuで動かしてみたいので、Supervisorを用いて1つのコンテナ内でnginxとgunicornの両方を起動できるようにするということを、HerokuでDockerコンテナを動かす時の制約を解決しながらやってみた。
nginxの設定
まずは、nginxを単体でHerokuで動くように設定していく。
gunicornのサイトにあるサンプルのnginx.confを元に作業したので、書き換えが必要な部分だけ記載していく。
http://docs.gunicorn.org/en/latest/deploy.html
ログの出力先変更
Herokuではログをstdoutとstderrに出力したいのでそのように設定する。
error_log stderr;
http {
server {
access_log /dev/stdout;
}
}
temp_path設定の変更
Herokuではコンテナは非rootユーザで動作するため、client_body_temp_pathなどを書き込める場所に設定する必要がある。
http {
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
PORTの設定
Heroku上のDockerコンテナは環境変数PORTで実行環境から渡されるポート番号をlistenする必要がある。
nginx.confの設定に環境変数を渡す方法としてperl_setなどを使う方法があるが、listenのポート番号に対しては使用できないらしい(参考)。そのためDockerfileのCMDの中で、sedでも使って無理矢理渡すくらいしか方法がなさそうだった。
さらに、前述のとおり実行ユーザの権限によりsedで書き換えたnginx.confの出力先に注意が必要で、その影響で一部の設定変更も必要になる。
COPY ./nginx.conf /opt/nginx.conf.temp
CMD sed -e "s/ENV_PORT/$PORT/g" nginx.conf.temp > /tmp/nginx.conf && \
nginx -c /tmp/nginx.conf -g "daemon off;"
# nginx.confの位置が通常と変わるので変更が必要
#include mime.types;
include /etc/nginx/mime.type;
ここまでで、いったんnginxが立ち上がり、当然まだgunicornは動いていないのでエラーページが表示される。
Supervisor
次にSupervisorを設定し、DockerfileのCMDをsupervisordの起動に変更する。
PORTのためのsedはCMD側に残ることになる。
COPY ./supervisord.conf /etc/
CMD sed -e "s/ENV_PORT/$PORT/g" nginx.conf.temp > /tmp/nginx.conf && \
supervisord -c /etc/supervisord.conf
supervisord.confでは、logfileやpidfileを設定しないと、デフォルト設定ではこちらも書き込み権限がなくてエラーになるところにだけ注意。
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/tmp/supervisord.pid
[program:nginx]
command=nginx -c /tmp/nginx.conf -g "daemon off;"
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:gunicorn]
command=gunicorn --bind unix:/tmp/gunicorn.sock app:app
以上で、Herokuにデプロイして動作することが確認できた。