DEBOPS.GUNICORN(5)

NAME
debops.gunicorn - Manage Gunicorn using Debian packages via Ansible

SYNOPSIS

debops service/gunicorn [--limit group,host,...] [--diff] [--check] [--tags tag1,tag2,...] [--skip-tags tag1,tag2,...] [<ansible-playbook options>] ...

DESCRIPTION

The Green Unicorn <http://gunicorn.org/> is a Python WSGI HTTP Server for UNIX. It uses a pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.

The debops.gunicorn Ansible role uses the Debian package configuration structure <https://chris-lamb.co.uk/posts/sysadmin-friendly-deployment-gunicorn-debian> on older Debian and Ubuntu releases to manage multiple gunicorn applications as a single service. This can be used to deploy applications that use either a system Python installation, or a virtualenv Python environment.

From Debian Stretch upwards, the role configures a custom set of systemd units to support Green Unicorn service instances.

GETTING STARTED

  Example inventory

To enable Green Unicorn service on a host, that host should be added to to a specific Ansible inventory group:

[debops_service_gunicorn]
hostname

  Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.gunicorn role:

---
- name: Manage Green Unicorn service
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_gunicorn' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  roles:
    - role: apt_preferences
      tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
      apt_preferences__dependent_list:
        - '{{ gunicorn__apt_preferences__dependent_list }}'
    - role: cron
      tags: [ 'role::cron', 'skip::cron' ]
    - role: logrotate
      tags: [ 'role::logrotate', 'skip::logrotate' ]
      logrotate__dependent_config:
        - '{{ gunicorn__logrotate__dependent_config }}'
    - role: python
      tags: [ 'role::python', 'skip::python', 'role::gunicorn' ]
      python__dependent_packages3:
        - '{{ gunicorn__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ gunicorn__python__dependent_packages2 }}'
    - role: gunicorn
      tags: [ 'role::gunicorn', 'skip::gunicorn' ]

SUPPORT FOR VIRTUALENV ENVIRONMENTS

The role can be used to run WSGI applications deployed in a virtualenv <http://virtualenv.org/> environment. To do this, you need to install a gunicorn Python module inside the environment at the same version as the Debian package. Below you can find instructions for doing that with Ansible.

The debops.gunicorn role deploys an Ansible local facts script which returns the version of the system gunicorn package. This fact can be accessed as:

ansible_local.gunicorn.version

In your role, you can define a YAML list which specifies what Python modules should be present in the virtualenv environment:

---
application__virtualenv_pip_packages:
  # Install compatible 'gunicorn' module inside the virtualenv
  - name: 'gunicorn'
    version: '{{ ansible_local.gunicorn.version|d(omit) }}'
  # Install 'setproctitle' for nice process names
  # (You'll need gcc and the python-dev or python3-dev package for this)
  - 'setproctitle'

Next, in your role task list, include set of tasks that will create the virtualenv environment and install the required modules inside:

---
- name: Install additional Python modules for gunicorn support
  pip:
    name:    '{{ item.name    | d(item) }}'
    version: '{{ item.version | d(omit) }}'
    virtualenv: '/path/to/virtualenv'
  with_flattened: '{{ application__virtualenv_pip_packages }}'
  become_user: 'app-user'

The above steps should ensure that the application deployed in the virtualenv environment can be started by the gunicorn service installed from the Debian packages. To do that, you can define the application using the role dependent variables in your role's defaults/main.yml file, like this:

---
application__gunicorn__dependent_applications:
  - name: 'virtualenv-app'
    working_dir: '/path/to/virtualenv/app/src'
    python: '/path/to/virtualenv/bin/python'
    mode: 'wsgi'
    user: 'app-user'
    group: 'app-group'
    args: [ '--bind=unix:/run/gunicorn/virtualenv-app.sock',
            '--workers={{ ansible_processor_vcpus|int + 1 }}',
            '--timeout=10' 'virtualenv-app.wsgi' ]

And the corresponding playbook which uses debops.gunicorn as a dependent role:

---
- name: Deploy the application
  hosts: [ 'application-hosts' ]
  become: True
  roles:
    - role: gunicorn
      gunicorn__dependent_applications:
        - '{{ application__gunicorn__dependent_applications }}'
    - role: application-role

Currently there's no way to request that the gunicorn service should be restarted apart from specifying the Restart gunicorn Ansible handler directly in your role, for example like this:

---
- name: Generate the application configuration
  template:
    src: 'path/to/virtualenv/app/src/config.j2'
    dest: '/path/to/virtualenv/app/src/config.j2'
    owner: 'app-user'
    group: 'app-group'
    mode: '0644'
  notify: [ 'Restart gunicorn' ]

This requires that the debops.gunicorn role is included in the playbook that manages your application. This restriction will be changed in the future, when Ansible handlers will be able to listen for notifications.

DEFAULT VARIABLE DETAILS

Some of debops.gunicorn default variables have more extensive configuration than simple strings or lists, here you can find documentation and examples for them.

  gunicorn__applications

The gunicorn__applications and gunicorn__dependent_applications lists manage the information about the WSGi-compatible applications served by Gunicorn. Each entry is a YAML dictionary with specific parameters. Most of the parameters are passed directly to the configuration file after some processing.

List of known parameters:

name
    Required. Name of the application server to use, it will be used as the configuration file name in the /etc/gunicorn.d/ directory, as well as the process name.

comment
    Optional. Additional comments added to the beginning of the configuration file; can be specified as a string or a YAML text block.

state
    Optional. If not specified or present, the configuration file will be generated. If absent, the configuration file will be removed.

    The rest of the parameters specified in a given entry should be dictionary keys with either a string, a YAML list or a YAML dictionary as values.

working_dir
    Required, string. Path to the working directory of a given application.

python
    Optional, string. Path to the Python executable to use. If not specified, the system Python version will be used.

binary
    Optional, string. Either gunicorn (default) or gunicorn3 to run Python 3 compatible applications.

mode
    Optional, string. What mode to use for the application, usually wsgi or django. This is only relevant on the older OS releases.

user
    Optional, string. UNIX user account which will be used to run the application processes. If not specified, www-data user account will be used. The role will create the user account if it doesn't exist, as long as the home parameter is also specified.

group
    Optional, string. UNIX group which will be used to run the application processes. If not specified, www-data group will be used. The role will create the group if it doesn't exist.

home
    Optional, string. The absolute path of the application account home directory. Required for automatic account creation.

system
    Optional, boolean. If not specified or True, the created UNIX account and group will be a system variant, with UID/GID <1000, which is typical for UNIX services.

environment
    Optional. YAML dictionary with environment variables to set for a given application. Each dictionary key should be the variable name, and dictionary value will be its value.

args
    Required. YAML list of arguments to pass to the gunicorn daemon. The last element of the list should be an application "entry point" module.

    The next set of dictionary keys contains less used parameters, which can be used to modify the internal service configuration.

bind
    Optional. Specify either a UNIX socket path as unix:/path/to/socket, or a TCP socket in the form of ipaddr:port (the role does not configure firewall).

    If not specified, the role will configure an UNIX socket in the path:

    /run/gunicorn-<name>/<name>.sock

    The socket directory will be created with the item.user parameter as the UNIX account owner, and item.group as the UNIX account group.

    On older OS releases, the socket will be created as:

    /run/gunicorn/<name>.sock

bind_mode
    Optional. Specify the file mode of the UNIX socket directory. If not specified, 0755 is used by default

pidfile
    Optional. Specify an absolute path to the file with the PID of the main Green Unicorn process.

    If not specified, the role will create a PID file in the path:

    /run/gunicorn-<name>/<name>.pid

    On older OS releases, the PID file will be created as:

    /run/gunicorn/<name>.pid

backlog
    Optional. Maximum number of clients that are allowed to connect, usually between 64-2048. Clients that exceed this number will receive a connection error.

  Examples

---
# Manage Green Unicorn applications
gunicorn__applications:
  # Deploy a system-wide WSGI application with Python 3
  - name: 'system-app'
    working_dir: '/path/to/deploy/dir'
    binary: 'gunicorn3'
    user: 'worker-user'
    group: 'worker-group'
    args: [ '--bind=0.0.0.0:8000',
            '--workers={{ ansible_processor_vcpus|int + 1 }}',
            '--timeout=10', 'system-app.wsgi' ]
  # Deploy a virtualenv WSGI application (requires gunicorn of the same version
  # to be installed inside the virtualenv)
  - name: 'virtualenv-app'
    comment: 'This application is deployed in a virtualenv'
    working_dir: '/path/to/virtualenv/app/src'
    python: '/path/to/virtualenv/bin/python'
    mode: 'wsgi'
    user: 'custom-user'
    group: 'custom-group'
    args: [ '--bind=unix:/run/gunicorn/virtualenv-app.sock',
            '--workers={{ ansible_processor_vcpus|int + 1 }}',
            '--timeout=10', 'virtualenv-app.wsgi' ]
  # Remove an old application configuration
  - name: 'old-app'
    state: 'absent'

AUTHOR
Maciej Delmanowski

COPYRIGHT
2014-2022, Maciej Delmanowski, Nick Janetakis, Robin Schneider and others