octubre 16, 2013

Configuración de Django en CentOS 6.x con NGINX, GUNICORN, VIRTUALENV y SUPERVISOR

PRE INSTALACION
Instalamos los siguientes paquetes para compilaciones. Es de utilidad en algunos paquetes que instalemos dentro de PIP y los archivos de desarrollo de python.
$ yum install gcc make python-devel
PIP, VIRTUALENV
Instalamos PIP desde los repositorios EPEL
$ yum -y install python-pip --enablerepo epel
Instalamos virtualenvwrapper desde PIP, este instalara como dependencia a virtualenv.
$ pip install virtualenvwrapper
Creamos nuestras variables de sistema para virtualenv y lo alojamos en el archivo de configuración bash del usuario unix.
$ echo 'export WORKON_HOME=/opt/virtualenvs' >> $HOME/.bashrc
$ echo 'export VIRTUALENVWRAPPER_HOOK_DIR=$WORKON_HOME/hooks' >> $HOME/.bashrc
$ echo 'source /usr/bin/virtualenvwrapper.sh' >> $HOME/.bashrc
Creamos la carpeta para HOOKs.
$ mkdir /opt/virtualenvs/hooks
Creamos un entorno virtual de prueba
$ mkvirtualenv projectwebapp
Salimos del entorno virtual
(projectwebapp)$ deactivate
DJANGO

Creamos una carpeta donde alojaremos nuestros proyectos Django y nos cambiamos al directorio.
$ mkdir /home/www/
$ cd /home/www
Habilitamos el entorno virtual para el proyecto
www $ workon projectwebapp
Instalamos Django desde PIP y creamos un proyecto Django
(projectwebapp) www $ pip install django
(projectwebapp) www $ django-admin.py startproject projectwebapp
(projectwebapp) www $ cd projectwebapp/
Iniciamos el servicio http de Django nativo para probar que todo este funcional hasta este punto.
(projectwebapp) projectwebapp $ python manage.py runserver
Validating models...
0 errors found
October 15, 2013 - 13:54:33
Django version 1.5.4, using settings 'projectwebapp.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Detenemos el servicio Django con el atajo de teclado CONTROL-C.

GUNICORN. Python WSGI HTTP Server 
Instalamos gunicorn via PIP dentro del entorno virtual del proyecto. También se instala setproctitle que nos permite renombrar el proceso gunicorn por el nombre del proyecto Django.
(projectwebapp) projectwebapp $ pip install gunicorn setproctitle
Estando en la carpeta del proyecto iniciamos el proyecto con gunicorn usando el modulo WSGI del proyecto Django.
(projectwebapp) projectwebapp $ gunicorn projectwebapp.wsgi
2013-10-15 15:05:17 [13252] [INFO] Starting gunicorn 18.0
2013-10-15 15:05:17 [13252] [INFO] Listening at: http://127.0.0.1:8000 (13252)
2013-10-15 15:05:17 [13252] [INFO] Using worker: sync
2013-10-15 15:05:17 [13257] [INFO] Booting worker with pid: 13257
Detenemos el servicio Gunicorn con el atajo de teclado CONTROL-C.
^C2013-10-15 15:05:33 [13257] [INFO] Worker exiting (pid: 13257)
2013-10-15 15:05:33 [13252] [INFO] Handling signal: int
2013-10-15 15:05:33 [13252] [INFO] Shutting down: Master
Salimos del entorno virtual
(projectwebapp) projectwebapp $ deactive
Creamos el directorio donde alojaremos el script bash que nos iniciara el Gunicorn, debemos tener creado antes el directorio /opt/gunicorn/ para alojar mas proyectos.
$ mkdir /opt/gunicorn/projectwebapp/
Creamos y editamos el archivo script para ejecutar el proyecto con gunicorn
$ vim /opt/gunicorn/projectwebapp/gunicorn_start.sh
#!/bin/bash

DJANGODIR=/home/www/projectwebapp             # Directorio del proyecto Django
SOCKFILE=/var/run/gunicorn/projectwebapp.sock # Unix socket
USER=nginx                                    # Usuario 
GROUP=root                                    # Grupo
NUM_WORKERS=3                                 # Numero de procesos para gunicorn
DJANGO_SETTINGS_MODULE=projectwebapp.settings # Archivo settings del proyecto
DJANGO_WSGI_MODULE=projectwebapp.wsgi         # Modulo WSGI del proyecto

# Activar el entorno virtual
cd $DJANGODIR
source /usr/bin/virtualenvwrapper.sh
workon projectwebapp
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

# Crear el directorio del socket si no existe
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

# Iniciar el proyecto Django
# No se usa el modo --daemon ya que este sera manejado por supervisor
exec gunicorn ${DJANGO_WSGI_MODULE} \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --log-level=debug \
  --bind=unix:$SOCKFILE
Permisos de ejecución
$ chmod u+x /opt/gunicorn/projectwebapp/gunicorn_start.sh
Ejecutamos el script para probar el funcionamiento correcto
$ /opt/gunicorn/projectwebapp/gunicorn_start.sh
SUPERVISOR. Cliente/Servidor de monitoreo y control de procesos 
Instalamos la mas reciente versión de supervisor desde PIP.
$ pip install supervisor
Creamos el archivo de configuración.
$ echo_supervisord_conf > /etc/supervisord.conf
Creamos la carpeta de logs.
$ mkdir /var/log/supervisor
Configuramos los siguientes parámetros.
$ vim /etc/supervisord.conf
# Modifique las siguientes configuraciones
[unix_http_server] 
...
file=/var/run/supervisor.sock
...
[supervisord]
...
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid 
umask=022
user=root
...
Creamos el script para supervisor que ejecutara el servicio como daemon y auto inicio con el sistema. Este es una adaptación propia basado en el script de Mike McGrath
$ vim /etc/init.d/supervisord
#!/bin/bash
#
# supervisord   This scripts turns supervisord on
#
# chkconfig: - 95 04
#
# processname:  supervisord
# config: /etc/supervisord.conf
#

# source function library
. /etc/rc.d/init.d/functions

RETVAL=0
conffile=${CONFFILE-/etc/supervisord.conf}
user=${USER-root}

start() {
        echo -n $"Starting supervisord: "
        runuser -l ${user} -c "supervisord -c ${conffile}" && echo_success || echo_failure
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/supervisord
}

stop() {
 echo -n $"Stopping supervisord: "
 killproc supervisord
 echo
 [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/supervisord
}

restart() {
 stop
 start
}

case "$1" in
  start)
 start
 ;;
  stop) 
 stop
 ;;
  restart|force-reload|reload)
 restart
 ;;
  condrestart)
 [ -f /var/lock/subsys/supervisord ] && restart
 ;;
  status)
 status supervisord
 RETVAL=$?
 ;;
  *)
 echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
 exit 1
esac

exit $RETVAL
Damos de alta nuestro script como servicio.
$ chkconfig --add supervisord
Lo agregamos al inicio de sistema con el runrevel por defecto.
$ chkconfig supervisord on
Iniciamos el servicio.
$ service supervisord start
Configuramos una instancia de programa agregando nuestro script gunicorn que ejecuta nuestra app Django.
$ vim /etc/supervisord.conf
# Agregue este bloque de configuracion segun su proyecto
[program:projectwebapp]
command=/opt/gunicorn/projectwebapp/gunicorn_start.sh
user=root
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/projectwebapp.log
redirect_stderr=True
Reiniciamos supervisor
$ service supervisord restart
NGINX. HTTP Server
Instalamos la repo NGINX oficial.
$ touch /etc/yum.repos.d/nginx.repo
$ vim /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1
Instalamos nginx, los agregamos al inicio de sistema e iniciamos.
$ yum install nginx
$ chkconfig nginx on
$ service nginx start
Creamos las carpetas donde alojamos configuración de los sitios.
$ mkdir /etc/nginx/sites-available
$ mkdir /etc/nginx/sites-enabled
$ mkdir /var/log/nginx/sites
Mas información sobre estas configuración de nginx y buenas practicas en http://desdelocalhost.blogspot.mx/2013/03/instalacion-y-configuracion-de-nginx.html. Creamos un respaldo de la configuración global y aplicamos las configuraciones siguientes para el servicio.
$ cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.old
$ vim /etc/nginx/nginx.conf
user  nginx;
# Numero de CPUs
worker_processes  2;
 
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
 
 
events {
    worker_connections  1024;
}
 
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  /var/log/nginx/access.log  main;
 
    sendfile        on;
    ## Optimiza el rendimiento de sendfile al enviar tramas HTTP
    ## en un solo paquete.
    tcp_nopush     on;
    ## Soporte gzip. Reduce la cantidad de datos a transferir
    gzip  on;
 
    ## Configuracion de buffers
    client_body_buffer_size  1K;
    client_header_buffer_size 1k;
    client_max_body_size 1k;
    large_client_header_buffers 2 1k;
 
    ## Configuracion de timeouts 
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     5 5;
    send_timeout          10;
 
    include /etc/nginx/conf.d/*.conf;
     
    ## Directorio de configuracion para vhost's en linea
    include /etc/nginx/sites-enabled/*.conf;
}
Agregamos la ip 127.0.0.1 para escucha solo local del sitio default, también podemos borrar este archivo si quieren des-habilitar completamente.
$ vim /etc/nginx/conf.d/default.conf 
server {
    listen       127.0.0.1:80;
Creamos y configuramos el sitio para nuestro proyecto projectwebapp de la siguiente manera.
$ vim /etc/nginx/sites-available/projectwebapp.conf
# Configuracion NGINX para projectwebapp
#
upstream projectwebapp {
  server unix:/var/run/gunicorn/projectwebapp.sock fail_timeout=0;
}

server {
    listen       10.0.0.23:80;

    # Configuracion HTTPS
    # listen       10.0.0.23:443 ssl;

    server_name  app.example.com;
    server_tokens off;

    access_log  /var/log/nginx/sites/projectwebapp.access.log;
    error_log  /var/log/nginx/sites/projectwebapp.error.log;

    # Configuracion HTTPS
    #ssl_certificate     /etc/nginx/ssl/server.crt;
    #ssl_certificate_key /etc/nginx/ssl/server.key;
    #ssl_prefer_server_ciphers on;
    #ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    #ssl_session_cache shared:SSL:2m;
    #ssl_ciphers RC4:HIGH:!MD5:!aNULL:!DH:!EDH;

    # HTTP Strict Transport Security (HSTS). Configuracion HTTPS
    #add_header Strict-Transport-Security max-age=31536000;

    # Limite subidas de archivos.
    client_max_body_size 1M;

    # Acceso solo por medio del dominio
    if ( $host !~ ^(app.example.com)$ ) {
         return 444;
    }
    # Metodos http permitidos
    if ($request_method !~ ^(GET|HEAD|POST)$ ) {
         return 444;
    }
    # Directorio para STATIC
    location /static/ {
            alias /home/www/projectwebapp/projectwebapp/static/;
    }
    # Directorio para MEDIA
    location /media/ {
            alias /home/www/projectwebapp/projectwebapp/media/;
    }

    location / {

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Configuracion HTTPS
        #proxy_set_header X-Forwarded-Proto https;

        proxy_set_header Host $http_host;
        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://projectwebapp;
            break;
        }
    }

   # Paginas de error 
    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /home/www/projectwebapp/templates/;
    }
    error_page 404 /404.html;
    location = /404.html {
        root /home/www/projectwebapp/templates/;
    }

}
Habilitamos nuestro archivo de sitio
$ ln -s /etc/nginx/sites-available/projectwebapp.conf /etc/nginx/sites-enabled/projectwebapp.conf
Reiniciamos nginx
$ service nginx restart
IPTABLES. Firewall
No olvidar abrir los respectivos puertos que usara nginx para darle salida a nuestros sitios. 
$ vim /etc/sysconfig/iptables
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
$ service iptables restart

No hay comentarios.:

Publicar un comentario