Implementando Django con gunicorn y nginx

81

Esta es una pregunta amplia, pero me gustaría obtener una respuesta canónica. He estado intentando implementar un sitio usando gunicorn y nginx en Django . Después de leer toneladas de tutoriales, he tenido éxito, pero no puedo estar seguro de que los pasos que seguí sean lo suficientemente buenos para ejecutar un sitio sin problemas o tal vez haya mejores formas de hacerlo. Esa incertidumbre es molesta.

Es por eso que estoy buscando una respuesta muy detallada y bien explicada para los novatos. No quiero explicar demasiado lo que sé y lo que no sé, ya que esto podría sesgar un poco las respuestas y otras personas podrían beneficiarse en menor grado de sus respuestas. Sin embargo, algunas cosas que me gustaría que se mencionen son:

  • ¿Qué "configuración" ha visto que funciona mejor? Solía virtualenv y moví mi Django proyecto dentro de este entorno, sin embargo he visto otros ajustes en los que hay una carpeta para entornos virtuales y otra para los proyectos.

  • ¿Cómo puedo configurar las cosas de una manera que permita alojar varios sitios en un solo servidor?

  • ¿Por qué algunas personas sugieren usar gunicorn_django -b 0.0.0.0:8000y otras sugieren gunicorn_django -b 127.0.0.1:8000? Probé este último en una instancia de Amazon EC2 pero no funcionó mientras que el primero funcionó sin problemas.

  • ¿Cuál es la lógica detrás del archivo de configuración de nginx? Hay tantos tutoriales que usan archivos de configuración drásticamente diferentes que estoy confundido sobre cuál es mejor. Por ejemplo, algunas personas usan alias /path/to/static/foldery otras root /path/to/static/folder. Tal vez puedas compartir tu archivo de configuración preferido.

  • ¿Por qué creamos un enlace simbólico entre site-availabley sites-enableden /etc/nginx?

  • Algunas mejores prácticas son, como siempre, bienvenidas :-)

Gracias

Robert Smith
fuente
¿Puedes publicar un ejemplo en git sobre estos nginx y gunicorn / uwsgi? Será más útil para nuevos estudiantes como yo.
Shiva
@Shiva En realidad, la respuesta de miki725 contiene una muestra muy completa de un archivo de configuración. Si desea una introducción muy completa sobre lo que está sucediendo con nginx, le recomiendo <a href=" amazon.com/Nginx-HTTP-Server-Cl%C3%A9ment-Nedelcu/dp/… book</a>. la integración gunicorn es muy simple se destaca <a href=" docs.djangoproject.com/en/dev/howto/deployment/wsgi/gunicorn/...>.
Robert Smith

Respuestas:

106

¿Qué "configuración" ha visto que funciona mejor? Usé virtualenv y moví mi proyecto django dentro de este entorno, sin embargo, he visto otras configuraciones donde hay una carpeta para entornos virtuales y otra para proyectos.

virtualenv es una forma de aislar entornos Python; como tal, no tiene un papel importante que desempeñar en la implementación ; sin embargo, durante el desarrollo y las pruebas , es un requisito, si no muy recomendable.

El valor que obtendría de virtualenv es que le permite asegurarse de que las versiones correctas de las bibliotecas estén instaladas para la aplicación. Por lo tanto, no importa dónde coloque el entorno virtual en sí. Solo asegúrese de no incluirlo como parte del sistema de control de versiones del código fuente.

El diseño del sistema de archivos no es crítico. Verá muchos artículos que ensalzan las virtudes de los diseños de directorios e incluso proyectos esqueléticos que puede clonar como punto de partida. Siento que esto es más una preferencia personal que un requisito estricto. Seguro que es bueno tenerlo; pero a menos que sepa por qué , no agrega ningún valor a su proceso de implementación, así que no lo haga porque algún blog lo recomienda a menos que tenga sentido para su escenario. Por ejemplo, no es necesario crear un setup.pyarchivo si no tiene un servidor PyPi privado que sea parte de su flujo de trabajo de implementación.

¿Cómo puedo configurar las cosas de una manera que permita alojar varios sitios en un solo servidor?

Hay dos cosas que necesita para realizar configuraciones de varios sitios:

  1. Un servidor que está escuchando en la IP pública en el puerto 80 y / o el puerto 443 si tiene SSL.
  2. Un montón de "procesos" que ejecutan el código fuente real de django.

La gente usa nginx para el n. ° 1 porque es un proxy muy rápido y no viene con la sobrecarga de un servidor completo como Apache. Puede utilizar Apache si se siente cómodo con él. No hay ningún requisito de que "para varios sitios, utilice nginx"; solo necesita un servicio que esté escuchando en ese puerto, sepa cómo redirigir (proxy) a sus procesos que ejecutan el código django real.

Para el n. ° 2, hay algunas formas de iniciar estos procesos. gevent / uwsgi son los más populares. Lo único que debe recordar aquí es no usar runserver en producción .

Esos son los requisitos mínimos absolutos. Normalmente la gente agrega algún tipo de administrador de procesos para controlar todos los "servidores django" (# 2) en ejecución. Aquí verás upstarty supervisormencionaste. Prefiero supervisor, ya que no necesita hacerse cargo de todo el sistema (a diferencia de los advenedizos). Sin embargo, nuevamente, este no es un requisito estricto . Puede ejecutar perfectamente un montón de screensesiones y separarlas. La desventaja es que, si su servidor se reinicia, tendría que reiniciar las sesiones de pantalla.

Personalmente recomendaría:

  1. Nginx para el n. ° 1
  2. Elija entre uwsgi y gunicorn, yo uso uwsgi.
  3. supervisor para la gestión de los procesos de backend.
  4. Cuentas de sistema individuales (usuarios) para cada aplicación que aloja.

La razón por la que recomiendo el # 4 es para aislar los permisos; de nuevo, no es un requisito.

¿Por qué algunas personas sugieren usar gunicorn_django -b 0.0.0.0:8000 y otras sugieren gunicorn_django -b 127.0.0.1:8000? Probé este último en una instancia de Amazon EC2 pero no funcionó mientras que el primero funcionó sin problemas.

0.0.0.0significa "todas las direcciones IP" - es una meta dirección (es decir, una dirección de marcador de posición). 127.0.0.1es una dirección reservada que siempre apunta a la máquina local. Por eso se llama "localhost". Solo es accesible para procesos que se ejecutan en el mismo sistema.

Normalmente, el servidor de aplicaciones para el usuario (el número 1 de la lista anterior) escucha la dirección IP pública. Usted debe asociarla explícitamente el servidor a una dirección IP .

Sin embargo, si por alguna razón está en DHCP o no sabe cuál será la dirección IP (por ejemplo, es un sistema recién aprovisionado), puede decirle a nginx / apache / cualquier otro proceso al que unirse 0.0.0.0. Esta debería ser una medida provisional temporal .

Para los servidores de producción, tendrá una IP estática. Si tiene una IP dinámica (DHCP), puede dejarla 0.0.0.0. Sin embargo, es muy raro que tenga DHCP para sus máquinas de producción.

Encuadernación gunicorn / uwsgi a esta dirección se no se recomienda en la producción. Si vincula su proceso de backend (gunicorn / uwsgi) a 0.0.0.0, puede ser accesible "directamente", sin pasar por su proxy de front-end (nginx / apache / etc); alguien podría simplemente solicitar http://your.public.ip.address:9000/y acceder a su aplicación directamente, especialmente si su servidor front-end (nginx) y su proceso back-end (django / uwsgi / gevent) se están ejecutando en la misma máquina .

Sin embargo, es libre de hacerlo si no quiere tener la molestia de ejecutar un servidor proxy de front-end.

¿Cuál es la lógica detrás del archivo de configuración de nginx? Hay tantos tutoriales que usan archivos de configuración drásticamente diferentes que estoy confundido sobre cuál es mejor. Por ejemplo, algunas personas usan "alias / ruta / a / carpeta / estática" y otras "raíz / ruta / a / carpeta / estática". Tal vez puedas compartir tu archivo de configuración preferido.

Lo primero que debe saber sobre nginx es que no es un servidor web como Apache o IIS. Es un proxy. Entonces verá diferentes términos como 'upstream' / 'downstream' y múltiples "servidores" que se definen. Tómate un tiempo y revisa primero el manual de nginx.

Hay muchas formas diferentes de configurar nginx; pero aquí es una respuesta a su pregunta en aliasfrente root. rootes una directiva explícita que vincula la raíz del documento (el "directorio de inicio") de nginx. Este es el directorio que verá cuando realice una solicitud sin una ruta comohttp://www.example.com/

aliassignifica "asignar un nombre a un directorio". Los directorios con alias pueden no ser un subdirectorio de la raíz del documento.

¿Por qué creamos un enlace simbólico entre los sitios disponibles y los sitios habilitados en / etc / nginx?

Esto es algo exclusivo de debian (y sistemas similares a debian como ubuntu). sites-availableenumera los archivos de configuración para todos los hosts / sitios virtuales del sistema. Un enlace simbólico de sites-enableda sites-available"activa" ese sitio o host virtual. Es una forma de separar archivos de configuración y habilitar / deshabilitar hosts fácilmente.

Burhan Khalid
fuente
1
¡Gran respuesta! Muchas preguntas aclaradas. ¿Puede desarrollar un poco más (o agregar un ejemplo) sobre lo que quiere decir con vincular explícitamente el servidor a una dirección IP y que la vinculación de gunicorn / uwsgi debería estar vinculada a 0.0.0.0? Desafortunadamente, creo que eso es lo que estaba haciendo. ¡Gracias!
Robert Smith
7
Una computadora típica tendrá como mínimo dos direcciones IP: 127.0.0.1y la que le asigne la red; este es el mínimo: su máquina puede tener múltiples interfaces y múltiples direcciones IP. Debe configurar su servidor web (o cualquier proceso, en realidad); escuchar en una dirección IP: esto es lo que quiero decir con ser explícito. Cuando se vincula a 0.0.0.0, le está diciendo al programa que escuche todas las direcciones IP, incluidas las nuevas que puedan estar asignadas a su máquina . Esta no es una buena práctica por varias razones (la seguridad es una de ellas).
Burhan Khalid
Entendido. Ya configuré Gunicorn correctamente. ¡Muchas gracias!
Robert Smith
nginx puede ofrecer contenido estático.
Marcin
cómo sabría el servidor en qué archivo configuramos la dirección del servidor/etc/nginx/sites-enabled
Shiva
11

No soy un gurú de la implementación, pero compartiré algunas de mis prácticas para implementar Django con gevent (aunque debería ser similar a gunicorn).

virtualenves genial por razones en las que no entraré. Sin embargo, encontré virtualenv-wrapper( docs ) muy útil, especialmente cuando estás trabajando en muchos proyectos, ya que permite cambiar fácilmente entre los diferentes virtualenvs. Esto realmente no se aplica al entorno de implementación; sin embargo, cuando necesito solucionar problemas en el servidor mediante SSH, me parece muy útil. Otra ventaja de usarlo es que administra el directorio virtualenv, por lo que tiene menos trabajo manual. Los Virtualenvs están diseñados para ser desechables, de modo que en caso de que tenga problemas con la versión o cualquier otro problema de instalación, puede simplemente volcar el env y crear uno nuevo. Como resultado, es una buena práctica no incluir el código de su proyecto dentro de virtualenv. Debe mantenerse separado.

En cuanto a la configuración de varios sitios, virtualenves prácticamente la respuesta. Debería tener un virutalenv separado para cada proyecto. Solo eso puede resolver muchos problemas. Luego, cuando realice la implementación, un proceso de Python diferente ejecutará diferentes sitios, lo que evitará cualquier posible conflicto entre las implementaciones. Una herramienta que encontré particularmente útil para administrar varios sitios en el mismo servidor es supervisor( docs). Proporciona una interfaz sencilla para iniciar, detener y reiniciar diferentes instancias de Django. También es capaz de reiniciar automáticamente un proceso cuando falla o cuando se inicia la computadora. Entonces, por ejemplo, si se genera alguna excepción y nada la detecta, todo el sitio web puede fallar. El supervisor lo captará y reiniciará la instancia de Django automáticamente. El siguiente es un ejemplo de configuración del programa supervisor (un solo proceso):

[program:foo]
command=/path/toviertualenv/bin/python deploy.py
directory=/path/where/deploy.py/is/located/
autostart=true
autorestart=true
redirect_stderr=True
user=www

Para Nginx, sé que puede ser abrumador al principio. Encontré el libro de Nginx muy útil. Explica todas las principales directivas de nginx.

En mi instalación de nginx, encontré que la mejor práctica es configurar solo las configuraciones principales en el nginx.confarchivo y luego tengo una carpeta separada sitesdonde guardo las configuraciones de nginx para cada uno de los sitios que alojo. Luego solo incluyo todos los archivos de esa carpeta en el archivo de configuración principal. Yo uso la directiva include sites/+*.conf;. De esta forma solo incluye los archivos que comienzan con el +símbolo dentro de la sitescarpeta. De esa manera, solo con el nombre del archivo, puedo controlar qué archivos de configuración se cargan. Entonces, si deseo deshabilitar un sitio determinado, solo tengo que cambiar el nombre del archivo de configuración y reiniciar nginx. No estoy seguro de lo que quiso decir con "enlace simbólico entre los sitios disponibles y los sitios habilitados en / etc / nginx" en su pregunta, ya que son carpetas con nombre Apache pero realizan una tarea similar a la includedirectiva.

Como para rootalias directivas y , son prácticamente las mismas excepto donde se calcula su raíz. In alias, lo que sea en el locationin cayó, mientras que en la raíz no. Imagen que tiene la siguiente configuración de nginx:

location /static {
    alias /some/path/;
}
location /static2 {
    root /some/other/path/;
}

Si el usuario accede a estas URL, nginx intentará buscar los archivos en los siguientes lugares del sistema:

/static/hello/world.pdf => /some/path/hello/world.pdf
/static2/hello/world.pdf => /some/other/path/static2/hello/world.pdf

Esta es una configuración simple para el sitio nginx:

server {
    server_name .foodomain.com;
    listen 80;

    access_log logs/foodomain.log;

    gzip                on;
    gzip_http_version   1.0;
    gzip_comp_level     2;
    gzip_proxied        any;
    gzip_min_length     1100;
    gzip_buffers        16 8k;
    gzip_types          text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    # Some version of IE 6 don't handle compression well on some mime-types, so just disable for them
    gzip_disable "MSIE [1-6].(?!.*SV1)";

    # Set a vary header so downstream proxies don't send cached gzipped content to IE6
    gzip_vary on;

    location / {
        proxy_read_timeout      30s;
        proxy_pass              http://localhost:8000;
        proxy_set_header        Host                 $host;
        proxy_set_header        User-Agent           $http_user_agent;
        proxy_set_header        X-Real-IP            $remote_addr;
    }

    location /media {
        alias   /path/to/media/;
        expires 1y;
    }

    location /static {
        autoindex on;
        expires   1y;
        alias     /path/to/static/;
    }

     location /favicon.ico {
        alias /path/to/favicon.ico;
    }
}

Espero que esto te ayude un poco.

miki725
fuente
De hecho, ¡tu respuesta ayuda mucho! Supervisor suena genial y esa es una de las pocas cosas en las que parece haber consenso entre los blogueros. Grandes consejos sobre entornos virtuales y su envoltorio. Estuve tentado de agregar virtualenv-wrapper a la mezcla, pero no quería aumentar la complejidad en esta pregunta innecesariamente. En cuanto a sitios disponibles y habilitados para sitios, nginx contiene esos directorios. ¿Dónde crea su archivo de configuración para nginx? ¿Dentro de tu proyecto Django?
Robert Smith
Personalmente los tengo en la carpeta de configuración de nginx. En mi caso lo es /usr/local/nginx/config/sites. Sin embargo, no estoy seguro de si es el método correcto o mejor. La razón por la que los mantengo allí es porque si lo muevo, de alguna manera tengo que incluirlo en nginx, ya sea mediante la inclusión manual de la includedirectiva o haciendo enlaces simbólicos. En cualquier caso, es trabajo manual, así que lo mantengo en el lugar de configuración principal.
miki725
Estoy leyendo el libro que recomendó :-) Es genial y, como recordará, /sites/*.conf es una forma sugerida de hacerlo. De todos modos, gracias por tu respuesta.
Robert Smith
De nada. Una sección sobre el libro que pensé que no fue muy útil es cómo usar Django con nginx. Book recomienda usar fastcgi, que no es tan sencillo como usar proxy pass. Así que puedes saltarte el Capítulo 6.
miki725
Acabo de terminar de leer el libro. Es genial. De hecho, leí el capítulo 6 porque quería saber que hoy fastcgi funciona, pero tienes razón ... no fue muy útil. ¡Gracias!
Robert Smith
2

Bueno, en lo que respecta a las mejores prácticas que ha formulado en su pregunta, no puedo evitar compartir una herramienta que funcionó de maravilla para mí, literalmente. ¡Yo mismo solía confundirme en varios archivos de configuración de gunicorn, nginx, supervisorD para varios sitios! Pero ansiaba automatizar de alguna manera todo el proceso para poder realizar cambios en mi aplicación / sitio e implementarlo al instante. Su nombre es django-fagungis. Puede encontrar detalles de mi experiencia con la automatización de implementación de Django aquí . Acabo de configurar un fabfile.py UNA VEZ (django-fagungis usa fabric para automatizar todo el proceso y hace un virtualenv en su servidor remoto que es MUY útilpara administrar las dependencias de varios sitios alojados en un solo servidor. Utiliza nginx, gunicorn y supervisorD para manejar la implementación del proyecto / sitio de Django) y django-fagungis clona mi último proyecto de bitbucket (que utilizo para subversion) y lo implementa en mi servidor remoto y solo tengo que ingresar tres comandos en el shell de mi máquina local y eso !! Para mí, esto ha resultado ser la mejor práctica y sin problemas para la implementación de Django.

Ali Raza Bhayani
fuente
¡Gracias!. Le echaré un vistazo.
Robert Smith