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


あらまし

Django + MySQL で開発したアプリケーションの本稼働用の環境を、Ansible を使って Amazon EC2 上に構成しました。
前回の投稿では Nginx と Gunicorn を UNIX ドメインソケットで接続していましたが、これを HTTP 接続に変更しました。

UNIX ドメインソケット接続

これが変更前の構成です。

なぜソケットで接続したのか

本来のソケットのメリットは通信が高速であることです。以下のリンクで学習しました。
Unixドメインソケット - Goならわかるシステムプログラミング ― 第9回
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話

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

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

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

(2) Udemy のコース

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

HTTP接続

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

なぜHTTP接続に変更したのか

(1) 構成変更の自由度

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

(2) トラブルシューティングのしやすさ

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

Ansible Playbookの変更

前回作成した Playbook の Djangoロール から ソケットのユニットファイルをテンプレートから生成するタスク を丸ごと削除して、残った部分を以下のように変更しました。

varsファイル

前回: varsファイル
以下の変数を追加しました。

  • 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 のポートです。
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サービスのユニットファイルをテンプレートから生成して起動するタスク

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

  • ソケットへの依存がなくなったので、Requires=gunicorn.socketを削除しました。
  • ExecStart--bindの値をソケットから IPアドレス:ポートに変更し、--bind 127.0.0.1:8000としました。
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の仮想ホスト設定ファイルをテンプレートから生成するタスク

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

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に変更することができました。

参考

記事一覧