Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Configure NGINX to Serve ASGI from UNIX Socket?

I am unable to connect an application running on ASGI + Gunicorn through a Unix socket on NGINX on localhost via a docker container.

Provided that I am in the docker container and run NGINX:

/usr/sbin/nginx

I can open http://localhost/api/v1/items and get a 404 from NGINX, which means it is at least running.

Exec-ing the running docker service, I can start Gunicorn with the following command:

gunicorn app.main:app --name asgi --workers 3 --user=root --group=root --bind=unix:///tmp/asgi.sock --log-level=debug --log-file=- -k uvicorn.workers.UvicornWorker -c /gunicorn_conf.py

Gunicorn starts correctly, and with another exec, I can curl the UNIX socket that I have bound to as such and receive a 200 response.

curl --unix-socket ///tmp/asgi.sock http://localhost/api/v1/items

I think this means that I have some discrepancy in the configuration of NGINX directing traffic to http://localhost/api/v1/items.

nginx.conf

daemon off;
user  nginx;
worker_processes 1;
pid        /var/run/nginx.pid;
events {
    worker_connections 1024;
}

http {
  access_log /dev/stdout;
  upstream asgi {
    server unix:/tmp/asgi.sock fail_timeout=0;
  }

  server {
    listen   80;
    server_name localhost;
    client_max_body_size 4G;
    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://asgi;
    }

  }
}

gunicorn_conf.py

import json
import multiprocessing
import os

workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
host = os.getenv("HOST", "unix")
port = os.getenv("PORT", "///tmp/asgi.sock")
bind_env = os.getenv("BIND", None)
use_loglevel = os.getenv("LOG_LEVEL", "info")
if bind_env:
    use_bind = bind_env
else:
    use_bind = f"{host}:{port}"

cores = multiprocessing.cpu_count()
workers_per_core = float(workers_per_core_str)
default_web_concurrency = workers_per_core * cores
if web_concurrency_str:
    web_concurrency = int(web_concurrency_str)
    assert web_concurrency > 0
else:
    web_concurrency = max(int(default_web_concurrency), 2)

# Gunicorn config variables
loglevel = use_loglevel
workers = web_concurrency
bind = use_bind
keepalive = 120
errorlog = "-"

# For debugging and testing
log_data = {
    "loglevel": loglevel,
    "workers": workers,
    "bind": bind,
    # Additional, non-gunicorn variables
    "workers_per_core": workers_per_core,
    "host": host,
    "port": port,
}
print(json.dumps(log_data))
like image 553
Blake Lassiter Avatar asked Aug 17 '19 03:08

Blake Lassiter


1 Answers

Resolved my issue and updated original question with changed files. Main issue was using daemon off for supervisord to allow both NGINX and Gunicorn to run, and placing the server config of NGINX inside an html block.


In my original post, my NGINX config did not have the server inside http block, and I updated my logs to write to the console, though it makes little difference assuming the file location exists.

I am also starting NGINX and Gunicorn via Supervisord which was not originally stated, as I felt it was out of scope of the issue. However, I am now able to start both processes with Supervisord, but I had to daemon off; to the NGINX config to get this to work; without it, the process would say port is already in use.

I updated my post with my latest version of my config, including supervisor.ini

supervisor.ini

[supervisord]
nodaemon=true

[program:asgi]
command=gunicorn app.main:app --name asgi --workers 3 --user=root --group=root --bind=unix:/tmp/asgi.sock --log-level=debug --log-file=- -k uvicorn.workers.UvicornWorker -c /gunicorn_conf.py
user = root ; User to run as
autostart=true
autorestart=true
stdout_logfile=/dev/stdout ; Where to write log messages
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8 ; Set UTF-8 as default encoding

[program:nginx]
command=/usr/sbin/nginx -c /etc/nginx/conf.d/nginx.conf
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
# Graceful stop, see http://nginx.org/en/docs/control.html
stopsignal=QUIT

To run supervisord: /usr/bin/supervisord -c /etc/supervisor.d/supervisord.ini

To try it out yourself, see the github repo I made for this application.

like image 57
Blake Lassiter Avatar answered Oct 12 '22 15:10

Blake Lassiter