NginxとGunicornの接続をソケットからHTTPに変更した

# あらまし
Django + MySQL で開発したアプリケーションの本稼働用の環境を、Ansible を使って Amazon EC2 上に構成しました。
[前回の投稿](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559)では Nginx と Gunicorn を UNIX ドメインソケットで接続していましたが、これを HTTP 接続に変更しました。

## UNIX ドメインソケット接続
これが変更前の構成です。

![aws-network-wsgi_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/d4fa8ce6-fcc7-ce3d-146b-56e0d8c24a91.png)

### なぜソケットで接続したのか
本来のソケットのメリットは通信が高速であることです。以下のリンクで学習しました。
● [Unixドメインソケット - Goならわかるシステムプログラミング ― 第9回](https://ascii.jp/elem/000/001/415/1415088/)
● [調べなきゃ寝れない！と調べたら余計に寝れなくなったソケットの話](https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748)

しかしソケット接続を選んだ最大の理由は、単純に Gunicorn の公式ドキュメントと Udemy のコース内容に倣ったためでした。

#### (1) Gunicorn の公式ドキュメント

参考: Deploying Gunicorn (Systemd): http://docs.gunicorn.org/en/latest/deploy.html#systemd

![gunicorn-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/bd7d0531-8748-c27e-d5eb-aba97acf5b47.png)

#### (2) Udemy のコース

このコースを購入して学習しました。
「【徹底的に解説！】Djangoの基礎をマスターして、3つのアプリを作ろう！」

## HTTP接続
しかしその後、ソケットを削除して下図の構成に変更しました。
（左上の /run/gunicorn ディレクトリは PID ファイルの保存用なので、接続方式とは無関係です）

![aws-network-wsgi_http.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/eb4ba338-0b0e-c2de-12e8-6ea8367aa22c.png)

### なぜHTTP接続に変更したのか
#### (1) 構成変更の自由度

* Nginx と Gunicorn を別インスタンスに配置することが可能になります（ソケットは同一インスタンス内接続のみ）
* Nginx と Gunicorn の関係を 1対多にすることが可能になります
* Nginx を ELB（Elastic Load Balancing）などと交換することが可能になります

#### (2) トラブルシューティングのしやすさ
図のようにクライアントから直接 Gunicorn に接続できるので、問題発生時に原因箇所の切り分けをしやすくなります。また gunicorn.socket への依存がなくなるので、問題の要因自体が減ります。

# Ansible Playbookの変更
前回作成した Playbook の [Djangoロール](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559#django%E3%83%AD%E3%83%BC%E3%83%AB) から [ソケットのユニットファイルをテンプレートから生成するタスク](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559#%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88%E3%81%AE%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%8B%E3%82%89%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%82%BF%E3%82%B9%E3%82%AF) を丸ごと削除して、残った部分を以下のように変更しました。

## varsファイル
前回: [varsファイル](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559#vars%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)
以下の変数を追加しました。

* `gunicorn_allow_ipaddr`は Gunicorn から見た Nginx の IPアドレスで、127.0.0.1 は同一インスタンス内からの接続のみを許可する設定です。0.0.0.0 にすればどこからでも接続できるようになります。
* `gunicorn_listen_ipaddr`は Nginx から見た Gunicorn の IPアドレスです。
* `gunicorn_listen_port`は Nginx から見た Gunicorn のポートです。

```Yaml:inventories/development/group_vars/all.yml
gunicorn_allowed_ipaddr: '127.0.0.1'  # 開発中は 0.0.0.0 でOK
gunicorn_listen_ipaddr: '127.0.0.1'
gunicorn_listen_port: 8000
```
## Gunicornサービスのユニットファイル
前回: [Gunicornサービスのユニットファイルをテンプレートから生成して起動するタスク](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559#gunicorn%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%8B%E3%82%89%E7%94%9F%E6%88%90%E3%81%97%E3%81%A6%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B%E3%82%BF%E3%82%B9%E3%82%AF)

ユニットファイルのテンプレートを以下のように変更しました。

* ソケットへの依存がなくなったので、`Requires=gunicorn.socket`を削除しました。
* `ExecStart`の`--bind`の値をソケットから IPアドレス:ポートに変更し、`--bind 127.0.0.1:8000`としました。

```Ini:roles/django/templates/gunicorn.service.j2
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
PIDFile={{ gunicorn_run_dir }}/pid
User={{ service_user }}
Group={{ service_group }}
WorkingDirectory={{ django_project_base_dir }}
ExecStart=/usr/local/bin/gunicorn --pid {{ gunicorn_run_dir }}/pid   \
          --bind {{ gunicorn_allowed_ipaddr }}:{{ gunicorn_listen_port }} \
          {{ django_project_name }}.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

## Nginxの仮想ホスト設定ファイル
前回:[Nginxの仮想ホスト設定ファイルをテンプレートから生成するタスク](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559#nginx%E3%81%AE%E4%BB%AE%E6%83%B3%E3%83%9B%E3%82%B9%E3%83%88%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%8B%E3%82%89%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%82%BF%E3%82%B9%E3%82%AF)

`proxy_pass`で指定する転送先をソケットから HTTP の IPアドレス:ポートに変更し、`proxy_pass http://127.0.0.1:8000;`としました。

```Shell:roles/django/templates/nginx.conf.j2
server {
    listen       80;
    server_name  {{ ansible_host }};
    server_tokens off;

    location / {
        proxy_pass http://{{ gunicorn_listen_ipaddr }}:{{ gunicorn_listen_port }};  # ソケットから変更
        proxy_set_header Host               $host;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host   $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Real-IP          $remote_addr;
    }

    location /static {
        alias {{ django_project_base_dir }}/static;
    }
}
```

以上で Nginx と Gunicorn の接続をソケットからHTTPに変更することができました。

# 参考
* [Gunicorn Docs » Deploying Gunicorn (Systemd)](http://docs.gunicorn.org/en/latest/deploy.html#systemd)
* [Unixドメインソケット - Goならわかるシステムプログラミング ― 第9回](https://ascii.jp/elem/000/001/415/1415088/)
* [調べなきゃ寝れない！と調べたら余計に寝れなくなったソケットの話](https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748)

## 記事一覧

* 1/4: [DjangoをAmazon Linux 2にAnsibleで構成する、Aurora (MySQL) をCloudFormationでUTF-8に構成する、そして接続するまでが地味に大変だったこと](https://qiita.com/3244/items/6000822f3a165e71c56d)
* 2/4: [Nginx + Gunicorn + Django + Aurora (MySQL) の構成を図で説明してみる](https://qiita.com/3244/items/c83d9449f0286707b2fb)
* 3/4: [Nginx + Gunicorn + Django + Aurora (MySQL) の本番環境をAnsible Playbookで構成する](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559)
* 4/4: NginxとGunicornの接続をソケットからHTTPに変更した