• Servidor Django en una Raspberry PI

    Basta ya de buscar.

    Cuando intenté montar mi propio servidor low cost de Django en mi Raspberry Pi me harté de buscar y buscar y buscar tutoriales por internet, que si nginx por aquí, que si WSGI por allá. Ahora veía unos coletazos de apache por otra parte, pero nada que funcionase y, por supuesto, todo en inglés (aunque se supone que eso no debería ser un problema). Tras buscar y rebuscar, recopilar y combinar, he conseguido hacer un tutorial conciso, rápido y sencillo.

    Vamos por partes para no perdernos, los puntos que vamos a tratar son los siguientes:

    • Herramientas necesarias
    • Un poco de historia
    • Contexto inicial
    • ¿Qué es un servidor web?
    • ¿Qué es Django?
    • Montaje del servidor

    Conciso, hemos dicho conciso.Vamos a ello.

    Herramientas necesarias

    Lo que necesitamos para montar nuestro servidor es:

    1. Una Raspberry Pi (la que yo estoy usando es el modelo B+) con
      build-essentials, python-dev y python-pip
    2. Una máquina para comprobar que el servidor funciona

    Montaje del servidor

    Vamos allá. Lo primero es conectarnos a la raspberry, ya sea mediante SSH o conectándole diréctamente una pantalla y un teclado.

    Por cada herramienta que se use se pondrá un link a su página, ya que no es objeto de esta guía explicar cada una de las herramientas, solo las más cruciales.

    Lo primero de todo es instalar virtualenv, ésta herramienta nos permitirá crear entornos de Python aislados, cada uno con sus herramientas instaladas de forma totalmente independiente. Para hacer ésto ejecutamos el siguiente comando:

    sudo pip install virtualenv

    Una vez tenemos ésto instalaremos una herramienta que más que necesaria, es útil. Esta herramienta es virtualenvwrapper, la cual nos ofrece una serie de comandos para poder manejar los entornos de desarrollo creados por virtualenv de manera mucho más sencilla. Para instalar esta herramienta:

    sudo pip install virtualenvwrapper

    Ahora vamos a meter las manos en la masa, vamos a crear el entorno de python que vamos a usar para desplegar nuestra aplicación. El nombre que le vamos a poner nosotros es django:

    mkvirtualenv django

    Una vez creado, éste debe estar activado automáticamente. En caso de que no se active, debemos ejecutar el siguiente comando:

    workon django

    Donde django es el nombre que le hayamos puesto al entorno. Para saber si un entorno de python está activado, debe aparecer su nombre entre paréntesis antes del nombre de usuario conectado en la terminal, como se ve en la imagen siguiente:

    entorno virtual activo

    Entorno virtual activo

     

     

     

    Ahora vamos a crear una carpeta en la que tendremos nuestro proyecto, asumimos que nos encontramos en el directorio ~ (/home/nombre de usuario/). El directorio que vamos a crear nosotros se llamará web. Una vez creado, nos colocaremos dentro de él. Lo haremos de la siguiente manera:

    mkdir web
    cd web/

    Una vez aquí, instalaremos las herramientas necesarias para nuestra aplicación de django. Ya que nosotros vamos a hacer una de ejemplo, sólo necesitamos django y docutils. Para instalar las dos herramientas, lo haremos mediante el clásico pip:

    pip install django docutils

    Una vez haya terminado de instalar satisfactoriamente django (en mi caso estoy usando la versión 1.4.7) y docutils, crearemos un proyecto de django. Nuestro proyecto de prueba se llamará cocopi:

    django-admin.py startproject cocopi

    Cuando se haya creado la aplicación haremos una primera prueba. Nos colocaremos en la carpeta de la aplicación (cocopi) y ejecutaremos el siguiente comando:

    python manage.py runserver

    Si todo ha ido bien hasta ahora nos debe salir el típico mensaje de servidor de Django iniciado:

    servidor iniciado

    Servidor iniciado

     

     

     

     

    Ahora, desde otra máquina, intentamos acceder al servidor de la raspberry poniendo su dirección IP y el puerto :8000. Si todo está bien hasta ahora, el navegador no debe poder conectar al servidor, ya que se está iniciando en la IP 127.0.0.1 de la raspberry. Lo que debemos hacer es indicar a Django explícitamente que inicie el servidor en la IP de la raspberry (que debe ser fija. Para ver cómo poner una IP fija a un dispositivo en tu red tendrás que consultar el manual de tu router).  Es necesario que tengas abierto el puerto 80 en tu router y apuntando a la IP de la raspberry. Para saber cuál es la IP de la raspberry se debe usar el comando ifconfig, el cual dará un resultado parecido a este:

    resultado ifconfig

    Resultado del comando ifconfig

     

     

     

     

     

     

    Lo que nos interesa es el parámetro inet addr de la interfaz eth0 (porque la raspberry está conectada por cable al router, si estuviera conectada mediante un adaptador Wi-Fi probablemente sería wlan0). Una vez que tenemos la IP, ejecutaremos el siguiente comando:

    python manage.py runserver 192.168.1.179:8000

    Donde la IP la deberás sustituir por la que tenga tu raspberry. Si ahora pruebas a acceder desde el navegador de la otra máquina la dirección IP de la raspberry con el puerto 8000, deberá funcionar.

    Ya estamos más cerca de nuestro objetivo. El siguiente paso será cambiar el servidor de ejecución de la aplicación de Django. El que usaremos será Gunicorn junto con la herramienta South. Para instalarlas, volveremos a usar pip:

    pip install south gunicorn

    Cuando se hayan instalado ambas herramientas, habremos de incluirlas como aplicaciones instaladas de nuestra aplicación de Django. Por lo que editaremos el archivo settings.py mediante el comando:

    nano cocopi/settings.py

    Este comando sólo funcionará si estás respetando los directorios que hemos ido siguiendo y estás dentro de la carpeta de la aplicación de Django. Una vez abierto ese archivo, debemos buscar la sección INSTALLED_APPS y añadir south y gunicorn, deberá quedar algo como en la siguiente imagen:

    aplicaciones instaladas

    Aplicaciones intaladas

     

     

     

     

     

     

    Es importante que el último elemento de la lista no tenga una coma al final.

    Una vez hayamos añadido esas aplicaciones a la lista de instaladas, debemos pegar este trozo de código al final del archivo settings.py:

    LOGGING = {
    	'version': 1,
    	'disable_existing_loggers': False,
    	'filters': {
    		'require_debug_false': {
    			'()': 'django.utils.log.RequireDebugFalse'
    		}
    	},
    	'handlers': {
    		'mail_admins': {
    			'level': 'ERROR',
    			'filters': ['require_debug_false'],
    			'class': 'django.utils.log.AdminEmailHandler'
    		}
    	},
    	'loggers': {
    		'django.request': {
    			'handlers': ['mail_admins'],
    			'level': 'ERROR',
    			'propagate': True,
    		},
    	}
    }

    Pulsamos Ctrl+X y luego la tecla Y para guardar los cambios en el fichero. El siguiente paso será sincronizar la base de datos mediante el comando:

    python manage.py syncdb

    Si durante el proceso pide crear un super-usuario, decir que sí. En nuestro caso, los datos de éste serán:

    • Usuario: cocodigo
    • Contraseña: cocodigo
    • Email: cocodigo@cocodigo.com

    Cuando haya terminado de sincronizarse la base de datos, haremos la segunda prueba. Volveremos a ejecutar la aplicación en la IP de la raspberry, pero ésta vez desde el servidor de Gunicorn, con el comando:

    python manage.py run_gunicorn --bind=192.168.1.179:8000

    Donde la IP es la de la raspberry, la cual la obtuvimos mediante el comando ifconfig. Si intentamos conectar desde otra máquina a la IP de la raspberry con el puerto 8000 deberá funcionar tal y como lo hacía antes, pero lo cierto es que ahora se estará accediendo al servidor de Gunicorn y no al por defecto de Django.

    Ya casi lo tenemos. Lo siguiente será instalar una herramienta que nos permitirá indicar programas a iniciar cuando se inicie el sistema (por si alguna vez se desconecta la raspberry de forma accidental, no tener si quiera que conectarnos a ella para que todo siga funcionando como si nada). La herramienta que usaremos será supervisor. Para instalarla:

    sudo apt-get install supervisor

    Una vez instalada la herramienta vamos a decirle qué programa iniciar. Para ello crearemos un archivo de configuración de supervisor mediante el siguiente comando:

    sudo nano /etc/supervisor/conf.d/gunicorn.conf

    Y en el archivo que se nos abre, debemos poner:

    [program:gunicorn]
    command = /home/pi/.virtualenvs/django/bin/python /home/pi/.virtualenvs/django/bin/gunicorn cocopi.wsgi:application
    directory = /home/pi/web/cocopi
    user = pi
    autostart = true
    autorestart = true
    stdout_logfile = /var/log/supervisor/gunicorn.log
    stderr_logfile = /var/log/supervisor/gunicorn_err.log

    La primera línea indica que lo que vamos a iniciar es un programa y que se llama gunicorn. command especifica el comando a ejecutar. Como estamos trabajando en espacios virtuales aislados debemos indicar la ruta completa hasta el archivo que ejecutará el comando. En rojo se marca el nombre del espacio virtual (virtualenv) que deberá ser sustituido por el nombre que le hayas dado al tuyo, si no es el mismo que el del tutorial. En naranja se encuentra el nombre de la aplicación de django creada. Si la tuya no se llama así, cambia eso por el nombre que le hayas puesto. directory indica la carpeta desde la que se ejecutará el comando. Esta ruta deberá apuntar a la carpeta de tu proyecto (la misma en la que se encuentre el archivo manage.py). Las siguientes 3 líneas indican el usuario con el que se ejecutará el comando, si el programa debe arrancar automáticamente y si debe reiniciarse automáticamente en caso de fallo, respectivamente. Por último, stdout_logfile stderr_logfile indican la ruta a los ficheros de logs de salida y de errores, respectivamente.

    Una vez guardado el fichero, para hacerlo funcionar debemos ejecutar:
    sudo supervisorctl reread
    Este comand debe mostrar como salida:
    gunicorn:available

    Luego ejecutaremos:
    sudo supervisorctl update
    Y debe mostrar:
    gunicorn: added process group

    Y por último:
    sudo supervisorctl status
    Que debe mostrar:

    gunicorn 				RUNNING	pid XXX, uptime XX:XX:XX

    Debido a que si ahora intentamos conectarnos desde otra máquina no funcionará, debemos instalar una herramienta temporal para hacer una pequeña prueba. La herramienta que instalaremos será lynx que no es más que un navegador por comandos. Para instalar la herramienta usaremos el comando:

    sudo apt-get install lynx

    Una vez instalada ejecutaremos el siguiente comando:

    lynx 127.0.0.1:8000

    Lo cual debe mostrar la página de inicio de nuestra aplicación en la consola. Si todo ha ido bien, podemos cerrar lynx con la tecla Q. Una vez cerrado, vamos a probar que la herramienta supervisor hace bien su trabajo y no nos hemos equivocado al configurarla. Para ésto ejecutaremos el comando sudo reboot para reiniciar la raspberry y, cuando se haya iniciado de nuevo, ejecutaremos el comando:

    sudo supervisorctl status
    Que debe mostrar, de nuevo:

    gunicorn 				RUNNING	pid XXX, uptime XX:XX:XX

    Lynx ya se puede desinstalar (si se quiere), mediante el comando:
    sudo apt-get uninstall lynx

    Ya solo queda la última pincelada. Volvemos a activar nuestro entorno de desarrollo virtual con el comando:

    workon django

    Una vez activado, vamos a crear los directorios en los que se almacenarán nuestro archivos estáticos (imágenes, css, etc). Estos directorios estarán en una carpeta dentro de la carpeta /var/www. En nuestro caso, la carpeta será cocopi. Para crear los directorios:

    sudo mkdir /var/www
    sudo mkdir /var/www/cocopi
    sudo mkdir /var/www/cocopi/static
    sudo mkdir /var/www/cocopi/media

    Una vez creados, le asignamos los permisos necesarios al usuario pi (ya que es el que dijimos que ejecutaría el comando del servidor gunicorn):

    sudo chown -R pi:www-data /var/www

    Ahora tendremos que indicar a nuestra aplicación dónde guardar los archivos estáticos. Para ello editamos el archivo settings.py:

    cd web/cocopi/
    nano cocopi/settings.py

    Y añadimos las siguientes variables si no existen. En caso de que ya existan las modificaremos para dejarlas como sigue:

    MEDIA_ROOT = '/var/www/cocopi/media'
    MEDIA_URL = '/media/'


    STATIC_ROOT = '/var/www/cocopi/static'
    STATIC_URL = '/static/'

    MEDIA_ROOT y STATIC_ROOT hacen referencia a dónde se guardarán los archivos, mientras que MEDIA_URL STATIC_URL indican dónde se le dirá al mundo que se guardan los archivos estáticos (es lo que se mostrará en el navegador del cliente cuando se pida una imagen o una hoja de estilos…).

    Una vez guardado el archivo habrá que transportar los archivos estáticos a esos directorios. Este paso es muy importante porque si no se hace, el cliente no tendrá acceso a imágenes, hojas de estilo ni nada que esté en la carpeta static de nuestro proyecto. Para transportar los archivos estáticos se usará el comando:

    python manage.py collectstatic

    En caso de que pidiera confirmación, decir que sí.

    Y ahora, la apoteosis final. Instalaremos nginx que es el servidor HTTP que culminará todo el proceso, lo que nos permitirá quitar el maldito puerto 8000 y acceder a nuestra aplicación como a cualquier otra página. Para instalar ésto, usaremos el comando:

    sudo apt-get install nginx

    Una vez instalado, crearemos un fichero en el que configuraremos nuestro servidor virtual dentro de los sitios disponibles. Esto lo haremos mediante el comando:

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

    Y dentro de ese archivo escribiremos:

    # define an upstream server named gunicorn on localhost port 8000
    upstream gunicorn {
        server localhost:8000;
    }
    
    # make an nginx server
    server {
        # listen on port 80
        listen 80;
    
        # for requests to these domains
        server_name cocopi.com www.cocopi.com;
    
        # look in this directory for files to serve
        root /var/www/cocopi/;
    
        # keep logs in these files
        access_log /var/log/nginx/cocopi.access.log;
        error_log /var/log/nginx/cocopi.error.log;
    
        # You need this to allow users to upload large files
        # See http://wiki.nginx.org/HttpCoreModule#client_max_body_size
        # I'm not sure where it goes, so I put it in twice. It works.
        client_max_body_size 0;
    
        # THIS IS THE IMPORTANT LINE
        # this tries to serve a static file at the requested url
        # if no static file is found, it passes the url to gunicorn
        try_files $uri @gunicorn;
    
        # define rules for gunicorn
        location @gunicorn {
            # repeated just in case
            client_max_body_size 0;
    
            # proxy to the gunicorn upstream defined above
            proxy_pass http://gunicorn;
    
            # makes sure the URLs don't actually say http://gunicorn
            proxy_redirect off;
    
            # If gunicorn takes > 5 minutes to respond, give up
            # Feel free to change the time on this
            proxy_read_timeout 5m;
    
            # make sure these HTTP headers are set properly
            proxy_set_header Host            $host;
            proxy_set_header X-Real-IP       $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

     

    La línea server_name indica el nombre del servidor, esto sirve para identificar las peticiones al dominio. Aquí debemos poner el nombre que tendrá o tiene nuestro dominio. La línea root apunta al directorio en el que se encuentran los archivos estáticos, que es el que creamos dentro de /var/www/.  Por último access_log error_log indican la ruta a los archivos de log que creará nginx.

    Cuando tengamos el archivo creado y guardado, debemos pasarlo de sitios disponibles a sitios activados. Esto lo haremos de la siguiente forma:

    cd /etc/nginx/sites-enabled/
    sudo ln -s ../sites-available/cocopi

    El primer comando nos lleva el directorio de sitios activados y el segundo crea un enlace al fichero creado anteriormente. Una vez hecho ésto, reiniciaremos nginx con el comando:

    sudo service nginx restart

    Y desde la máquina local accedemos a la IP de la raspberry sin el puerto 8000 y… AL FIN. Ya tienes tu servidor montado.

    Una vez terminado el servidor, ya solo tienes que registrar tu dominio y hacer que el hosting apunte a la IP de tu router.

    Si alguna vez tiene que hacer cambios en algún archivo de tu proyecto (vistas, URLs, settings…) para que tome efecto, tendrás que reiniciar el servidor de Gunicorn. Para hacer ésto, primero tienes que identificar el PID de Gunicorn. Esto lo puedes hacer con el comando:

    sudo supervisorctl status

    Y una vez que tengas el PID, ejecutar el comando:

    kill -HUP PID

    Eso ha sido todo, espero que no te haya resultado muy difícil y, para cualquier duda, deja un comentario o contacta directamente conmigo. Muchas gracias por leerme.

5 comentarios por el momento.

  1. Juan dice:

    Hola,

    Muy buen post!!!

    Estoy probando el tutorial con raspberry 2 para hacer un portal django y necesito ayuda para hacer integraciones con otro hardware como arduino.¿Estáis disponibles?

    Saludos

  2. alberto dice:

    Hola,
    Necesito instalar un servidor sobre windows 7, estoy siguiendo el tutorial pero me frene al intentar instalar guricorn, me podes guiar por donde debo seguir.
    Saludos

  3. paola dice:

    Hola… tengo una inquietud.
    el servidor solo funciona si esta conectado a una red ethernet o wifi? o solo esta es necesaria para colocar la ip fija a la pi no se necesita después de hacer la configuración?

  4. Ferr dice:

    Hola tengo el siguiente error en el log de gunicorn

    supervisor: couldn't chdir to /home/pi/web/my-firs-blog: ENOENT
    supervisor: child process was not spawned

Responder a Ferr Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>