はじめに
GCPのロードバランサーの動作を確認したく、ComputeEngineでウェブアプリを立てることにしました。
もともとはロードバランサーの検証作業が目的だったのですが、その過程の作業もちゃんと記録しておいた方がいいなと思い、今回これを記事にすることにしました。
「ComputeEngineを使ってPython3系+flask環境でウェブアプリを構築する」という内容については、Qiitaの別記事にしていますので、よかったらそちらも参考にしてください。
今回はの内容は**「nginx+gunicorn+flask」環境を作ること**です。
インストールはできても設定の仕方が分からず上手く動かなかった!
という経験をされた方もいらっしゃるのではないでしょうか。
自分も普段はnginxやgunicornを触る機会があまりないので、色々と手探りで環境を構築しました。
自分の作業の備忘録として記事にしておくという意味もあります
よかったら参考にしてください
前提
この記事の内容を進めるにあたっての前提は下記の通りです。
- Linux環境(Debian9)があること
- Python3系環境がインストールされていること
- pip3コマンドが使える状態になっていること
- flaskがインストールされていること
上記の環境を構築するには、こちら↓の記事を参考にしてください。
GCPのComputeEngine上にPython3系+flask環境を構築する
nginxのインストール
インストールは簡単です。次の2つのコマンドを打てばサクッとインストールできます。
sudo apt update
sudo apt install nginx
![Shot 2.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F3d6065aa-b35a-8146-54a5-8d8e83473472.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=a3e47cfd58dbc7c7f55361e132540099)
はい、これでインストールは完了です。
5秒もかかりません
nginxの動作確認
nginxが動作しているか確認していきましょう。
1. デーモン状態確認
まずは、systemctlでデーモンの状態を確認します。
sudo systemctl status nginx
![Shot 4.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F6f8aeccf-7e59-3aa3-1d2b-5a34cf392270.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ba2b8ccc603988c999bb4663b058cdb2)
緑の文字のところで「active(Running)」が確認できればOKです。
インストール完了後、自動的にデーモン起動してますね。
もし、activeになっていなかったら、次のコマンドを試してみて下さい。
sudo systemctl start nginx
2. クライアントリクエストの応答確認
Webサーバが起動しているということで、クライアントからのリクエストにちゃんと応答しているかどうか確認してみましょう。
次のcurlコマンドで確認でいます。
curl 127.0.0.1
![Shot 5.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2Fef264ece-4814-b340-32a6-3e7a20101e40.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=048e69e1bd2649381de8307a45ead099)
はい、ちゃんとリクエストに応答してくれてますね
ちなみに、nginxをストップした後、curlコマンドを打つと↓のようになります。
gunicornの環境準備
gunicornをインストールしていきます。
インストールは簡単です。
下記のコマンドでサクッとインストールできます。
※gunicornとは何か?については、こちらのページの絵が分かりやすいです。
では、次のコマンドでインストールしていきましょう。
sudo pip3 install gunicorn
![Shot 7.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F65060088-2cfc-cf32-7776-8ad9e5ecee80.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ca085d6683d460a7cdbab49beb8356a5)
このような画面になればOKです。
gunicornの動作確認
まず、gunicornを動かすためにコンフィグファイルを用意してあげます。
1. gconf.pyの準備
gunicornのコンフィグファイルを準備します。
※コンフィグファイルなのに.pyファイルなんです…
※ちなみに、今回はgconf.pyというファイル名にしましたが、この名前は任意です。
nanoやvimなどで、このファイルを作成してください。
作成場所はflaskアプリのPythonファイルのある場所に配置します。
今回の例では、ホームディレクトリ直下に配置します。
import multiprocessing
# Worker Processes
workers = 2
worker_class = 'sync'
# Logging
logfile = '/var/www/apps/sampleapp/app.log'
loglevel = 'info'
logconfig = None
# Socket Definition
socket_path = 'unix:/tmp/gunicorn.sock'
bind = socket_path
この設定ファイルのポイントとしては、ソケットの定義(一番下の2行目)部分です。
この定義によってnginxとflaskをソケット経由で連携できるようにしています。
こんな感じでファイルを作ります。
![Shot 3.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F9af1189e-1ef0-95cd-7b75-1737e330f28c.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=5f94030f19c54ad53e19bb18a7f7078d)
配置場所は、この通りホームディレクトリ直下です。
![Shot 9.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F1e513088-c443-9b40-1808-f9952268c751.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=15a2d882b3c9a3de8ef9e302edd57949)
ちなみに同じディレクトリにあるflaskの中身であるmain.pyの内容は次の通りです。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, Flask World!!'
if __name__ == '__main__':
app.run(debug=False, host='127.0.0.1', port=5000)
2. gunicornの起動
gconf.pyファイルも準備できたので、次はgunicornの起動を確認します。
次のコマンドでgunicornを起動します。
※前提としては、main.pyとgconf.pyがカレントディレクトリに存在することです。
gunicorn main:app --config gconf.py
![Shot.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2Fba4e902d-0e49-a7d1-3555-a4808c7ef7ea.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ceedad77e9e0b7c4b4723ff9b464d6f0)
これでgunicornが起動してます。
ポイントは下記の2つです。
- 「main:aap」のところで、カレントディレクトリにある「main.py」を指定している。
- gconf.pyでgunicornの設定をしている。
もう一つ、SSHでシェルを立ち上げて、curlコマンドで応答を確認してみます…
と、いいたいところですが、これはまだできません。
nginxとgunicornを繋げてあげる必要があるからです。
それについては、次に説明します。
nginxとgunicornを繋げる
nginxとgunicornはsocketで連携します。
gunicorn側の設定は、すでにgconf.pyの中でソケットの定義をしています。
後は、nginx側の設定が必要になりますので、ここではその設定について説明します。
nginxのディレクトリ(/etc/nginx/conf.d/)にproject.confファイルを作成します。
sudo nano /etc/nginx/conf.d/project.conf
upstream app_server{
server unix:/tmp/gunicorn.sock;
}
server{
listen 80;
server_name appserver;
location = /favicon.ico { access_log off; log_not_found off;}
location /static/ {
alias /usr/share/nginx/html/static;
}
}
そして、もともとあったdefaultのnginx設定ファイルを次のコマンドで移動します。
※defaultの設定を無効化するため
sudo mv /etc/nginx/sites-enabled/default ~/default
そして、設定を読み込ませるために次のコマンドでnginxをreloadします。
sudo systemctl reload nginx
※この段階でエラーが出る場合は、設定ファイルが誤っている可能性があります。
ファイルチェックするには次のコマンドを打ってください。
sudo nginx -t
nginxの動作確認(gunicorn連携)
gunicornの設定及びnginxの設定が完了したので、動作確認してみます。
改めて下記のコマンドでgunicornを起動します。
gunicorn main:app --config gconf.py
もう一つSSHで別のシェルを立ち上げてcurlしてみます。
curl localhost
次のような画面になればOKです。
nginxとgunicornが連携されていることになります。
![Shot 1.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F001f1535-fbcd-506e-523a-d08780f1a01c.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=6c5e339e0fe3ddf4b48899da2cc0ffb1)
その他
環境構築する過程で、その他必要になる可能性のあるコマンドなどを、ここで紹介します。
gunicornのプロセス稼働確認
ps ax|grep gunicorn
gunicornのプロセスkill
pkill gunicorn
nginxコンフィグファイルの構文チェック
sudo nginx -t
nginxの状態確認
sudo systemctl status nginx
nginxエラーログの確認
cat /var/log/nginx/error.log
gunicorn.socketの状態確認
sudo systemctl status gunicorn.socket
gunicorn.serviceの状態確認
sudo systemctl status gunicorn.service
gunicornの自動起動(SystemDの設定)
一応、これまでの記事内容で「nginx+gunicorn+flask」環境は構築できます。
ただ、私の場合、サーバ起動時に自動的にgunicornプロセスも起動させたかったので、その方法をここに記録しておきます。
serviceファイルとsocketファイルの2つが必要になりますので、下記の通り作成します。
serviceファイル作成
sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
User=masato_kikukawa_topgate_co_jp
Group=masato_kikukawa_topgate_co_jp
RuntimeDirectory=gunicorn
WorkingDirectory=/home/masato_kikukawa_topgate_co_jp/
ExecStart=/usr/local/bin/gunicorn main:app --config gconf.py
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
socketファイル作成
sudo nano /etc/systemd/system/gunicorn.socket
Description=gunicorn socket
[Socket]
ListenStream=/tmp/gunicorn.sock
[Install]
WantedBy=sockets.target
ソケットの有効化
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
gunicorn自動起動の動作確認
ComputeEngineのVMを再起動して、gunicornが問題なく起動しているかcurlコマンドで確認します。
curl localhost
![Shot 2.png](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F556132%2F3a27637c-1965-a4d9-f0d9-451fe98130f2.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=85c9a305c438593e75240790f2d4930c)
このようにウェブアプリケーションの応答が返ってくればOKです
おわりに
いかがでしたでしょうか。
nginxやgunicorn、そしてsystemdの設定に普段から慣れていないと、結構躓くポイントがあります。
自分も何度か躓きながらこの記事を作りました。
特に全体像が見えないと、躓いたときに自分の作業内容を見失いがちです。
今回の設定の全体像をチャートにしましたので、これをイメージしながら作業すると迷子を防げるかと思います。
また、デーモンやプロセスの状態を適宜確認しながら進めると不具合の原因箇所を特定しやすくなるので、「その他」の項にかかれているコマンドを活用しながら進めるといいかと思います。