nginx+gunicorn+flask環境をComputeEngine上で構築する

# はじめに

GCPのロードバランサーの動作を確認したく、ComputeEngineでウェブアプリを立てることにしました。
もともとはロードバランサーの検証作業が目的だったのですが、その過程の作業もちゃんと記録しておいた方がいいなと思い、今回これを記事にすることにしました。

[「ComputeEngineを使ってPython3系+flask環境でウェブアプリを構築する」](https://qiita.com/Masato_Kikukawa/items/a5354315b4ff9c6ae87e "")という内容については、Qiitaの別記事にしていますので、よかったらそちらも参考にしてください。

今回はの内容は**「nginx+gunicorn+flask」環境を作ること**です。

インストールはできても設定の仕方が分からず上手く動かなかった！
という経験をされた方もいらっしゃるのではないでしょうか。

自分も普段はnginxやgunicornを触る機会があまりないので、色々と手探りで環境を構築しました。
自分の作業の備忘録として記事にしておくという意味もあります:sweat_smile:

よかったら参考にしてください:thumbsup:

---


#### 前提

この記事の内容を進めるにあたっての前提は下記の通りです。

- Linux環境（Debian9）があること
- Python3系環境がインストールされていること
- pip3コマンドが使える状態になっていること
- flaskがインストールされていること

上記の環境を構築するには、こちら↓の記事を参考にしてください。
[GCPのComputeEngine上にPython3系+flask環境を構築する](https://qiita.com/Masato_Kikukawa/items/a5354315b4ff9c6ae87e "")

---

# nginxのインストール

インストールは簡単です。次の２つのコマンドを打てばサクッとインストールできます。

```
sudo apt update
sudo apt install nginx
```

<img width="1012" alt="Shot 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/3d6065aa-b35a-8146-54a5-8d8e83473472.png">

はい、これでインストールは完了です。
5秒もかかりません:wink:

---

## nginxの動作確認

nginxが動作しているか確認していきましょう。


#### 1. デーモン状態確認

まずは、systemctlでデーモンの状態を確認します。

```
sudo systemctl status nginx
```

<img width="1012" alt="Shot 4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/6f8aeccf-7e59-3aa3-1d2b-5a34cf392270.png">

緑の文字のところで「active（Running）」が確認できればOKです。
インストール完了後、自動的にデーモン起動してますね。

もし、activeになっていなかったら、次のコマンドを試してみて下さい。

```
sudo systemctl start nginx
```

#### 2. クライアントリクエストの応答確認

Webサーバが起動しているということで、クライアントからのリクエストにちゃんと応答しているかどうか確認してみましょう。

次のcurlコマンドで確認でいます。

```
curl 127.0.0.1
```

<img width="1012" alt="Shot 5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/ef264ece-4814-b340-32a6-3e7a20101e40.png">

はい、ちゃんとリクエストに応答してくれてますね:ok_hand:

ちなみに、nginxをストップした後、curlコマンドを打つと↓のようになります。
<img width="1012" alt="Shot 6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/066e1cb9-8cdd-5ecb-8be1-605a955376ce.png">



---

# gunicornの環境準備

gunicornをインストールしていきます。
インストールは簡単です。
下記のコマンドでサクッとインストールできます。

※gunicornとは何か？については、[こちら](https://codingnetworker.com/2016/01/deploy-the-network-configuration-generator-using-ansible-and-vagrant/)のページの絵が分かりやすいです。


では、次のコマンドでインストールしていきましょう。

```
sudo pip3 install gunicorn
```

<img width="1012" alt="Shot 7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/65060088-2cfc-cf32-7776-8ad9e5ecee80.png">

このような画面になればOKです。

---

## gunicornの動作確認

まず、gunicornを動かすためにコンフィグファイルを用意してあげます。

#### 1. gconf.pyの準備

gunicornのコンフィグファイルを準備します。
※コンフィグファイルなのに.pyファイルなんです…:sweat_smile:
※ちなみに、今回はgconf.pyというファイル名にしましたが、この名前は任意です。

nanoやvimなどで、このファイルを作成してください。
作成場所はflaskアプリのPythonファイルのある場所に配置します。

今回の例では、ホームディレクトリ直下に配置します。

```python:~/gconf.py
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
```

この設定ファイルのポイントとしては、ソケットの定義（一番下の２行目）部分です。

この定義によってnginxとflaskをソケット経由で連携できるようにしています。

こんな感じでファイルを作ります。

<img width="1012" alt="Shot 3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/9af1189e-1ef0-95cd-7b75-1737e330f28c.png">


配置場所は、この通りホームディレクトリ直下です。

<img width="367" alt="Shot 9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/1e513088-c443-9b40-1808-f9952268c751.png">

ちなみに同じディレクトリにあるflaskの中身であるmain.pyの内容は次の通りです。

```python:~/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
```

<img width="820" alt="Shot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/ba4e902d-0e49-a7d1-3555-a4808c7ef7ea.png">

これでgunicornが起動してます。
ポイントは下記の２つです。

* 「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
```


```conf:/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
```

このような画面になるはずです。
<img width="820" alt="Shot 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/9d9b36dd-78ed-ce51-40d0-69a9c596bc5d.png">

もう一つSSHで別のシェルを立ち上げてcurlしてみます。

```
curl localhost
```

次のような画面になればOKです。
nginxとgunicornが連携されていることになります。

<img width="819" alt="Shot 1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/001f1535-fbcd-506e-523a-d08780f1a01c.png">


---

# その他

環境構築する過程で、その他必要になる可能性のあるコマンドなどを、ここで紹介します。

## 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ファイルの２つが必要になりますので、下記の通り作成します。

## serviceファイル作成

```
sudo nano /etc/systemd/system/gunicorn.service
```

```/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
```

```/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
```

<img width="898" alt="Shot 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/3a27637c-1965-a4d9-f0d9-451fe98130f2.png">

このようにウェブアプリケーションの応答が返ってくればOKです:relaxed:


---


# おわりに

いかがでしたでしょうか。

nginxやgunicorn、そしてsystemdの設定に普段から慣れていないと、結構躓くポイントがあります。
自分も何度か躓きながらこの記事を作りました。

特に全体像が見えないと、躓いたときに自分の作業内容を見失いがちです。

今回の設定の全体像をチャートにしましたので、これをイメージしながら作業すると迷子を防げるかと思います。

また、デーモンやプロセスの状態を適宜確認しながら進めると不具合の原因箇所を特定しやすくなるので、「その他」の項にかかれているコマンドを活用しながら進めるといいかと思います。


![IMG_6822.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556132/a0ae78c8-bb87-4242-f7b3-d9581ab52cf7.jpeg)