Nginx Gunicorn Django Socket¶
Servir un sitio Web Django con gunicorn y Nginx es algo que tarde o temprano todos hacemos, aunque solo sea para ver como responde nuestro sitio antes de ponerlo en producción y lidiar con los problemas que nos podemos encontrar cuando de verdad lo subamos a un servidor.
Esta es una configuración básica que funciona muy bien.
En primer lugar, instalamos nginx
sudo dnf install nginx
Primero creamos un entorno virtual con virtualenvwrapper
.
mkvirtualenv practica
El resto del ejemplo, estaremos usando el entorno virtual practica
.
Instalar Django y Gunicorn con pip
.
pip install django
pip install gunicorn
Creamos un directorio para todo el proyecto en ~/webapps/
yo para los ejemplos, uso mi usuario
snicoper
, tu siempre pon tu usuario!.
cd ~/webapps
mkdir example.com
Creamos unos directorios dentro de example.com
cd example.com
mkdir bin run
Y ahora, creamos el proyecto Django, también dentro de example.com
django-admin startproject example
Esto nos crea el típico proyecto Django
.
├── bin
├── example
│ ├── example
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── manage.py
└── run
Crearemos un archivo gunicorn_start.sh
en el directorio bin/
.
vim bin/gunicorn_start.sh
y añadimos las siguientes lineas
#!/bin/bash
NAME="example.com" # Name of the application
DJANGODIR=/home/snicoper/webapps/example.com/example # Django project directory
LOGFILE=/var/log/gunicorn/gunicorn.log
LOGDIR=$(dirname $LOGFILE)
SOCKFILE=/home/snicoper/webapps/example.com/run/gunicorn.sock # we will communicate using this unix socket
USER=snicoper # the user to run as
GROUP=snicoper # the group to run as
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=example.settings # which settings file should Django use
DJANGO_WSGI_MODULE=example.wsgi # WSGI module name
echo "Starting $NAME as `whoami`"
# Activate the virtual environment
source /home/snicoper/.virtualenvs/example.com/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--bind=unix:$SOCKFILE \
--log-level=debug \
--log-file=$LOGFILE 2>>$LOGFILE
Actualizar las rutas y usuario/grupo del script, y le damos permisos de ejecución.
chmod +x bin/gunicorn_start.sh
Crear directorio /var/log/gunicorn
y dentro, el archivo gunicorn.log
sudo mkdir /var/log/gunicorn
sudo chown snicoper:snicoper /var/log/gunicorn
touch /var/log/gunicorn/gunicorn.log
Y por ultimo, configurar un servidor virtual de nginx, para ello, creamos un archivo en
etc/nginx/conf.d/example.com.conf
sudo vim etc/nginx/conf.d/example.com.cof
Y añadimos lo siguiente, una vez mas, asegurase que las rutas son las correctas y el usuario.
upstream example_app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
server unix:/home/snicoper/webapps/example.com/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com;
client_max_body_size 4G;
access_log /var/log/nginx/example.com-access.log;
error_log /var/log/nginx/example.com-error.log;
location /static/ {
alias /home/snicoper/webapps/example.com/static/;
}
location /media/ {
alias /home/snicoper/webapps/example.com/media/;
}
location / {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS, this helps Rack
# set the proper protocol for doing redirects:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll stuff. It's also safe to set if you're
# using only serving fast clients with Unicorn + nginx.
# Otherwise you _want_ nginx to buffer responses to slow
# clients, really.
# proxy_buffering off;
# Try to serve static files from nginx, no point in making an
# *application* server like Unicorn/Rainbows! serve static files.
if (!-f $request_filename) {
proxy_pass http://example_app_server;
break;
}
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/snicoper/webapps/example.com/templates/;
}
}
Reiniciamos el servidor nginx con
sudo systemctl start nginx.service
# Si lo queremos como servicio.
sudo systemctl enable nginx.service
Abrir el puesto 80
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --reload
Para la practica, el puerto no es necesario, pero hay queda :), y ahora le en /etc/hosts
le
decimos que example.com
apunte a 127.0.0.1
sudo vim /etc/hosts
# Añadimos
127.0.0.1 example.com
Primero lo probamos manualmente
cd ~/webapps/example.com/bin
./gunicorn_start.sh
Entramos a [http://example.com](http://example.com)
Lo mas seguro que SELinux se queje con algo así SELinux is preventing nginx from write access on the sock_file gunicorn.sock..
Yo, para solucionar eso hice lo siguiente:
cd ~/webapps/example.com/run/
sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx
sudo semodule -i nginx.pp
Ya con eso, me funciono bien. Ahora, ya solo nos falta poner que gunicorn_start.sh
se inicie al
reiniciar la maquina y poderlo reiniciar de una manera rápida.
Casi todo el mundo lo hace con supervisor
, pero yo probé creando un servicio systemd y me funciona
muy bien, así que es como lo voy a poner.
sudo vim /etc/systemd/system/gunicorn.service
Y añadimos los siguiente
[Unit]
Description=gunicorn daemon
After=syslog.target
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
User=snicoper
Group=snicoper
WorkingDirectory=/home/snicoper/webapps/example.com/bin/
ExecStart=/bin/bash gunicorn_start.sh
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Para reiniciar, etc, se usa los típicos comandos de systemd
sudo systemctl start gunicorn.service
sudo systemctl stop gunicorn.service
sudo systemctl restart gunicorn.service
sudo systemctl enable gunicorn.service
Esta configuración ha sido por socket entre gunicorn y nginx, pero también es posible hacerlo por IP, tengo unos [apuntes sobre el tema](http://apuntes-snicoper.readthedocs.org/es/latest/linux/nginx/nginx_gunicorn_django.html).
—
Fuentes