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!