Dockerizando Apps en Django

Docker es una plataforma abierta para construir, enviar y ejecutar aplicaciones distribuidas. Brinda a los programadores, equipos de desarrollo e ingenieros de operaciones la caja de herramientas común que necesitan para aprovechar la naturaleza distribuida y en red de las aplicaciones modernas.
La contenedorización ofrece varias ventajas, como un aprovisionamiento más rápido o entornos aislados y fáciles de recrear. Si no sabe qué es la contenedorización, puede pensar en ella como una forma simplificada de virtualización. O mejor aún, puede buscar una descripción detallada, que no es el propósito de esta publicación.
Por un tiempo, he estado interesado en «Dockerizar» mis aplicaciones de Django para mejorar mis canalizaciones de implementación. Si bien la documentación en Docker es muy buena, sus ejemplos son demasiado generales para mí, así que decidí investigar un poco sobre cómo hacerlo. En esta publicación, tengo la intención de dar un paseo por «Dockerizando Django» y exponer un puerto ejecutando uwsgi, que se puede usar para la producción a diferencia del servidor de desarrollo de Django.
Explicaré la mayoría de los pasos, sin embargo, recomiendo leer docker’s tutorial on images. Asumo que tienes Docker instalado y corriendo.
Ok, iniciemos.
En la raíz de nuestro proyecto Django (misma carpeta que manage.py) tendremos alguna estructura :
- carpeta de requisitos, que contiene los requisitos de Python que debe instalar pip.
- require.apt, que contiene los requisitos del sistema.
- my_project.ini, que se utiliza para mantener las configuraciones de uwsgi.
El primer paso es proporcionar un Dockerfile en la raíz del proyecto, que le dice a Docker cómo construir la imagen. Mi Dockerfile se ve así:
FROM ubuntu:14.04 RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV PYTHONBUFFERED 1 ADD ./requirements /requirements ADD ./install_os_dependencies.sh /install_os_dependencies.sh ADD ./requirements.apt /requirements.apt ADD ./my_project.ini /my_project.ini RUN ./install_os_dependencies.sh install RUN pip3 install -r "requirements/requirements.txt" RUN groupadd -r django && useradd -r -g django django ADD . /app RUN chown -R django /app RUN chgrp -R django /app WORKDIR /app EXPOSE 8000 CMD sudo -u django uwsgi my_project.ini --master
Esto construirá nuestra imagen usando como base dockerhub’s ubuntu. Puede cambiar esto cambiando la primera línea, siempre que obtenga los requisitos para su otra imagen:
FROM ubuntu:14.04
Necesitamos generar los entornos locales explícitamente para evitar algunos problemas con Python y Unicode. Eso puede no ser necesario con otras imágenes.
RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV PYTHONBUFFERED 1
A continuación, agregamos los archivos necesarios para construir nuestra imagen:
ADD ./requirements /requirements ADD ./install_os_dependencies.sh /install_os_dependencies.sh ADD ./requirements.apt /requirements.apt ADD ./my_project.ini /my_project.ini
install_os_dependencies.sh es un script bash que instalará los requisitos del sistema, que se enumeran en require.apt. Se inicia con:
RUN ./install_os_dependencies.sh install
Puede encontrar este script en PyDanny’s cookiecutter repository.
Estoy usando Python3 para este proyecto, por lo que mis requisitos.apt deben reflejar que:
##basic build dependencies of various Django apps for Ubuntu 14.04 #build-essential metapackage install: make, gcc, g++, build-essential #required to translate gettext python3-dev python3-pip ##shared dependencies of: ##Pillow, pylibmc zlib1g-dev ##Postgresql and psycopg2 dependencies libpq-dev ##Pillow dependencies libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms1-dev libwebp-dev ##pylibmc libmemcached-dev libssl-dev ##django-extensions graphviz-dev ##lxml libxml2-dev libxslt-dev
En este punto, nuestra imagen tiene las dependencias básicas del sistema, instalemos nuestras dependencias de Python:
RUN pip3 install -r "requirements/requirements.txt"
El archivo requirements.txt debe estar en un formato que pip pueda entender. Para este proyecto, instalaré solo Django y uwsgi, por lo que el archivo es muy corto:
django==1.8.4 uWSGI==2.0.11.1
Ahora que todos los requisitos están instalados, crearemos un nuevo usuario con permisos más bajos, que usaremos para ejecutar uwsgi:
RUN groupadd -r django && useradd -r -g django django ADD . /app RUN chown -R django /app RUN chgrp -R django /app WORKDIR /app
WORKDIR es la forma preferida de Docker para cambiar directorios (no algo así como RUN cd).
Ok, tenemos nuestra aplicación y todos sus requisitos instalados. Necesitamos exponer un puerto para hablar con el mundo (8000 en este caso):
EXPOSE 8000
En este punto, podríamos ejecutar el servidor de desarrollo de nuestro Django y exponerlo, pero eso no sería útil para la producción. Agregaré uwsgi para ejecutar mi aplicación Python.
El siguiente paso es definir las configuraciones para uwsgi en my_project.ini:
[uwsgi] http = :8000 chdir = /app/ env = DJANGO_SETTINGS_MODULE=config.settings wsgi-file = config/wsgi.py processes = 4 threads = 2
Con la directiva http, le estamos diciendo que sirva http. Es posible que desee utilizar sockets tcp o sockets Unix, lo que requerirá un cambio en esa configuración. Por ahora, solo quiero acceder al servidor desde mi navegador local, por lo que http es suficiente.
chdir le dice a uwsgi dónde está la aplicación.
Establecemos una variable de entorno para seleccionar una de nuestras configuraciones. Si tiene configuraciones específicas de instancia, puede cambiarlas aquí (local, prueba, producción, etc.).
La última línea en nuestro Dockerfile define qué comando se ejecutará cuando comencemos nuestra imagen
CMD sudo -u django uwsgi my_project.ini --master
Corremos uwsgi como el usuario que creamos.
Ok, hemos terminado de construir nuestro Dockerfile. Es hora de verificar si podemos construir nuestra imagen (no olvide el.):
docker build -t django_app .
Si todo funciona, deberíamos tener algo como esto:
Successfully built dd40ad889782
Ese es el id the nuestra nueva imagen. Podemos correrla ahora:
docker run -p 8000:8000 -ti dd40ad889782
Le decimos a Docker qué puertos queremos reenviar (host: guest) con -p. -ti nos permite detener el contenedor con ctrl + C. Si no nos importa esto, esa bandera puede omitirse.
Suponiendo que su aplicación Django no tenga problemas, debería poder usarla desde http://localhost:8000.
Ni siquiera mencioné las bases de datos porque eso está más relacionado con la configuración de Django que con la configuración de Docker. Puede usar sqlite para que funcione.
Si desea inspeccionar las partes internas de su nuevo contenedor, puede acceder a ellas con:
docker exec -it weird_name bash
Donde weird_name es el nombre asignado por Docker a su contenedor. Si desea verificar sus contenedores en ejecución, puede ejecutar:
docker p
Esto debería ser suficiente para comenzar. Mantuve la mayoría de las cosas al mínimo para evitar una complejidad innecesaria.