Cómo configurar uWSGI y Nginx para servir aplicaciones Python en Ubuntu 14.04

Introducción

En esta guía, configuraremos una aplicación WSGI simple servida por uWSGI. Utilizaremos el servidor web Nginx como proxy inverso al servidor de aplicaciones para proporcionar un manejo de conexión más robusto. Estaremos instalando y configurando estos componentes en un servidor Ubuntu 14.04.

Definiciones y Conceptos

Aclarando Algunos Términos

Antes de sumergirnos, deberíamos abordar algunos términos confusos asociados con los conceptos interrelacionados con los que estaremos tratando. Estos tres términos separados que parecen intercambiables, en realidad tienen significados distintos:

  • WSGI: Una especificación de Python que define una interfaz estándar para la comunicación entre una aplicación o marco de trabajo y un servidor de aplicaciones/web. Esto fue creado para simplificar y estandarizar la comunicación entre estos componentes para lograr consistencia e intercambiabilidad. Básicamente, esto define una interfaz de API que puede utilizarse sobre otros protocolos.
  • : Un contenedor de servidor de aplicaciones que tiene como objetivo proporcionar una pila completa para desarrollar e implementar aplicaciones y servicios web. El componente principal es un servidor de aplicaciones que puede manejar aplicaciones de diferentes lenguajes. Se comunica con la aplicación utilizando los métodos definidos por la especificación WSGI, y con otros servidores web a través de una variedad de otros protocolos. Esta es la pieza que traduce las solicitudes de un servidor web convencional a un formato que la aplicación puede procesar.
  • uwsgi: Un protocolo binario rápido implementado por el servidor uWSGI para comunicarse con un servidor web más completo. Este es un protocolo de cableado, no un protocolo de transporte. Es la forma preferida de comunicarse con servidores web que están proxyando solicitudes a uWSGI.

Requisitos de la Aplicación WSGI

La especificación WSGI define la interfaz entre el servidor web y las porciones de aplicación de la pila. En este contexto, “servidor web” se refiere al servidor uWSGI, que es responsable de traducir las solicitudes de los clientes a la aplicación utilizando la especificación WSGI. Esto simplifica la comunicación y crea componentes débilmente acoplados para que pueda intercambiar fácilmente cualquiera de los dos lados sin mucho problema.

El servidor web (uWSGI) debe tener la capacidad de enviar solicitudes a la aplicación al activar un “callable” definido. El callable es simplemente un punto de entrada a la aplicación donde el servidor web puede llamar a una función con algunos parámetros. Los parámetros esperados son un diccionario de variables ambientales y un callable proporcionado por el componente del servidor web (uWSGI).

En respuesta, la aplicación devuelve un iterable que se utilizará para generar el cuerpo de la respuesta del cliente. También llamará al callable del componente del servidor web que recibió como parámetro. El primer parámetro al activar el callable del servidor web será el código de estado HTTP y el segundo será una lista de tuplas, cada una de las cuales define un encabezado de respuesta y un valor para enviar de vuelta al cliente.

Con el componente de “servidor web” de esta interacción proporcionado por uWSGI en este caso, solo necesitaremos asegurarnos de que nuestras aplicaciones tengan las cualidades descritas anteriormente. También configuraremos Nginx para manejar las solicitudes reales de los clientes y proxyarlas al servidor uWSGI.

Instalar los Componentes

Para comenzar, necesitaremos instalar los componentes necesarios en nuestro servidor Ubuntu 14.04. Podemos hacer esto principalmente usando apt y pip.

Primero, actualiza tu índice de paquetes apt y luego instala las bibliotecas y encabezados de desarrollo de Python, el gestor de paquetes Python pip, y el servidor web y proxy inverso Nginx:

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

Una vez completada la instalación del paquete, tendrás acceso al administrador de paquetes pip de Python. Podemos usar esto para instalar el paquete virtualenv, que utilizaremos para aislar el entorno de Python de nuestra aplicación de cualquier otro que pueda existir en el sistema:

sudo pip install virtualenv

Una vez hecho esto, podemos comenzar a crear la estructura general de nuestra aplicación. Crearemos el entorno virtual discutido anteriormente e instalaremos el servidor de aplicaciones uWSGI dentro de este entorno.

Configurar un directorio de aplicaciones y un Virtualenv

Comenzaremos creando una carpeta para nuestra aplicación. Esto puede contener una carpeta anidada que contenga el código de la aplicación real en una aplicación más completa. Para nuestros propósitos, este directorio simplemente contendrá nuestro entorno virtual y nuestro punto de entrada WSGI:

mkdir ~/myapp/

A continuación, muévete al directorio para poder configurar el entorno para nuestra aplicación:

cd ~/myapp

Crea un entorno virtual con el comando virtualenv. Lo llamaremos myappenv por simplicidad:

virtualenv myappenv

A new Python environment will be set up under a directory called myappenv. We can activate this environment by typing:

source myappenv/bin/activate

Tu indicador debería cambiar para indicar que ahora estás operando dentro del entorno virtual. Se verá algo así:

(myappenv)username@host:~/my_app$

Si deseas salir de este entorno en cualquier momento, simplemente puedes escribir:

deactivate

Si has desactivado tu entorno, vuelve a activarlo para continuar con la guía.

Con este entorno activo, cualquier paquete de Python instalado estará contenido dentro de esta jerarquía de directorios. No interferirán con el entorno de Python del sistema. Con esto en mente, ahora podemos instalar el servidor uWSGI en nuestro entorno usando pip. El paquete para esto se llama uwsgi (este sigue siendo el servidor uWSGI y no el protocolo uwsgi):

pip install uwsgi

Puede verificar que ahora está disponible escribiendo:

uwsgi --version

Si devuelve un número de versión, el servidor uWSGI está disponible para su uso.

Crea una Aplicación WSGI

A continuación, crearemos una aplicación WSGI increíblemente simple utilizando los requisitos de especificación WSGI que discutimos anteriormente. Para reiterar, el componente de la aplicación que debemos proporcionar debe tener las siguientes propiedades:

  • Debe proporcionar una interfaz a través de un objeto llamable (una función u otro constructo de lenguaje que pueda ser llamado).

  • El objeto llamable debe tomar como parámetros un diccionario que contiene pares clave-valor similares a variables de entorno y un objeto llamable que es accesible en el servidor (uWSGI).

  • El objeto llamable de la aplicación debe devolver un iterable que producirá el cuerpo para enviar al cliente.

  • La aplicación debe llamar al objeto llamable del servidor web con el estado HTTP y los encabezados de solicitud.

Escribiremos nuestra aplicación en un archivo llamado wsgi.py en nuestro directorio de la aplicación:

nano ~/myapp/wsgi.py

Dentro de este archivo, crearemos la aplicación más simple compatible con WSGI que podamos. Como con todo el código Python, asegúrate de prestar atención a la sangría:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

El código anterior constituye una aplicación WSGI completa. De manera predeterminada, uWSGI buscará una función llamable llamada application, por eso llamamos a nuestra función application. Como puedes ver, toma dos parámetros.

El primero lo llamamos environ porque será un diccionario de tipo clave-valor similar a una variable de entorno. El segundo se llama start_response y es el nombre que la aplicación utilizará internamente para referirse al llamable del servidor web (uWSGI) que se envía. Ambos nombres de parámetros se seleccionaron simplemente debido a su uso en los ejemplos de la especificación PEP 333 que define las interacciones WSGI.

Nuestra aplicación debe tomar esta información y hacer dos cosas. Primero, debe llamar al llamable que recibió con un código de estado HTTP y cualquier encabezado que quiera enviar de vuelta. En este caso, estamos enviando una respuesta “200 OK” y configurando el encabezado Content-Type a text/html.

En segundo lugar, necesita devolver un iterable para usar como el cuerpo de la respuesta. Aquí, simplemente hemos usado una lista que contiene una única cadena de HTML. Las cadenas también son iterables, pero dentro de una lista, uWSGI podrá procesar la cadena completa con una sola iteración.

En un escenario del mundo real, este archivo probablemente se utilizaría como un enlace al resto del código de su aplicación. Por ejemplo, los proyectos de Django incluyen un archivo wsgi.py por defecto que traduce las solicitudes del servidor web (uWSGI) a la aplicación (Django). La interfaz simplificada de WSGI sigue siendo la misma independientemente de lo complejo que sea el código de la aplicación real. Esta es una de las fortalezas de la interfaz.

Guarde y cierre el archivo cuando haya terminado.

Para probar el código, podemos iniciar uWSGI. Le diremos que use HTTP por el momento y que escuche en el puerto 8080. Le pasaremos el nombre del script (con el sufijo eliminado):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

Ahora, si visita la dirección IP o el nombre de dominio de su servidor en su navegador web seguido de :8080, debería ver el texto del encabezado de primer nivel que pasamos como cuerpo en nuestro archivo wsgi.py:

Detenga el servidor con CTRL-C cuando haya verificado que esto funciona.

Hemos terminado de diseñar nuestra aplicación real en este punto. Puede desactivar nuestro entorno virtual si lo desea:

deactivate

Configure un Archivo de Configuración de uWSGI

En el ejemplo anterior, iniciamos manualmente el servidor uWSGI y le pasamos algunos parámetros en la línea de comandos. Podemos evitar esto creando un archivo de configuración. El servidor uWSGI puede leer configuraciones en una variedad de formatos, pero utilizaremos el formato .ini por simplicidad.

Para continuar con el nombramiento que hemos estado utilizando hasta ahora, llamaremos al archivo myapp.ini y lo colocaremos en nuestra carpeta de aplicación:

nano ~/myapp/myapp.ini

Dentro, necesitamos establecer una sección llamada [uwsgi]. Esta sección es donde residirán todos nuestros elementos de configuración. Comenzaremos identificando nuestra aplicación. El servidor uWSGI necesita saber dónde está el callable de la aplicación. Podemos especificar el archivo y la función dentro de él:

[uwsgi]
module = wsgi:application

Queremos marcar el proceso inicial de uwsgi como maestro y luego generar un número de procesos secundarios. Empezaremos con cinco trabajadores:

[uwsgi]
module = wsgi:application

master = true
processes = 5

En realidad, vamos a cambiar el protocolo que uWSGI utiliza para comunicarse con el mundo exterior. Cuando estábamos probando nuestra aplicación, especificamos --protocol=http para poder verla desde un navegador web. Dado que configuraremos Nginx como un proxy inverso frente a uWSGI, podemos cambiar esto. Nginx implementa un mecanismo de proxy uwsgi, que es un protocolo binario rápido que uWSGI puede usar para comunicarse con otros servidores. El protocolo uwsgi es en realidad el protocolo predeterminado de uWSGI, por lo que simplemente al omitir una especificación de protocolo, se revertirá a uwsgi.

Dado que estamos diseñando esta configuración para usar con Nginx, también vamos a cambiar de usar un puerto de red y usar en su lugar un socket Unix. Esto es más seguro y rápido. El socket se creará en el directorio actual si usamos una ruta relativa. Lo llamaremos myapp.sock. Cambiaremos los permisos a “664” para que Nginx pueda escribir en él (estaremos iniciando uWSGI con el grupo www-data que utiliza Nginx). También agregaremos la opción vacuum, que eliminará el socket cuando el proceso se detenga:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

Necesitamos una opción final ya que crearemos un archivo Upstart para iniciar nuestra aplicación al arrancar. Upstart y uWSGI tienen ideas diferentes sobre lo que la señal SIGTERM debería hacer con una aplicación. Para resolver esta discrepancia y que los procesos se puedan manejar como se espera con Upstart, solo necesitamos agregar una opción llamada die-on-term para que uWSGI mate el proceso en lugar de recargarlo:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

Guarde y cierre el archivo cuando haya terminado. Este archivo de configuración ahora está listo para ser usado con un script Upstart.

Cree un archivo Upstart para administrar la aplicación

Podemos lanzar una instancia de uWSGI al arrancar para que nuestra aplicación esté siempre disponible. Colocaremos esto en el directorio /etc/init que verifica Upstart. Lo llamaremos myapp.conf:

sudo nano /etc/init/myapp.conf

Primero, podemos comenzar con una descripción del servicio y elegir los niveles de ejecución del sistema donde debería ejecutarse automáticamente. Los niveles de ejecución de usuario estándar son 2 a través de 5. Le diremos a Upstart que detenga el servicio cuando esté en cualquier nivel de ejecución fuera de este grupo (como cuando el sistema se está reiniciando o está en modo de usuario único):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

A continuación, le diremos a Upstart qué usuario y grupo deben ejecutar el proceso. Queremos ejecutar la aplicación bajo nuestra propia cuenta (estamos utilizando demo en esta guía, pero debería sustituir su propio usuario). Sin embargo, queremos establecer el grupo en el usuario www-data que utiliza Nginx. Esto es necesario porque el servidor web necesita poder leer y escribir en el socket que nuestro archivo .ini creará:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

A continuación, ejecutaremos los comandos reales para iniciar uWSGI. Dado que instalamos uWSGI en un entorno virtual, tenemos un trabajo extra que hacer. Podríamos simplemente proporcionar la ruta completa al ejecutable de uWSGI, pero en cambio, activaremos el entorno virtual. Esto facilitaría las cosas si dependiéramos de software adicional instalado en el entorno.

Para hacer esto, usaremos un bloque de script. Dentro, cambiaremos a nuestro directorio de aplicación, activaremos el entorno virtual (debemos usar . en scripts en lugar de source), y comenzaremos la instancia de uWSGI apuntando a nuestro archivo .ini:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

Con eso, nuestro script de Upstart está completo. Guarde y cierre el archivo cuando haya terminado.

Ahora, podemos iniciar el servicio escribiendo:

sudo start myapp

Podemos verificar que se haya iniciado escribiendo:

ps aux | grep myapp
demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

Esto se iniciará automáticamente al arrancar. Puedes detener el servicio en cualquier momento escribiendo:

sudo stop myapp

Configurar Nginx para actuar como proxy hacia uWSGI

En este punto, tenemos una aplicación WSGI y hemos verificado que uWSGI puede leerla y servirla. Hemos creado un archivo de configuración y un script de Upstart. Nuestro proceso uWSGI escuchará en un socket y comunicará utilizando el protocolo uwsgi.

Ahora estamos en el punto en el que podemos trabajar en configurar Nginx como un proxy inverso. Nginx tiene la capacidad de actuar como proxy utilizando el protocolo uwsgi para comunicarse con uWSGI. Este es un protocolo más rápido que HTTP y tendrá un mejor rendimiento.

La configuración de Nginx que estaremos estableciendo es extremadamente simple. Crea un nuevo archivo dentro del directorio sites-available dentro de la jerarquía de configuración de Nginx. Llamaremos a nuestro archivo myapp para que coincida con el nombre de la aplicación que hemos estado utilizando:

sudo nano /etc/nginx/sites-available/myapp

Dentro de este archivo, podemos especificar el número de puerto y el nombre de dominio al que este bloque de servidor debería responder. En nuestro caso, estaremos utilizando el puerto predeterminado 80:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Dado que deseamos enviar todas las solicitudes en este dominio o dirección IP a nuestra aplicación WSGI, crearemos un único bloque de ubicación para las solicitudes que comiencen con /, que debería coincidir con todo. Dentro, utilizaremos la directiva include para incluir una serie de parámetros con valores predeterminados razonables desde un archivo en nuestro directorio de configuración de Nginx. El archivo que contiene estos se llama uwsgi_params. Después, pasaremos el tráfico a nuestra instancia de uWSGI a través del protocolo uwsgi. Utilizaremos el socket unix que configuramos anteriormente:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

Eso es todo lo que necesitamos para una aplicación simple. Hay algunas mejoras que podrían hacerse para una aplicación más completa. Por ejemplo, podríamos definir una serie de servidores uWSGI ascendentes fuera de este bloque y luego pasarlos a ese. Podríamos incluir algunos parámetros de uWSGI adicionales. También podríamos manejar cualquier archivo estático desde Nginx directamente y pasar solo las solicitudes dinámicas a la instancia de uWSGI.

Sin embargo, no necesitamos ninguna de esas funciones en nuestra aplicación de tres líneas, así que podemos guardar y cerrar el archivo.

Active la configuración del servidor que acabamos de hacer vinculándola al directorio sites-enabled:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Revise el archivo de configuración en busca de errores de sintaxis:

sudo service nginx configtest

Si informa que no se detectaron problemas, reinicie el servidor para implementar los cambios:

sudo service nginx restart

Una vez que Nginx se reinicie, debería poder ir al nombre de dominio o dirección IP de su servidor (sin un número de puerto) y ver la aplicación que configuró:

Conclusión

Si has llegado hasta aquí, has creado una aplicación WSGI simple y tienes una idea de cómo deberían diseñarse aplicaciones más complejas. Hemos instalado el contenedor/servidor de aplicaciones uWSGI en un entorno virtual hecho a medida para servir nuestra aplicación. Hemos creado un archivo de configuración y un script Upstart para automatizar este proceso. Delante del servidor uWSGI, hemos configurado un proxy inverso de Nginx que puede comunicarse con el proceso uWSGI utilizando el protocolo de conexión uwsgi.

Puedes ver fácilmente cómo esto puede expandirse al configurar un entorno de producción real. Por ejemplo, uWSGI tiene la capacidad de gestionar múltiples aplicaciones usando algo llamado “modo emperador”. Puedes ampliar la configuración de Nginx para balancear la carga entre instancias de uWSGI o para manejar archivos estáticos para tu aplicación. Cuando sirvas múltiples aplicaciones, puede ser de tu interés instalar uWSGI globalmente en lugar de en un entorno virtual, dependiendo de tus necesidades. Los componentes son bastante flexibles, por lo que deberías poder ajustar su configuración para adaptarse a muchos escenarios diferentes.

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-ubuntu-14-04