Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(async): add uvicorn, asgi, upgrade django=4.1 #473

Merged
merged 13 commits into from
Nov 27, 2023
3 changes: 1 addition & 2 deletions {{cookiecutter.github_repository}}/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
load_dotenv(os.path.join(os.path.dirname(__file__), ".env"))

# We defer to a DJANGO_SETTINGS_MODULE already in the environment.
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.development'
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.production")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.production")

application = get_asgi_application()
4 changes: 2 additions & 2 deletions {{cookiecutter.github_repository}}/compose/dev/django/start
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ set -o nounset

python /app/manage.py collectstatic --noinput
{%- if cookiecutter.add_asgi.lower() == "y" %}
/usr/local/bin/gunicorn asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn.workers.UvicornWorker
gunicorn asgi --bind 0.0.0.0:8000 --chdir=/app -k uvicorn.workers.UvicornWorker
{%- else %}
/usr/local/bin/gunicorn wsgi --bind 0.0.0.0:5000 --chdir=/app --access-logfile - --error-logfile -
gunicorn wsgi --bind 0.0.0.0:8000 --chdir=/app --access-logfile - --error-logfile -
{%- endif %}
10 changes: 10 additions & 0 deletions {{cookiecutter.github_repository}}/docs/backend/server_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@

Our overall stack looks like this:

{%- if cookiecutter.add_asgi.lower() == 'y' %}
```
the web client <-> the web server (nginx) <-> the socket <-> ASGI <-> Django
```
{%- else %}
```
the web client <-> the web server (nginx) <-> the socket <-> uWSGI <-> Django
```
{%- endif %}

A web server faces the outside world. It can serve files (HTML, images, CSS, etc) directly from the file system. However, it can’t talk directly to Django applications; it needs something that will run the application, feed it requests from web clients (such as browsers) and return responses.

{%- if cookiecutter.add_asgi.lower() == 'y' %}
ASGI (ASGI stands for Asynchronous Server Gateway interface) which runs through Gunicorn running the actual Django instance. ASGI is an interface and sit in between the web server (NGINX) and the Django application. It creates a Unix socket, and serves responses to the web server via the asgi protocol.
{%- else %}
uWSGI is a [WSGI](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) implementation, it creates a Unix socket, and serves responses to the web server via the uwsgi protocol.
{%- endif %}

## Third Party Services

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,23 @@ server {

# Setup named location for Django requests and handle proxy details
location / {
{%- if cookiecutter.add_asgi.lower() == 'y' %}
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://uvicorn;

{%- else %}
uwsgi_pass unix:///tmp/uwsgi-{{ project_namespace }}.sock;
include /etc/nginx/uwsgi_params;

# set correct scheme
uwsgi_param UWSGI_SCHEME $http_x_forwarded_proto;
{%- endif %}
}
{% endraw %}
{%- if cookiecutter.enable_whitenoise.lower() == 'n' %}
Expand All @@ -68,3 +80,14 @@ server {
}{% endraw %}
{%- endif %}
}

{%- if cookiecutter.add_asgi.lower() == 'y' %}
upstream uvicorn {
server unix://{{ asgi_socket }};
}

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
{%- endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ server {

{% if vm and (nginx_cert.stat.exists == false or nginx_key.stat.exists == false) %}
location / {
{%- if cookiecutter.add_asgi.lower() == 'y' %}
proxy_pass unix://{{ asgi_socket }};
{$- else %}
uwsgi_pass unix:///tmp/uwsgi-{{ project_namespace }}.sock;
{%- endif %}
include /etc/nginx/uwsgi_params;

# set correct scheme
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ pg_user: dev
pg_password: password
django_requirements_file: requirements.txt

# asgi related variables
asgi_user: www-data
asgi_group: www-data
asgi_workers: 2
asgi_socket: /tmp/django-{{ domain_name }}-asgi.sock
asgi_user: www-data
asgi_group: www-data
asgi_workers: 2

sun337 marked this conversation as resolved.
Show resolved Hide resolved
# uwsgi related variables
uwsgi_user: www-data
uwsgi_group: www-data
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% raw %}---
- name: apt_get install uwsgi packages
apt: pkg={{ item }} state=present
with_items:
- uuid-dev
- libcap-dev
- libpcre3-dev
tags: ["configure"]

- name: make sure project directory is owned by asgi group
file: path={{ project_path }} state=directory owner={{user}} group={{asgi_group}} recurse=yes
tags: ["configure"]

- name: copy django-asgi logrotate
template: src=django.logrotate.j2
dest=/etc/logrotate.d/asgi-{{ deploy_environment}}-{{project_name}}-django
mode=644
tags: ["configure"]

- name: make sure log directory exists
file: path={{ project_log_dir }} state=directory owner={{asgi_user}} group={{asgi_group}} mode=751 recurse=yes
tags: ["configure"]

- name: copy Django asgi service to systemd
template: src=django.asgi.ini.j2
dest=/etc/systemd/system/asgi-{{ project_namespace }}.service
mode=644
tags: ["deploy"]
{% endraw %}
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,24 @@
become: false
tags: ['deploy']

- import_tasks: uwsgi-setup.yml

- name: Run compilemessages for static translations
django_manage: command=compilemessages app_path={{ project_path }} virtualenv={{ venv_path }}
become: false
tags: ['deploy']

{%- if cookiecutter.add_asgi.lower() == 'y' %}
- import_tasks: asgi-setup.yml

- name: Reload asgi processes
systemd: state=restarted name=asgi-{{ project_namespace }}
{%- else %}
- import_tasks: uwsgi-setup.yml

- name: Reload uwsgi processes
command: uwsgi --reload {{ uwsgi_pid_file }}
become: true
when: not uwsgiconf.changed
{%- endif %}
tags: ['deploy']{% endraw %}
{%- if cookiecutter.add_celery.lower() == 'y' %}
notify: reload celery # reload celery everytime uwsgi conf changes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[Unit]
Description={{ project_namespace }} gunicorn daemon
After=network.target

[Service]
Environment=LC_ALL=en_US.utf-8
Environment=LANG=en_US.utf-8
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=gunicorn
User={{ asgi_user }}
Group={{ asgi_group }}
WorkingDirectory={{ project_path }}
ExecStart={{ venv_path }}/bin/gunicorn -w {{ asgi_workers }} --bind unix://{{ asgi_socket }} --access-logfile {{project_log_dir}}/asgi.log --capture-output --error-logfile {{project_log_dir}}/asgi-errors.log -k uvicorn.workers.UvicornWorker asgi:application

[Install]
WantedBy=multi-user.target
2 changes: 1 addition & 1 deletion {{cookiecutter.github_repository}}/provisioner/vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ pg_gis: {{ 'True' if cookiecutter.add_postgis.lower() == 'y' else 'False' }}
ansible_python_interpreter: /usr/bin/python3
django_settings: settings.production
django_requirements_file: requirements.txt
python_version: 3.9
python_version: 3.11
sun337 marked this conversation as resolved.
Show resolved Hide resolved
postgresql_version: 13
postgis_version: 3