Desplegar Drupal 8 en un contenedor en Azure App Service

Pues hoy tocaba Drupal 8 🙂 y he estado buscando la mejor forma, tanto de despliegue en App Service como de automatización con Azure DevOps, para montar este escenario. No soy ninguna experta en Drupal, por lo que si hay algún punto que se pueda hacer mejor el feedback siempre es bien recibido. En este artículo te cuento cómo montar un entorno en local que luego migraré a Microsoft Azure.

Entorno en local con ddev

Existen diferentes formas de crear tu entorno para Drupal en local, pero el más sencillo con el que me he encontrado ha sido con la herramienta ddev. He seguido las instrucciones de la documentación oficial de Drupal. Por lo que básicamente he hecho lo siguiente:

He instalado ddev, que me ayuda a montarme un entorno de desarrollo de forma rápida, apoyándose en Docker:

curl -L https://raw.githubusercontent.com/drud/ddev/master/scripts/install_ddev.sh | bash

Después he creado un sitio con Drupal 8 con la ayuda de Composer.

# Replace my_site_name!
export SITE_NAME=drupal-src
composer create-project drupal-composer/drupal-project:8.x-dev --stability dev --no-interaction $SITE_NAME
cd $SITE_NAME

Dentro de mi proyecto he configurado ddev:

ddev config --docroot web --projectname $SITE_NAME --projecttype drupal8

y lo he arrancado para que me genere los contenedores pertinentes y me de la URL por la que acceder a mi nuevo sitio:

ddev start

Para darle un poco de emoción al asunto, he elegido la plantilla de demo durante el asistente.

Plantilla de ejemplo de Drupal para hacer las pruebas

Exportación de la base de datos a Azure Database for MySQL

Ahora que todo funciona correctamente en local, lo siguiente que he hecho ha sido crearme una base de datos en Azure Database for MySQL. La versión de MySQL que he desplegado es la 5.7 , ya que la versión 8 parece que todavía no anda muy bien con Drupal 8.

Ahora lo que necesito es hacer un backup de la base de datos asociada a mi entorno de desarrollo a través del siguiente comando con ddev:

ddev export-db --gzip=false >/tmp/db.sql

Como todavía no tengo una base de datos en Azure a la que volcar esta información, solo un servidor de base de datos, a través del cliente de MySQL me he conectado al servidor que acabo de crear y me he creado una base de datos vacía:

mysql -h YOUR_SERVER.mysql.database.azure.com -u USER@YOUR_SERVER -p YOUR_PASSWORD
CREATE DATABASE drupaldb;

Nota: si tienes instalado MySQL Workbench puedes usar el cliente de MySQL exportando la siguiente ruta:

export PATH=$PATH:/Applications/MySQLWorkbench.app/Contents/MacOS

Por último he volcado el backup en la nueva base de datos:

mysql -h YOUR_SERVER.mysql.database.azure.com -u USER@YOUR_SERVER -p YOUR_PASSWORD drupaldb < /tmp/db.sql 

Asegurate de que has permitido en tu servidor de MySQL en Azure la conexión desde otros servicios de la nube (y tu IP por si quieres probar en local). También he deshabilitado el SSL para este artículo.

Ahora debes añadir la configuración de la base de datos a web/sites/default/settings.php (recuerda que hay mejores formas de hacer esto. Esto es solo un piloto):

$databases['default']['default'] = [
  'database' => 'drupaldb',
  'username' => 'USER@YOUR_SERVER',
  'password' => 'YOUR_PASSWORD',
  'host' => 'YOUR_SERVER.mysql.database.azure.com',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'collation' => 'utf8mb4_general_ci',
];

También necesitas añadir un hash_salt a este archivo:

$settings['hash_salt'] = 'CHANGE_THIS';

Para evitar problemas a la hora de migrar al nuevo entorno he deshabilitado el preprocesamiento de los archivos javascript y css:

/**
 * Disable CSS and JS aggregation.
 * https://stackoverflow.com/questions/57027471/javascript-css-not-loading-after-drupal-8-migration
 */
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;

Lo ideal es habilitarlo de nuevo una vez que haya finalizado el despliegue.

He eliminado el archivo .gitignore del proyecto para que todos los cambios que realice en local vayan a mi nueva imagen. Obviamente habrá que afinar el mismo para que solo se suban los directorios y archivos que corresponda.

DockerFile

Para poder desplegar mi solución en un contenedor en App Service necesito un archivo Dockerfile. El equipo de App Service tiene diferentes imágenes subidas en Docker Hub, con el código de las mismas en GitHub, por lo que voy a utilizar esta como base. La estructura de mi proyecto quedaría de esta forma, estando en drupal-src mi proyecto:

Estructura de mi proyecto de Drupal 8 para Web App for Containers

Hay algunos ficheros que no necesitas, como drupal-database-install-tasks.php o incluso parte del script de entrypoint.sh, donde inicialmente recupera el código fuente de la aplicación de un repositorio git. Por otro lado he hecho los siguientes cambios:

Docker file

En este archivo solo he añadido la sentencia ADD . /drupal-src ${DRUPAL_PRJ} para que todo el contenido de mi código fuente se copie dentro de la imagen.

# 1. Drupal
# ====================
RUN mkdir -p $DOCKER_BUILD_HOME
#Add your code to /home/drupal_prj
ADD ./drupal-src ${DRUPAL_PRJ}

entrypoint.sh

En este archivo, la función setup_drupal estaba recuperando el código fuente de un repositorio git, entre otras tareas. Lo he modificado para que simplemente recupere el código que copié en el archivo Dockerfile, ajuste los permisos (me volví loca con las imágenes) y utilice drush rebuild para reconstruir la cache y que no pasen cosas raras (me volví loca con los estilos).

#Get drupal from your code
setup_drupal(){    
    cd $DRUPAL_PRJ    
    chmod a+w "$DRUPAL_PRJ/web/sites/default" 
    if [ -e "$DRUPAL_PRJ/web/sites/default/settings.php" ]; then 
        #Test this time, if application settings are set to a personal git, myabe drupal has already installed in repo.
        echo "INFO: Settings.php is exist..."    
    else
        echo "INFO: Settings.php isn't exist..."    
        mkdir -p "$DRUPAL_PRJ/web/sites/default/files"
        cp "$DRUPAL_PRJ/web/sites/default/default.settings.php" "$DRUPAL_PRJ/web/sites/default/settings.php"
    fi
    chmod a+w "$DRUPAL_PRJ/web/sites/default/files"
    chmod a+w "$DRUPAL_PRJ/web/sites/default/settings.php"
    while test -d "$DRUPAL_HOME"  
    do
        echo "INFO: $DRUPAL_HOME is exist, clean it ..."        
        chmod 777 -R $DRUPAL_HOME 
        rm -Rf $DRUPAL_HOME
    done
    
    echo "Composer install --no-dev"
    composer install -o --no-dev --no-interaction
    echo "Giving write permissions to $DRUPAL_PRJ/web/sites/default/files/"
    chmod -R 777 "$DRUPAL_PRJ/web/sites/default/files/" 
    echo "Rebuild cache (drush rebuild)..."
    drush rebuild
    echo "Clean generated images (drush "
    ln -s $DRUPAL_PRJ/web  $DRUPAL_HOME           	
}

El resto de archivos se quedan igual que en el ejemplo de GitHub.

Despliegue con Azure DevOps

Lo siguiente que he hecho ha sido subir a GitHub mi código fuente y me he creado un proyecto dentro de Azure DevOps para poder gestionar la integración continua y el despliegue continuo:

Pipeline para la build

La pipeline de build sería como la siguiente:

Build pipeline

Como estoy trabajando con contenedores de Docker, necesito como mínimo estas dos tareas. A esta pipeline le he asociado el repositorio de GitHub y he utilizado un agente de Linux hosteado por Azure DevOps.

Build an image

Generación de la imagen de docker

La imagen se genera con el archivo Dockerfile del código fuente y he elegido un Container Registry en Azure como repositorio para mis imágenes. La razón por la cual necesito especificarlo ahora es para que me etiquete la imagen de manera correcta, y automática 🙂

Push an image

Publicar la imagen en Azure Container Registry

Una vez que la imagen se ha creado correctamente en el agente, el siguiente paso es subirla al ACR, donde utilizo el mismo que en el paso anterior y simplemente hago push.

Pipeline para la release

En este ejemplo he creado una pipeline con un único stage:

Tareas en la pipeline de release

Bash Script

Es interesante que sepas que puedes tener ciertas tareas en Azure DevOps que luego puedes desactivar. En este caso, la primera solamente la estaba usando para comprobar que la variable Build.BuildId está disponible, ya que no quiero sobrescribir la imagen una y otra vez sino que prefiero tener una nueva imagen por cada build y así tenerla versionada por si necesito dar un paso atrás.

Deploy in staging slot to warm up

Esta tarea va a desplegar mi nueva imagen, con mi proyecto de Drupal 8, en el slot llamado staging, para que pueda hacer un precalentamiento del sitio y cambiar después al slot de producción. En ella debo personalizar los siguientes valores:

Por otro lado, en el apartado Application and Configuration Settings debes almacenar los siguientes valores para poder conectarte a tu ACR:

  • DOCKER_REGISTRY_SERVER_URL: puedes encontrar la URL de tu ACR en el apartado Overview del mismo.
  • DOCKER_REGISTRY_SERVER_USERNAME: se encuentra en Access Keys.
  • DOCKER_REGISTRY_SERVER_PASSWORD: también en el apartado Access Keys.
Application settings necesarias para el acceso a ACR

En esta tarea también he configurado la variable de salida AppServiceApplicationUrl para poder recuperarla en la siguiente:

Configuración de la variable de salida AppServiceApplicationUrl

Web Url

Lo mismo que en anterior bash, sólo lo he usado para comprobar que Staging.AppServiceApplicationUrl tiene el valor de la URL de staging,que siempre viene bien un poco de debugging 😀

Wait for 2 minutes

Tarea que espera el tiempo que le indiques

Me descargué esta tarea desde el marketplace, en la que simplemente espero 2 minutos para que le dé tiempo al contenedor a arrancar.

Warmup staging slot

Tarea Warmup staging slot

Otra tarea más del marketplace que me ayudará en el proceso de precalentamiento del sitio, pasándole como variable la URL del entorno de staging y los paths a los que quiero que haga una llamada.

Swap to production

Por último, una vez que el entorno de staging está listo, hago el swap con producción. Cuando finalice todo el proceso, si accedes a la URL de tu App Service deberías de ver tu Drupal funcionando.

Cómo ver los logs

Te recomiendo que habilites los logs de App Service en el apartado Monitoring > App Service logs.

Monitoring > App Service logs

Después, haz clic en Go en la sección Advanced Tools:

App Service – Advanced Tools

Accederás al portal Kudu donde podrás ver tus logs en el apartado Log Stream:

App Service – Kudu – Log stream

¡Saludos!

1 Comment

  1. Alejandro Alvarado febrero 8, 2021 at 2:16 pm

    Hola Gisela, gracias por el artículo; nos ha servido muchísimo. Tenemos una consulta sobre el uso de Azure Blob Storage en conjunto con el Drupal; debido a que al ser un container entonces se perderían los archivos (imágenes, etc.) y demás que los usuarios suben al CMS y que no quedan en la base de datos de Azure SQL. ¿Sabe de algún Drupal module o library de Microsoft Azure que recomiendes para implementar dicha integración? Hemos buscado varias opciones pero aún no hemos dado con una solución.

    Reply

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.