Introducción al Desarrollo en Drupal 8
Curso de desarrollo en Drupal 8: aprende a desarrollar en back-end para uno de los CMS PHP más...
Aprende a desplegar tus proyectos Drupal utilizando Docker gracias a este artículo, en el que te contamos todo el proceso paso a paso y de forma práctica.
Este artículo es en realidad una pequeña guía - tutorial a modo de introducción. En este artículo conocerás las claves principales de Docker, sus conceptos básicos y su terminología. Comprenderás su mecánica habitual y además practicarás despliegues de proyectos basados en Drupal 9 a partir de casos de uso de contenedores Docker. Con esto podrás iniciar con facilidad nuevos proyectos basados en Drupal en tu entorno local.
A día de hoy se han producido muchos cambios en el contexto de Drupal. Recientemente hemos realizado el tránsito a una nueva versión mayor -Drupal 9, que salió el día tres de junio de este mismo año-, y aunque esta evolución no resulta tan abismal como el salto que se produjo entre la versión 7 y la 8 (que trajo muchos cambios profundos), lo cierto es que Drupal sigue avanzando a una gran velocidad mientras cumple con sus compromisos de actualizaciones de cara a las siguientes versiones.
Uno de los cambios producidos en los últimos años radica en la manera en la que abordamos proyectos basados en esta tecnología: la forma en la que preparamos nuestros entornos, la manera en la que configuramos los proyectos, como desplegamos nuestros proyectos y de manera complementaria, cómo y con qué hemos ampliado nuestro stack tecnológico habitual, es decir, el conjunto de herramientas que usamos para implementar proyectos basados en Drupal.
Este conjunto de puntos de evolución no resulta puramente intrínseco (no son determinados desde el interior de la plataforma), pero es cierto que ha tenido un fuerte componente adaptativo: Nos hemos preocupado mucho de saber que se estaba usando en otros contextos para poder incorporarlo al entorno propio de Drupal. Así, entre lo externo y lo interno, es como Drupal ha venido cambiando los últimos años. Y entre esas dos dimensiones es donde Docker adquiere una importancia fundamental. En este artículo propondremos, además de una interpretación crítica de ello, algunas experiencias para practicar de manera combinada con Docker y Drupal mientras razonamos la importancia real y diaria de esta relación. Comencemos.
¿Tienes conocimientos previos de Drupal? Aquí tienes una pequeña introducción teórica a qué es Drupal, y recuerda que tienes disponibles el curso sobre Drupal de OpenWebinars.
A menudo, en las organizaciones que realizan su tránsito a Drupal aparecen una serie de cuestiones iniciales que son muy parecidas y recurrentes, y que resumen bastante bien las claves fundamentales a las que nos enfrentamos cuando nos decantamos por ejecutar proyectos basados en Drupal. La cantidad de preguntas, dudas y cuestiones iniciales que aparecen en ese contexto puede agruparse o reducirse en el siguiente esquema que propongo:
¿Cómo preparamos nuestros entornos para un proyecto Drupal?
¿Cómo hacemos que el equipo trabaje de manera colaborativa en el mismo proyecto?
¿Cómo desplegamos en los sucesivos entornos disponibles para el proyecto (Test, Stage, Live, Producción, etc)?
Estos son los tres retos fundacionales que una organización que cambia a Drupal debe abordar en primer lugar. Cualquier equipo que haya realizado este viaje se ha hecho estas preguntas y con mayor o menor soltura, con mayor o menor artesanía e incluso con mayor o menor número de herramientas ha debido responder con su práctica diaria y su aprendizaje. El stack tecnológico puede ampliarse o reducirse en base a las necesidades y complejidad del proyecto, incorporando multitud de herramientas como Kubernetes, OpenShift, Kibana, Apache Kafka y muchas más, pero sin duda siempre habrá una herramienta presente en todas las ejecuciones de proyectos, la piedra angular de la ejecución Drupal que aparece en la resolución de las tres preguntas anteriores: Docker.
En la base de casi cualquier cosa que podamos construir con Drupal, siempre estará Docker como recurso básico… mira este ejemplo conceptual, resume un esquema de seis capas para Kubernetes, una plataforma OpenSource para gestionar aplicaciones basadas en contenedores, muy usada en la actualidad para despliegues de cierta envergadura y que facilita el escalado vertical u horizontal:
Como puedes ver, aunque es posible que el resto de cajas no te suenen demasiado, ya puedes ver como en la base de la plataforma anida el concepto central de contenedor Docker. Conocerlos y trabajar con ellos es fundamental, así que vamos a acercarnos todo lo posible a esta herramienta. Si tienes conocimientos previos y quieres ampliarlos, aquí tienes un curso de Docker para desarrolladores.
Ya sabemos más o menos en que se basa Docker: la filosofía detrás del concepto de contenedor, la construcción mínima de sistemas operativos mediante la suma de diferentes capas funcionales que terminan generando una virtualización software donde se inyectan una serie de ficheros en el anfitrión para aprovechar su kernel y poder crear otros sistemas operativos virtualizados, tal y como se puede interpretar en la siguiente imagen:
Sabemos también que en torno a esta filosofía se generan diversos conceptos relacionados para ampliar las interacciones entre contenedores, sistemas y subsistemas (ficheros docker-compose, Dockerfiles, Imágenes, Contenedores, etc), y además una infraestructura que nos permite conectar las diferentes partes:
Creas un Dockerfile con todos los pasos necesarios para crear tu sistema operativo y tu aplicación.
La subes a un repositorio tipo Github.
Conectas una cuenta de DockerHub a ese repositorio y das de alta tu imagen a partir del Dockerfile original.
Defines servicios / contenedores en local mediante un fichero docker-compose.yml que usará tus imágenes de DockerHub para levantarn en tu entorno local el despliegue de tu aplicación.
Todo esto hace que el gráfico anterior se amplie y se expanda algo más, resultando en un marco como el siguiente:
Dónde el despliegue de un proyecto queda definido por las interacciones entre varios contenedores, a razón de un contenedor por cada aplicación a usar, teniendo en cuenta los siguientes conceptos:
En los siguientes apartados vamos a materializar algunos de los conceptos anteriores practicando con estos conceptos.
Como pre-requisito necesario, necesitamos tener instalado en nuestro sistema operativo los recursos base: Docker (para la creación de contenedores) y Docker-Compose (para ejecutar aplicaciones de varios contenedores). Normalmente Docker-Compose nos facilita las cosas permitiendo diseñar una red de contenedores para un proyecto a través de una descripción sucesiva de servicios a lo largo de un fichero YAML de extensión .yml. En este punto, según tu sistema operativo puedes tener varias opciones:
Si estás en Windows, puedes instalar y configurar tu propio WSL (Subsistema de Linux para Windows) y a continuación instalar Docker Desktop for Windows. Con la versión basada en WSL parece que funciona mucho mejor que antes, aunque puede ocasionar conflictos si ya tienes activada la virtualización hardware por hipervisor. Por ejemplo, si tienes activada VirtualBox con alguna VM te recomiendo que la apagues antes de iniciar todo el proceso.
Para Linux, tendrás que resolver algunos pasos más que anoto a continuación. En concreto, estas instrucciones están pensadas para un entorno Ubuntu o basado en Debian, pero seguro que pueden versionarse fácilmente para otra distribución.
# Actualizamos la lista de paquetes.
$ sudo apt update
# Instalamos algunos recursos básicos para el sistema.
$ sudo apt install -y build-essential apt-transport-https ca-certificates jq curl software-properties-common file git gnupg-agent
# Instalamos Docker en el sistema añadiendo el repositorio, actualizando e instalando.
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io
# Habilitamos Docker en el sistema.
$ sudo systemctl unmask docker
$ sudo systemctl start docker
$ sudo systemctl enable --now docker
# Añadimos el usuario actual de nuestro sistema al grupo Docker.
$ sudo usermod -aG docker $USER
# Abrimos permisos para el daemon de Docker a los usuarios del sistema.
$ sudo chmod 666 /var/run/docker*
# Chequeamos si tenemos ya Docker funcionando.
$ systemctl is-active docker
# Comprobamos la versión de Docker.
$ docker --version
# Realizamos una prueba de ejecución de Docker.
$ docker run hello-world
# Instalamos Docker-Compose en el sistema.
$ sudo apt install -y docker-compose
# Comprobamos si se instaló Docker-Compose y obtenemos su versión.
$ docker-compose --version
Si te resulta más cómodo, puedes descargarte las instrucciones anteriores reunidas en un script bash a partir de la dirección del repositorio: https://github.com/davidjguru/scripting_for_drupal/blob/main/installers/tools_and_environment/installing_docker_dockercompose.
Ejecutando los siguientes pasos:
$ wget https://raw.githubusercontent.com/davidjguru/scripting_for_drupal/main/installers/tools_and_environment/installing_docker_dockercompose
$ chmod +x installing_docker_dockercompose
$ ./installing_docker_dockercompose
Para este apartado vamos a tomar un camino poco habitual. Llegados a este punto normalmente los tutoriales suelen comenzar a explicar cómo se prepara un Dockerfile, como se articula la imagen, la ejecución de esta, la conexión con otros contenedores, etc. Pero algo así nos dejaría fuera del alcance acotado de este tutorial (algo así merece su propio curso, algo como el curso de introducción a Docker de OpenWebinars).
En lugar de esto, vamos a empezar por el final: ya tenemos preparado todo, y a partir de ahí deduciremos que está ocurriendo. ¿Por qué? en primer lugar, este proyecto que vamos a usar tiene mucho potencial a nivel didáctico, ya que viene pre-diseñado con multitud de servicios / contenedores disponibles: Nginx, Apache, Drupal, MariaDB, PostgreSQL, Redis, Memcached, Varnish, etc. En segundo lugar, es muy extraño hoy día articular desde cero una imagen para Docker: lo normal es partir de una ya preparada y sumar algunas acciones específicas. Ya casi nunca construimos nuestras propias imágenes de manera integral, así que este proyecto sirve perfectamente a nuestros fines.
Para ello vamos a tomar como referencia el repositorio de docker4drupal creado por el equipo de Wodby, en la dirección: https://github.com/wodby/docker4drupal.
Aquí encontramos ya la articulación integral de un proyecto Drupal previamente configurada a través de un fichero docker-compose.yml con la que podemos jugar. Comenzaremos por copiar localmente el repositorio en nuestro entorno:
$ git clone https://github.com/wodby/docker4drupal.git
Que copiará en nuestra máquina todo el contenido del repositorio. A partir de ahí, y con Docker y Docker-Compose ya instalado en nuestro entorno, solo tenemos que ejecutar la siguiente instrucción para levantar todo el sistema:
$ cd docker4drupal
$ docker4drupal$ docker-compose up -d
Que inmediatamente levantará la red de contenedores pre-configurada:
Starting docker4drupal_crond_1 ... done
Starting my_drupal9_project_php ... done
Starting my_drupal9_project_traefik ... done
Starting my_drupal9_project_mariadb ... done
Starting my_drupal9_project_nginx ... done
A partir de este momento, podemos consultar los contenedores levantados en nuestro sistema ejecutando la instrucción:
$ docker ps
¿Qué ha ocurrido? bien en primer lugar, se han levantado todos los contenedores habilitados en el proyecto. Si consultas la lista de servicios / contenedores disponibles en https://github.com/wodby/docker4drupal/blob/master/README.md podrás ver el listado completo de recursos, pero solo están marcados los habilitados directamente (si no sería una red muy extensa de contenedores).
¿Cómo se han deshabilitado ciertos servicios / contenedores? Bien, esto ocurre en el fichero de configuración docker-compose.yml que puedes ver en tu editor de texto favorito o IDE:
Aquí puedes ver algunos bloques comentados, registros que corresponden a los servicios deshabilitados y solo necesitarán detener la red de contenedores, descomentar el bloque del servicio / contenedor que quieras habilitar y volver a levantar la red de nuevo:
docker4drupal$ docker-compose stop
docker4drupal$ vim docker-compose.yml
docker4drupal$ docker-compose up -d
¿Qué contenedores tienes habilitados por defecto? En este caso dispones ya de partida de los siguientes servicios:
En esta dirección que Traefik expone podrás acceder a tu instalador de Drupal desde el navegador, asomándote al contenedor de Docker que despliega el servicio de Drupal: http://drupal.docker.localhost:8000.
Esta dirección en tu navegador te llevará al proceso de instalación habitual de Drupal:
A continuación podríamos seguir con el proceso de instalación vía navegador habitual, pero ¿y si probamos Drush en el contenedor?
Bien, para eso solo tengo que ejecutar dentro del contenedor Drupal la instrucción, usando el nombre del contenedor (en mi caso es my_drupal9_project_php
):
docker4drupal$ docker exec my_drupal9_project_php drush si --site-name=Example-Drupal --account-name=admin --account-pass=admin -y
Los datos de conexión a la base de datos ya existen y están pre-configurados en el fichero de variables de entorno .env que viene con el proyecto:
DB_NAME=drupal
DB_USER=drupal
DB_PASSWORD=drupal
DB_ROOT_PASSWORD=password
DB_HOST=mariadb
DB_PORT=3306
DB_DRIVER=mysql
y voilá! en tu navegador ya estará instalado Drupal 9 con un usuario admin/admin listo para el acceso:
Y tras un login con user y password: admin/admin
en la dirección: http://drupal.docker.localhost:8000/user/login ya puedes acceder al menú de creación de contenidos y crear tu primer nodo en Drupal 9:
Dirección: http://drupal.docker.localhost:8000/node/add/article
Para empezar la primera cuestión que surge es… pero ¿dónde está mi código fuente? ¿Dónde se aloja, en este caso, el código base de esta instalación Drupal para poder versionarlo? Bien, para responder a esta pregunta, volveremos a revisar la composición del fichero descriptivo de servicios docker-compose.yml, donde se especifican los “volúmenes” asociados a cada servicio.
¿Qué son los “volúmenes”?
Conceptualmente, un volumen es la solución aportada por Docker para que la información relacionada con un contenedor sea persistente, es decir, resista al momento en el que el contenedor sea detenido y reiniciado, y dicha información siga existiendo. Son carpetas / ficheros (para Linux todo es un fichero), preparadas para el almacenamiento de recursos: código fuente, datos, imágenes, ficheros, etc.
Suele ser una implementación dentro del anfitrión (host) que queda mapeada entre este y el huesped (Guest), de tal manera que el contenedor cree que escribe ficheros en una dirección final del anfitrión. Y como el resto de recursos, se describen el fichero docker-compose.yml
.
En este caso que nos ocupa, al revisar el fichero, vemos como en cierto momento de la descripción del servicio php para Drupal se articula el volumen usado para los datos:
php:
image: wodby/drupal-php:$PHP_TAG
container_name: "${PROJECT_NAME}_php"
environment:
PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S mailhog:1025
volumes:
- ./:/var/www/html:cached
Por tanto, nuestros datos son mapeados y almacenados a partir de la ruta:
./:/var/www/html:cached
Sabemos qué “.” en Linux significa el directorio actual, pero ¿cuál es este? (de hecho, si vamos a la carpeta docker4drupal local del proyecto no encontramos los datos de Drupal) ¿Dónde está? este se encuentra localizado en el directorio actual, pero desde el punto de vista del Docker Engine desde el que se ejecutan las instrucciones, así que en realidad, nuestro código vive habitualmente en la dirección a partir del directorio raíz de tu sistema (/var/lib/docker:
/var/lib/docker/volumes/docker4drupal_codebase/_data/
Es aquí donde podrás encontrar el código base de tu instalación Drupal, con su estructura de directorios habitual. El caso es que realmente no resulta una dirección muy intuitiva, o suficientemente sencilla como para afrontar el siguiente paso: poner tu instalación de Drupal bajo el control de versiones (Git) y llevarlo a un repositorio remoto Github / Gitlab, así que puedes probar a detener docker, modificar la ruta del volumen por una dirección más “amigable”, por ejemplo, justo dónde tienes el material de /docker4drupal, y volver a levantar la red de contenedores (docker-compose up -d). Así tendrás un escenario de pruebas y aprendizaje mucho más cómodo para practicar.
A continuación, te dejo con dos bloques más: un listado de comandos Docker habituales y un set de enlaces a información útil.
¿Estás ready para practicar con Docker y Drupal? También tienes disponible un curso de introducción a Docker aquí en OpenWebinars.
# Ejecuta un contenedor en modo “detached”, volviendo al terminal del anfitrión.
docker run -d vendorexample/appexample
# Ejecuta un contenedor con nombre custom.
docker run -d --name web-custom-name nginx:1.14-alpine
# Reinicia un contenedor.
docker restart IDCONTAINER
# Ejecuta un contenedor a partir de una imagen centOS y hace login en el terminal del invitado.
docker run -it centos bash
# Ejecuta un contenedor en background con parada final marcada.
docker run -d centos sleep 100
# Ejecuta un contenedor mapeando puertos (-p) entre anfitrion y huesped, mapeando también volúmenes (-v) y activando un usuario (-u)
docker run -p 80:8080 -v /locahost/folder:/container/folder \
-u root jenkins/jenkins
# Obtén una lista de todos tus contenedores existentes.
docker ps
# Extrae una lista de contenedores, pero mostrándolos por su ID.
docker ps -q
# Mata todos los contenedores seleccionándolos por ID.
docker kill $(docker ps -q)
# Destruye un contenedor en concreto por su ID.
docker rm IDCONTAINER
# Destruye todos los contenedores que tengan status=exited
docker rm $(docker ps -q -f status=exited)
# Ejecuta un comando dentro de la terminal de un contenedor.
docker exec IDCONTAINER unixcommand
# Conéctate al terminal de un contenedor.
docker exec -it IDCONTAINER /bin/bash
# Conéctate al terminal de un contenedor como root
docker exec -ti -u root IDCONTAINER /bin/bash
Un clásico:
# Copiar ficheros desde local al contenedor Docker.
docker cp db/dump.sql IDCONTAINER:/tmp/dump.sql
# Copiar ficheros desde el contenedor Docker a tu local.
docker cp IDCONTAINER:/tmp/dump_test.sql ./db
# Muestra la salida de consola de un contenedor desde la del anfitrión.
docker attach IDCONTAINER
# Muestra toda la información relacionada con un contenedor específico.
docker container inspect IDCONTAINER
# Muestra en concreto información sobre la IP interna de un contenedor específico.
docker inspect drupal | grep "IPAddress"
# Muestra el log de un contenedor en concreto.
docker logs -f IDCONTAINER
# Localiza información en logs:
# Busca “error” (insensitive a mayus / minus) en las últimas mil líneas de
# un contenedor Jenkins añadiendo el timestamp al principio de cada línea.
sudo docker logs -t --tail 1000 jenkins 2 >&1 \
| grep -i error
# Destruye y limpia datos sin usar de manera forzada en el sistema Docker.
docker system prune -f
# Muestra estadísticas de consumo en los contenedores.
docker stats
# Igual que el anterior, pero formateando la salida por columnas.
docker stats --all --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# Igual que los anteriores, pero en este caso fija los valores en un snapshot único por consola.
docker ps -q | xargs docker stats --no-stream
# Revisa la consistencia de tu fichero docker-compose
docker-compose -f docker-compose.yml config
# Arranca la red de contenedores en modo “detached”, volviendo a tu prompt.
docker-compose up -d
# Arranca la red de contenedores, pero montando estos a partir de las imágenes.
docker-compose up --build
# Para una red de contenedores.
docker-compose stop
# Detén tu red de contenedores, haz rebuild y la vuelves a lanzar regenerando contenedores, tras algún cambio.
docker-compose stop && docker-compose up --build --force-recreate
Aquí puedes practicar con la definición de imágenes Docker escribiendo Dockerfiles:
https://docs.docker.com/get-started/part2/
https://stackify.com/docker-build-a-beginners-guide-to-building-docker-images
Aquí puedes comprobar las equivalencias entre las distintas versiones de Docker-Compose y las versiones de Docker disponibles:
https://docs.docker.com/compose/compose-file/
Aquí puedes ver una guía de uso del equipo de wodby docker4drupal:
https://wodby.com/docs/1.0/stacks/drupal/local/#usage
Aquí puedes encontrar un listado más extenso de comandos para Docker y Docker-Compose:
https://www.therussianlullaby.com/blog/docker-docker-compose-and-ddev-cheatsheet/
También te puede interesar
Curso de desarrollo en Drupal 8: aprende a desarrollar en back-end para uno de los CMS PHP más...
Aprende a crear entidades personalizadas en Drupal 8 usando Drupal Entity API.