とーますメモ

Ruby on Rails / Goなどの学習メモ

【Ansible】Nginx + Gunicornの設定

Nginx + Gunicorn + Flaskの環境構築を行った際のメモ。

流れは、Client => Nginx => Gunicorn => Flask

NginxとGunicornの接続については以下のサイトが参考になった。
Nginx + Gunicorn + Django + Aurora (MySQL) の本番環境をAnsible Playbookで構成する - Qiita
NginxとGunicornの接続をソケットからHTTPに変更した - Qiita
Gunicorn用のSystemdソケットとサービスファイルの作成 - Qiita

Socket接続とHTTP接続があるが、
NginxとGunicornを別々のサーバ上に分けて設定しない限りは、Socket接続のほうが早いので、Socket接続を採用する。

Nginxの設定

やり方は省略するが、nginx用のユーザである「nginx」ユーザを作成する。
そして以下のページと同じ設定を行い、

thoames.hatenadiary.jp

upstreamの箇所を以下のように変更するだけ。

upstream app_server {
  server unix:/run/gunicorn/socket fail_timeout=0;
}

Gunicornの設定

Pythonの環境は以下のページを参考に「pyenv + pipenv」で構築
thoames.hatenadiary.jp

defaults/main.yml (※gunicorn_app_pathは任意)

---

gunicorn_version: "20.0.4"
gunicorn_bin: "{{ working_direcory }}/.venv/bin/gunicorn"

gunicorn_app_name: "gunicorn"
gunicorn_app_path: "run:app"  # app module in run.py

gunicorn_run_dir: "/run/gunicorn"
gunicorn_config_dir: "/etc/gunicorn"
gunicorn_socket: "gunicorn.socket"
gunicorn_bind: "unix:/run/{{gunicorn_app_name}}/socket"
gunicorn_user: "{{ admin_user }}"
gunicorn_dynamic_workers: False
gunicorn_cpu_coefficient: 2
gunicorn_workers: 1

# [Log]: use nginx log functions
gunicorn_loglevel: False
gunicorn_errorlog: False
gunicorn_accesslog: False
# gunicorn_loglevel: "info"
# gunicorn_errorlog: "/var/log/gunicorn/error.log"
# gunicorn_accesslog: "/var/log/gunicorn/access.log"

gunicorn_reload: False    # only for 'development'
gunicorn_timeout: 600   # because of scraping

tasks/main.yml

---

- name: Check exist pipenv command
  shell: type pipenv
  register: exist_pipenv
  failed_when: false
  tags: gunicorn

- name: Install Gunicorn
  shell: bash -lc "pipenv install gunicorn=={{ gunicorn_version }}"
  args:
    chdir: "{{ working_direcory }}"
  when: exist_pipenv is success
  tags: gunicorn

- name: Make gunicorn config directory
  file: path={{ gunicorn_config_dir }} state=directory
  tags: [gunicorn, gunicorn_config]

- name: Configure gunicorn
  template: src=gunicorn.py.j2 dest={{ gunicorn_config_dir }}/{{ gunicorn_app_name }}.py
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Configure gunicorn socket
  template: src=gunicorn.socket.j2 dest=/etc/systemd/system/{{ gunicorn_socket }}
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Configure systemd service
  template: src=systemd.conf.j2 dest=/etc/systemd/system/{{ gunicorn_app_name }}.service mode="0755"
  notify:
    - restart socket
    - restart gunicorn
  tags: [gunicorn, gunicorn_config]

- name: Enable gunicorn service
  service: name="{{ gunicorn_app_name }}.service" enabled=yes
  tags: [gunicorn, gunicorn_config]

handlers/main.yml

---
  
- name: restart gunicorn
  service: name={{ gunicorn_app_name }} state=restarted daemon_reload=yes

- name: restart socket
  systemd: name={{gunicorn_app_name}}.socket enabled=yes state=restarted daemon_reload=yes

gunicorn.socket

[Unit]
Description=gunicorn socket

[Socket]
ListenStream={{ gunicorn_run_dir }}/socket

[Install]
WantedBy=sockets.target

gunicorn.py (gunicorn用の設定ファイル)

import multiprocessing

bind = '{{ gunicorn_bind }}'
timeout = {{gunicorn_timeout}}

{% if gunicorn_loglevel %}
loglevel = '{{ gunicorn_loglevel }}'
{% endif %}
{% if gunicorn_accesslog %}
accesslog = '{{ gunicorn_accesslog }}'
{% endif %}
{% if gunicorn_errorlog %}
errorlog = '{{ gunicorn_errorlog }}'
{% endif %}

reload = {% if gunicorn_reload %}True{% else %}False{% endif %}

{% if gunicorn_dynamic_workers %}
workers = ({{gunicorn_cpu_coefficient}} * multiprocessing.cpu_count()) + 1
{% else %}
workers = {{gunicorn_workers}}
{% endif %}

gunicorn.service (systemd用のサービス設定)

[Unit]
Description = {{ gunicorn_app_name }}
Requires = {{ gunicorn_socket }}
After = network.target

[Service]
PermissionsStartOnly = true
User = {{ gunicorn_user }}
Group = {{ gunicorn_user }}
RuntimeDirectory = {{ gunicorn_app_name }}
PIDFile = {{ gunicorn_run_dir }}/{{ gunicorn_app_name }}.pid
WorkingDirectory = {{ working_direcory }}

ExecStart = {{ gunicorn_bin }} --pid {{ gunicorn_run_dir }}/{{ gunicorn_app_name }}.pid \
                               --config {{ gunicorn_config_dir }}/{{ gunicorn_app_name }}.py \
                               {{ gunicorn_app_path }}

ExecReload = /bin/kill -s HUP $MAINPID
ExecStop = /bin/kill -s TERM $MAINPID
PrivateTmp = true

[Install]
WantedBy = multi-user.target


[その他参考]
GitHub - azavea/ansible-gunicorn: An Ansible role for installing and configuring gunicorn.
Django + Nginx + Gunicorn でアプリケーションを立ち上げる | WEBカーテンコール
ansible/gunicorn.service.j2 at master · UTNkar/ansible · GitHub
Deploying Gunicorn — Gunicorn 20.0.4 documentation