miércoles, 23 de diciembre de 2015

Configurando Django para entrar en producción

En realidad estoy probando esta configuración para verificar las posibilidades de Django para entrar en producción. Pues que voy a usarlo de esta forma para desarrollo, aunque el servidor que trae para ese caso pueda ser suficiente. En todo caso vamos:
Estoy tomando en consideración:
  • La instalación de Debian Jessie es la mínima necesaria para que el sistema funcione
  • El usuario mafi es un usuario sin privilegios administrativos. Ni siquiera tiene permisos especiales mediante sudo
  • Lo anterior significa que el proceso uwsgi corre con los permisos y limitaciones de un usuario corriente. Que andar corriendo servicios como root es del diablo
Preparación del sistema base:
Como usuario root:
Instalamos paquetes necesarios
aptitude install python-pip
virtualenv nginx python-dev gcc
Creamos el directorio sobre el cual vamos a crear el entorno con virtualenv
mkdir /var/www/yacto
usermod -G -mafi www-data
chown mafi:www-data /var/www/yacto
Como el usuario sin privilegios (mafi en mi caso) Ahora, como el usuario corriente con el que vamos a trabajar
cd /var/www
virtualenv yacto/
cd yacto/
source bin/activate
Notará que hubo un cambio en la shell, y eso es bueno, de la siguiente forma:
mafi@dev:/var/www/yacto$
(yacto)mafi@dev:/var/www/yacto$
Ahora podemos instalar los paquetes en el entorno virtual
pip install Django uwsgi psycopg2
Inicializamos el proyecto
django-admin startproject yacto
Como usuario postgres (Incluso, en otro servidor dedicado para tal fin)
Si es que acaso es tu primera vez, basta con hacer lo siguiente
createuser -DRSP yacto
Ingrese la contraseña para el nuevo rol:
Ingrésela nuevamente:
createdb -O yacto yacto
Como el usuario sin privilegios (mafi en mi caso)
La configuración por defecto para un proyecto con Django se almacena en yacto/settings.py, así que lo configuramos de la siguiente forma:

Para configurar el uso de la base de datos anteriormente creada, modificamos el diccionario DATABASES de la siguiente forma:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'yactividades_2',
        'USER': 'yactividades_2',
        'PASSWORD': 'yactividades_2',
        'HOST': '192.168.2.3',
        'POST': '5432',
        'client_encoding': 'UTF8',
        'default_transaction_isolation': 'read committed',
        'timezone': 'UTC'
    }
}
Lo de 'client_encoding', 'default_transaction_isolation' y 'timezone' lo explican acá: Optimizing PostgreSQL’s configuration

Hacia el final del mencionado fichero, encontramos la sección "Static files". Al final de ella, agregamos la constante STATIC_ROOT
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")

Como consejo adicional, nunca esta de más configurar "Internationalization", sección que esta precisamente arriba de "Static Files"
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/

LANGUAGE_CODE = 'es-SV'

TIME_ZONE = 'America/El_Salvador'

USE_I18N = True

USE_L10N = True

USE_TZ = True
Empezamos a crear los ficheros de configuración:

El directorio referenciado en  para almacenar contenido estático:
mkdir static
aplicacion=$(basename $PWD)
touch $aplicacion.log
Creamos el fichero $aplicacion\_nginx.conf para configuración del sitio web con el siguiente contenido. Las opciones son las básicas funcionales, y basta con cambiar yacto por el nombre de su aplicación, aunque
# $aplicacion\_nginx.conf
# Conexión que Nginx hace con Django
upstream yacto {
    server unix:///var/www/yacto/yacto/yacto.sock;
}

# Configuración del servidor
server {
    # Puerto para servir el sitio
    listen     80;
    # Sustituir por IP o FQDN donde se quiera publicar tal sitio
    server_name yacto.salud.gob.sv;
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;

    # Ficheros estáticos del proyecto, referenciado en STATIC_ROOT del fichero settings.py del proyecto
    location /static {
        alias /var/www/yacto/yacto/static/;
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass yacto;
        #include /var/www/yacto/yacto/uwsgi_params;
        include /etc/nginx/uwsgi_params;
    }

}
Ahora creamos el fichero yacto_uwsgi.ini para configurar varias opciones a tomar por parte de uwsgi, las cuales bien pueden ser configuradas al arrancar la aplicación, pero respecto a configuraciones, siempres será más ordenado un archivo de configuración (Obvio, supongo), que guardar en alguna parte las opciones que un comando ha de usar.
# $aplicacion\_uwsgi.ini file
[uwsgi]

# Django-related settings
# the base directory (full path)
chdir           = /var/www/yacto/yacto
# Django's wsgi file
module          = yacto.wsgi
# the virtualenv (full path)
home            = /var/www/yacto

# process-related settings
# master
master          = true
# maximum number of worker processes
processes       = 10
# the socket (use the full path to be safe
socket          = yacto.sock
# Recordar que en Debian los ficheros se crean 755, lo cual hace necesario que esta opción se use, so riesgo de errores relacionados a permisos
chmod-socket    = 660
# clear environment on exit
vacuum          = true
# El proceso corre como demonio y loguea en yacto.log
# Lo mejor es usarlo sólo en conjunto a emperor, es un poco engorroso de usar de lo contrario
# daemonize     = yacto.log

Teniendo todo esto listo, preparamos a Django para irse en línea:
python manage.py makemigrations
No changes detected
python manage.py migrate
Operations to perform:
  Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying sessions.0001_initial... OK

python manage.py createsuperuser
Username (leave blank to use 'mafi'):
Email address: mafi@yacto.com
Password:
Password (again):
Superuser created successfully.

python manage.py collectstatic

You have requested to collect static files at the destination
location as specified in your settings:

    /var/www/yacto/yacto/static

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: yes

Copying '/var/www/yacto/local/lib/python2.7/site-packages/django/contrib/admin/static/admin/fonts/LICENSE.txt'
Copying '/var/www/yacto/local/lib/python2.7/site-packages/django/contrib/admin/static/admin/fonts/README.txt'
[... Contenido suprimido ...]
Copying '/var/www/yacto/local/lib/python2.7/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js'
Copying '/var/www/yacto/local/lib/python2.7/site-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE-JQUERY.txt'

56 static files copied to '/var/www/yacto/yacto/static'.

Y luego nos volvemos root de nuevo y hacemos lo siguiente para "declarar" el sitio en nginx
ln -s /var/www/yacto/yacto/yacto_nginx.conf /etc/nginx/sites-enabled/
systemctl status nginx.service

Fuentes
Pues que nada, esto es la versión más resumida de Setting up Django and your web server with uWSGI and nginx. La referida guía se detiene en bastante detalles que explican bastante el funcionamiento, incluso esta haciendo pruebas continuamente.

Tiene una ligera influencia de Setup Django on Debian 8

Y obviamente las fuentes oficiales: How to install DjangoManaging static files (e.g. images, JavaScript,CSS)

Otros apuntes interesantes

Otros apuntes interesantes