Tutorial

Cómo instalar Drupal con Docker Compose

Published on May 21, 2020
Español
Cómo instalar Drupal con Docker Compose

El autor seleccionó a la Fundación de las Naciones Unidas para recibir una donación como parte del programa Write for DOnations.

Kathleen Juell escribió la versión original de WordPress de este tutorial.

Introducción

Drupal es un sistema de administración de contenidos (CMS) escrito en PHP y se distribuye bajo la Licencia Pública General de GNU de código abierto. Personas y organizaciones en todo el mundo utilizan Drupal para impulsar sitios gubernamentales, blogs personales, empresas y más. Lo que distingue a Drupal de otras estructuras CMS es su creciente comunidad y un conjunto de funciones que incluyen procesos seguros, rendimiento fiable, modularidad y flexibilidad para adaptarse.

Drupal requiere la instalación de las pilas LAMP (Linux, Apache, MySQL y PHP) o LEMP (Linux, Nginx, MySQL y PHP), pero la instalación de componentes individuales es una tarea larga. Podemos utilizar herramientas como Docker y Docker Compose para simplificar el proceso de instalación de Drupal. En este tutorial, se utilizarán imágenes de Docker para instalar componentes individuales en los contenedores de Docker. Al utilizar Docker Compose, podemos definir y administrar varios contenedores para la base de datos, la aplicación y la conexión/comunicación entre ellos.

En este tutorial, instalaremos Drupal utilizando Docker Compose para poder aprovechar el uso de contenedores e implementar nuestro sitio web de Drupal en los servidores. Ejecutaremos contenedores para una base de datos de MySQL, un servidor web Nginx y Drupal. También protegeremos nuestra instalación obteniendo certificados TLS/SSL con Let’s Encrypt para el dominio que queremos asociar a nuestro sitio. Por último, configuraremos una tarea cron para renovar los certificados de modo que nuestro dominio permanezca seguro.

Requisitos previos

Para este tutorial, necesitaremos lo siguiente:

  • Un servidor con Ubuntu 18.04, un non-root user con privilegios sudo y un firewall activo. Para obtener información sobre cómo configurarlos, consulte esta guía de configuración inicial para servidores.
  • Docker instalado en su servidor conforme a los pasos 1 y 2 de Cómo instalar y usar Docker en Ubuntu 18.04. Este tutorial se probó en la versión 19.03.8.
  • Docker Compose instalado en su servidor conforme el paso 1 de Cómo instalar Docker Compose en Ubuntu 18.04. Este tutorial se probó en la versión 1.21.2.
  • Un nombre de dominio registrado. Para este tutorial, se utilizará your_domain en todo momento. Puede obtener un ejemplar gratis en Freenom o utilizar el registrador de dominios que desee.
  • Los dos registros DNS que se indican a continuación se configuraron para su servidor. Puede seguir esta introducción al DNS de DigitalOcean para obtener información sobre cómo agregarlos a una cuenta de DigitalOcean, si usa una:
    • Un registro A con your_domain orientado a la dirección IP pública de su servidor.
    • Un registro A con www.your_domain orientado a la dirección IP pública de su servidor.

Paso 1: Definir la configuración del servidor web

Antes de ejecutar cualquier contenedor, debemos definir la configuración de nuestro servidor web de Nginx. En nuestro archivo de configuración, se incluirán algunos bloques de ubicación específicos de Drupal y un bloque de ubicación para dirigir las solicitudes de verificación de Let´s Encrypt al cliente Certbot, a fin de renovar los certificados de manera automática.

Primero, crearemos un directorio del proyecto para nuestra configuración de Drupal que denominaremos drupal:

  1. mkdir drupal

Diríjase al directorio que acaba de crear:

  1. cd drupal

Ahora podemos crear un directorio para nuestro archivo de configuración:

  1. mkdir nginx-conf

Abra el archivo con nano o su editor de texto favorito:

  1. nano nginx-conf/nginx.conf

En este archivo, añadiremos un bloque de servidor con directivas para el nombre del servidor y el root del documento, bloques de ubicación para dirigir la solicitud de certificados del cliente de Certbot, procesamiento de PHP y solicitudes de recursos estáticos.

Agregue el siguiente código al archivo. Asegúrese de sustituir your_domain por su propio nombre de dominio:

~/drupal/nginx-conf/nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name your_domain www.your_domain;

    index index.php index.html index.htm;

    root /var/www/html;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass drupal:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

Nuestro bloque de servidor incluye la siguiente información:

Directivas:

  • listen: indica a Nginx que escuche en el puerto 80, lo que nos permitirá utilizar el complemento webroot de Certbot para nuestras solicitudes de certificados. Tenga en cuenta que aún no incluiremos el puerto 443; actualizaremos nuestra configuración para incluir SSL una vez que hayamos obtenido nuestros certificados de manera exitosa.

  • server_name: define el nombre de nuestro servidor y el bloque de servidor que debería utilizarse para las solicitudes a nuestro servidor. Asegúrese de sustituir your_domain en esta línea por su propio nombre de dominio.

  • index: la directiva index define los archivos que se utilizarán como índices al procesar las solicitudes a nuestro servidor. En este caso, modificamos el orden de prioridad predeterminado moviendo index.php antes de index.html para que Nginx priorice los archivos llamados index.php cuando sea posible.

  • root: nuestra directiva root nombra el directorio root para las solicitudes a nuestro servidor. Este directorio, /var/www/html, se crea como punto de montaje en el momento de la compilación siguiendo las instrucciones de nuestro Drupal Dockerfile. Estas instrucciones de Dockerfile también garantizan que los archivos de la versión de Drupal se monten en este volumen.

  • rewrite: si la expresión regular especificada (^/core/authorize.php/core/authorize.php(.*)$) coincide con una solicitud URI, el URI se modifica según lo especificado en la cadena de sustitución (/core/authorized.php$1).

Bloques de ubicación:

  • location ~ /.well-known/acme-challenge: este bloque de ubicación manejará las solicitudes al directorio .well-known, donde Certbot creará un archivo temporal para validar la resolución del DNS de nuestro dominio en nuestro servidor. Una vez implementada esta configuración, podremos utilizar el complemento webroot de Certbot para obtener los certificados para nuestro dominio.

  • location /: en este bloque de ubicación, utilizaremos una directiva try_files para buscar archivos que coincidan con solicitudes del URI individuales. Sin embargo, en lugar de devolver un estado 404 Not Found por defecto, pasaremos el control al archivo index.php de Drupal con los argumentos de solicitud.

  • location ~ \.php$: este bloque de ubicación se encargará del procesamiento de PHP y servirá como proxy de estas solicitudes a nuestro contenedor drupal. Debido a que nuestra imagen de Docker de Drupal se basará en la imagen php:fpm, también incluiremos opciones de configuración que son específicas para el protocolo FastCGI en este bloque. Nginx requiere un procesador de PHP independiente para solicitudes de PHP: en nuestro caso, estas solicitudes estarán a cargo del procesador php-fpm que está incluido con la imagen php:fpm. Además, este bloque de ubicación incluye directivas, variables y opciones específicas de FastCGI que actuarán como proxy para las solicitudes a la aplicación de Drupal que se ejecuten en nuestro contenedor Drupal, establecerán el índice preferido para el identificador URI de la solicitud analizada, y analizarán las solicitudes de URI.

  • location ~ /\.ht: este bloque se encargará de los archivos .htaccess, ya que Nginx no los proporcionará. La directiva deny_all garantiza que los archivos de .htaccess nunca se proporcionarán a los usuarios.

  • location = /favicon.ico, location = /robots.txt: estos bloques garantizan que las solicitudes a /favicon.ico y /robots.txt no se registren.

  • location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: este bloque desactiva el registro para solicitudes de activos estáticos y garantiza que estos activos tengan altas condiciones de almacenamiento en caché, ya que normalmente su mantenimiento es costoso.

Para obtener más información sobre proxy de FastCGI, consulte el artículo Información e implementación de proxy de FastCGI en Nginx. Para obtener información sobre los bloques de servidor y ubicación, consulte el artículo Información sobre algoritmos de selección de bloques de servidores y ubicación de Nginx.

Guarde y cierre el archivo cuando concluya la edición.

Una vez implementada su configuración de Nginx, puede crear variables de entorno para pasarlas a su aplicación y contenedores de bases de datos en tiempo de ejecución.

Paso 2: Definir variables de entorno

Nuestra aplicación de Drupal necesita una base de datos (MySQL, PostgresSQL, etc.) para guardar la información relacionada con el sitio. El contenedor de Drupal necesitará acceso a ciertas variables de entorno al momento de ejecución para acceder al contenedor de la base de datos (MySQL). Estas variables contienen la información confidencial, como las credenciales de la base de datos, por lo que no podemos exponerlas directamente en el archivo de Docker Compose, el archivo principal que contiene información sobre cómo se ejecutarán nuestros contenedores.

Siempre se recomienda establecer los valores confidenciales en el archivo .env y restringir su circulación. Esto impedirá que estos valores se copien en los repositorios de nuestro proyecto y se expongan al público.

En el directorio principal del proyecto, ~/drupal, cree y abra un archivo denominado .env:

  1. nano .env

Agregue las siguientes variables al archivo .env y sustituya las secciones resaltadas con las credenciales que quiere utilizar:

~/drupal/.env
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=drupal
MYSQL_USER=drupal_database_user
MYSQL_PASSWORD=drupal_database_password

Hemos agregado una contraseña para la cuenta administrativa root de MySQL, además de nuestro nombre de usuario y contraseña preferidos para la base de datos de nuestra aplicación.

Nuestro archivo .env contiene información confidencial, por lo que siempre se recomienda incluirlo en los archivos .gitignore y .dockerignore de un proyecto para que no se añada en nuestros repositorios de Git e imágenes de Docker.

Si planea trabajar con Git para el control de versiones, inicialice su directorio de trabajo actual como un repositorio con git init:

  1. git init

Abra el archivo .gitignore:

  1. nano .gitignore

Agregue lo siguiente:

~/drupal/.gitignore
.env

Guarde el archivo y ciérrelo.

De manera similar, abra el archivo .dockerignore:

  1. nano .dockerignore

Luego, agregue lo siguiente:

~/drupal/.dockerignore
.env
.git

Guarde el archivo y ciérrelo.

Ahora que hemos tomado medidas para proteger nuestras credenciales como variables de entorno, vayamos al siguiente paso para definir nuestros servicios en un archivo docker-compose.yml.

Paso 3: Definir servicios con Docker Compose

Docker Compose es una herramienta para definir y ejecutar aplicaciones de Docker de varios contenedores. Definimos un archivo YAML para configurar los servicios de nuestra aplicación. Un servicio en Docker Compose es un contenedor en ejecución, y Compose nos permite vincular estos servicios con volúmenes y redes compartidos.

Crearemos contenedores diferentes para la aplicación, la base de datos y el servidor web de Drupal. Además, también crearemos un contenedor para ejecutar Certbot, a fin de obtener los certificados para nuestro servidor web.

Cree un archivo docker-compose.yml:

  1. nano docker-compose.yml

Agregue el siguiente código para definir la versión del archivo de Compose y el servicio de base de datos mysql:

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

Analicemos cada uno con todas las opciones de configuración del servicio mysql:

  • image: especifica la imagen que se utilizará/extraerá para crear el contenedor. Siempre se recomienda utilizar la imagen con la etiqueta de la versión adecuada, y excluir la etiqueta latest para evitar conflictos futuros. Puedes obtener más información sobre las Prácticas recomendadas de Dockerfile en la documentación de Docker.

  • container_name: sirve para definir el nombre del contenedor.

  • command: se utiliza para anular el comando predeterminado (instrucción CMD) en la imagen. MySQL admite diferentes complementos de autenticación, pero mysql_native_password es el método tradicional para autenticar. Ya que PHP, y por lo tanto, Drupal, no admite la autenticación más reciente de MySQL, debemos establecer --default-authentication-plugin=mysql_native_password como el mecanismo de autenticación predeterminado.

  • restart: se utiliza para definir la política de reinicio del contenedor. La política unless-stopped reinicia un contenedor a menos que se detenga manualmente.

  • env_file: añade las variables de entorno desde un archivo. En nuestro caso, leerá las variables de entorno del archivo .env que definimos en el paso anterior.

  • volumes: monta rutas host o volúmenes con nombre, que se especificaron como subopciones de un servicio. Vamos a montar un volumen con nombre denominado db-data al directorio /var/lib/mysql en el contenedor, donde por defecto MySQL escribirá sus archivos de datos.

  • networks: define la red interna a la que se unirá nuestro servicio de aplicación. Definiremos las redes al final del archivo.

Ya hemos definido la definición de nuestro servicio mysql, así que ahora agregaremos la definición del servicio de la aplicación de drupal al final del archivo:

~/drupal/docker-compose.yml
...
  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

En esta definición del servicio, nombraremos nuestro contenedor y definiremos una política de reinicio, tal como lo hicimos con el servicio mysql. También añadiremos algunas opciones específicas para este contenedor:

  • image: aquí utilizamos la imagen de Drupal 8.7.8-fpm-alpine. Esta imagen tiene el procesador php-fpm que requiere nuestro servidor web de Nginx para administrar el procesamiento de PHP. Además, utilizamos la imagen alpine, proveniente del proyecto Alpine Linux, que reducirá el tamaño de la imagen en general y cuyo uso se aconseja en las prácticas recomendadas de Dockerfile. Drupal tiene más versiones de imágenes que puedes descubrir en Dockerhub.

  • depends_on: se utiliza para expresar dependencia entre los servicios. Definir el servicio mysql como la dependencia a nuestro contenedor drupal garantizará que nuestro contenedor drupal se cree después del contenedor mysql y habilitará nuestra aplicación para que se inicie sin problemas.

  • networks: aquí hemos agregado este contenedor a la red externa junto con la red interna. Esto garantizará que nuestro servicio mysql sea accesible únicamente desde el contenedor drupal a través de la red interna, y al mismo tiempo mantendrá este contenedor accesible para otros contenedores por medio de la red externa.

  • volumes: montaremos un volumen con nombre denominado drupal-data al punto de montaje /var/www/html creado por la imagen de Drupal. Usar un volumen con nombre de esta manera nos permitirá compartir el código de nuestra aplicación con otros contenedores.

A continuación, añadiremos la definición del servicio de Nginx después de la definición del servicio de drupal:

~/drupal/docker-compose.yml
...
  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

De nuevo, nombraremos nuestro contenedor y haremos que dependa del contenedor de Drupal por orden de inicio. También utilizaremos una imagen alpine: la imagen de Nginx 1.17.4-alpine.

Esta definición de servicio también incluye las siguientes opciones:

  • ports: expone el puerto 80 para habilitar las opciones de configuración que definimos en nuestro archivo nginx.conf en el paso 1.

  • volumes: aquí definimos tanto el volumen con nombre como la ruta host:

    • drupal-data:/var/www/html: montará el código de nuestra aplicación de Drupal al directorio /var/www/html, el que configuramos como root en nuestro bloque de servidor de Nginx.

    • ./nginx-conf:/etc/nginx/conf.d: montará el directorio de configuración de Nginx en el host al directorio pertinente del contenedor, lo cual garantizará que cualquier cambio que realicemos en los archivos del host se refleje en el contenedor.

    • certbot-etc:/etc/letsencrypt: montará los certificados y las claves pertinentes de Let’s Encrypt para nuestro dominio al directorio correspondiente del contenedor.

    • networks: hemos definido la red externa para que este contenedor solo se comunique con el contenedor drupal y no con el contenedor mysql.

Por último, agregaremos la última definición del servicio para el servicio certbot. Asegúrese de sustituir sammy@your_domain y your_domain por su correo electrónico y nombre de dominio:

~/drupal/docker-compose.yml
...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

Esta definición indica a Compose que introduzca la imagen certbot/certbot de Docker Hub. También utiliza volúmen con nombre para compartir recursos con el contenedor de Nginx, incluidos los certificados de dominio y la clave en certbot-etc, y el código de la aplicación en drupal-data.

También hemos utilizado depends_on para asegurarnos de que el contenedor certbot se inicie tras la ejecución del servicio del servidor web.

Aquí no hemos especificado ninguna red, porque este contenedor no se comunicará a ningún servicio a través de la red. Solo añade los certificados de dominio y la clave, que hemos montado utilizando los volúmenes con nombre.

También incluimos una opción de command que especifica un subcomando que debe ejecutarse con el comando de certbot predeterminado del contenedor. El cliente de Certbot admite complementos para obtener e instalar certificados. Incluimos certonly y --webroot en la línea de comandos para utilizar el complemento webroot para obtener un certificado. Puede encontrar más información sobre el complemento y los comandos adicionales en la documentación oficial de Certbot.

Debajo de la definición de servicio certbot, agregue las definiciones de red y volumen:

~/drupal/docker-compose.yml
...
networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

La clave networks de nivel superior nos permite especificar las redes que debemos crear. networks permite la comunicación entre los servicios/contenedores en todos los puertos, ya que están en el mismo host de demonio de Docker. Hemos definido dos redes: internal y external, para proteger la comunicación de los servicios del servidor web, drupal y mysql.

La clave volumes se utiliza para definir los volúmenes con nombre drupal-data, db-data y certbot-etc. Cuando Docker crea volúmenes, el contenido del volumen se almacena en un directorio del sistema de archivos host, /var/lib/docker/volumes/, que Docker administra. El contenido de cada volumen luego se monta desde este directorio en cualquier contenedor que utilice el volumen en cuestión. De esta manera, es posible compartir códigos y datos entre contenedores.

El archivo docker-compose.yml terminado tendrá este aspecto:

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

Ya terminamos de definir nuestros servicios. A continuación, iniciaremos el contenedor y probaremos nuestras solicitudes de certificado.

Paso 4: Obtener certificados y credenciales SSL

Podemos iniciar nuestros contenedores con el comando docker-compose up, que creará y ejecutará nuestros contenedores en el orden que especificamos. Si las solicitudes de nuestros dominios tienen éxito, veremos el estado de salida correcto en nuestro resultado y los certificados correctos montados en la carpeta /etc/letsencrypt/live del contenedor del servidor web.

Para ejecutar los contenedores en segundo plano, utilice el comando docker-compose up con el indicador -d:

  1. docker-compose up -d

Verá un resultado similar que confirmará la creación de sus servicios:

Output
... Creating mysql ... done Creating drupal ... done Creating webserver ... done Creating certbot ... done

Compruebe el estado de los servicios utilizando el comando docker-compose ps:

  1. docker-compose ps

Veremos los servicios mysql, drupal y webserver con un State (estado) de valor Up, a la vez que certbot arrojará un mensaje de estado con valor 0:

Output
Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp

Si ve algo diferente de Up en la columna State para los servicios mysql, drupal o webserver, o un estado de salida distinto a 0 para el contenedor certbot, asegúrese de verificar los registros de servicio con el comando docker-compose logs:

  1. docker-compose logs service_name

Ahora podemos verificar que nuestros certificados estén montados en el contenedor webserver utilizando el comando docker-compose exec:

  1. docker-compose exec webserver ls -la /etc/letsencrypt/live

Esto generará el siguiente resultado:

Output
total 16 drwx------ 3 root root 4096 Oct 5 09:15 . drwxr-xr-x 9 root root 4096 Oct 5 09:15 .. -rw-r--r-- 1 root root 740 Oct 5 09:15 README drwxr-xr-x 2 root root 4096 Oct 5 09:15 your_domain

Ahora que todo se ejecuta correctamente, podemos editar la definición de nuestro servicio de certbot para eliminar el indicador --staging.

Abra el archivo docker-compose.yml, vaya a la definición de servicio de certbot y sustituya el indicador --staging en la opción de comando por el indicador --force-renewal, el cual indicará a Certbot que quiere solicitar un nuevo certificado con los mismos dominios que un certificado existente. La definición actualizada de certbot tendrá este aspecto:

~/drupal/docker-compose.yml
...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
...

Tenemos que ejecutar docker-compose up de nuevo para recrear el contenedor de certbot. También incluiremos la opción --no-deps para indicar a Compose que puede omitir el inicio del servicio webserver, dado que ya está en ejecución:

  1. docker-compose up --force-recreate --no-deps certbot

Veremos un resultado que indicará que nuestra solicitud de certificado fue exitosa:

Output
Recreating certbot ... done Attaching to certbot certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log certbot | Plugins selected: Authenticator webroot, Installer None certbot | Renewing an existing certificate certbot | Performing the following challenges: certbot | http-01 challenge for your_domain certbot | http-01 challenge for www.your_domain certbot | Using the webroot path /var/www/html for all unmatched domains. certbot | Waiting for verification... certbot | Cleaning up challenges certbot | IMPORTANT NOTES: certbot | - Congratulations! Your certificate and chain have been saved at: certbot | /etc/letsencrypt/live/your_domain/fullchain.pem certbot | Your key file has been saved at: certbot | /etc/letsencrypt/live/your_domain/privkey.pem certbot | Your cert will expire on 2020-01-03. To obtain a new or tweaked certbot | version of this certificate in the future, simply run certbot certbot | again. To non-interactively renew *all* of your certificates, run certbot | "certbot renew" certbot | - Your account credentials have been saved in your Certbot certbot | configuration directory at /etc/letsencrypt. You should make a certbot | secure backup of this folder now. This configuration directory will certbot | also contain certificates and private keys obtained by Certbot so certbot | making regular backups of this folder is ideal. certbot | - If you like Certbot, please consider supporting our work by: certbot | certbot | Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate certbot | Donating to EFF: https://eff.org/donate-le certbot | certbot exited with code 0

Una vez que hayamos generado correctamente nuestros certificados, podemos actualizar nuestra configuración de Nginx para incluir SSL.

Paso 5: Modificar la configuración del servidor web y la definición del servicio

Después de instalar los certificados SSL en Nginx, tendremos que redirigir todas las solicitudes de HTTP a HTTPS. También tendremos que especificar las ubicaciones de nuestro certificado SSL y de la clave, y agregar encabezados y parámetros de seguridad.

Debido a que recreará el servicio webserver para incluir estas adiciones, puede detenerlo ahora:

  1. docker-compose stop webserver

Esto generará el siguiente resultado:

Output
Stopping webserver ... done

A continuación, vamos a eliminar el archivo de configuración de Nginx que creamos anteriormente:

  1. rm nginx-conf/nginx.conf

Abra otra versión del archivo:

  1. nano nginx-conf/nginx.conf

Añada el siguiente código al archivo para redireccionar HTTP a HTTP y para agregar credenciales, protocolos y encabezados de seguridad SSL. Recuerde sustituir your_domain por su dominio:

~/drupal/nginx-conf/nginx.conf
server {
    listen 80;
    listen [::]:80;

    server_name your_domain www.your_domain;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        rewrite ^ https://$host$request_uri? permanent;
    }
}
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name your_domain www.your_domain;

    index index.php index.html index.htm;

    root /var/www/html;

    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass drupal:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }
}

El bloque de servidor HTTP especifica el complemento webroot para solicitudes de renovación de Certbot al directorio .well-known/acme-challenge. También incluye una directiva de reescritura que dirige las solicitudes HTTP al directorio root hacia HTTPS.

El bloque de servidor HTTPS habilita ssl y http2. Para obtener más información sobre la iteración de HTTP/2 en protocolos HTTP y los beneficios que puede tener para el rendimiento de sitios web, consulte la introducción de Cómo configurar Nginx con compatibilidad para HTTP/2 en Ubuntu 18.04.

Estos bloques habilitan SSL, ya que hemos incluido las ubicaciones de nuestro certificado SSL y de la clave junto con los encabezados recomendados. Estos encabezados nos permitirán obtener una clasificación A en los sitios de prueba de servidores de SSL Labs y Security Headers.

Nuestras directivas root e index también se encuentran en este bloque, al igual que el resto de los bloques de ubicación específicos de Drupal que analizamos en el paso 1.

Guarde y cierre el archivo de configuración actualizado de Nginx.

Antes de comenzar a recrear el contenedor de webserver, tendremos que agregar una asignación de puerto 443 a la definición de nuestro servicio de webserver, ya que hemos habilitado los certificados SSL.

Abra el archivo docker-compose.yml:

  1. nano docker-compose.yml

Realice las siguientes modificaciones de la definición de servicio de webserver:

~/drupal/docker-compose.yml
...
  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external
...

Después de habilitar los certificados SSL, nuestro docker-compose.yml se verá de la siguiente manera:

~/drupal/docker-compose.yml
version: "3"

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: unless-stopped
    env_file: .env
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

  drupal:
    image: drupal:8.7.8-fpm-alpine
    container_name: drupal
    depends_on:
      - mysql
    restart: unless-stopped
    networks:
      - internal
      - external
    volumes:
      - drupal-data:/var/www/html

  webserver:
    image: nginx:1.17.4-alpine
    container_name: webserver
    depends_on:
      - drupal
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - drupal-data:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - external

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - drupal-data:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain

networks:
  external:
    driver: bridge
  internal:
    driver: bridge

volumes:
  drupal-data:
  db-data:
  certbot-etc:

Guarde y cierre el archivo. Ahora crearemos el servicio webserver con nuestra configuración actualizada:

  1. docker-compose up -d --force-recreate --no-deps webserver

Esto generará el siguiente resultado:

Output
Recreating webserver ... done

Compruebe los servicios con docker-compose ps:

  1. docker-compose ps

Veremos los servicios de mysql, drupal y webserver como Up, mientras que certbot arrojará un mensaje de estado con valor 0:

Output
Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

Ahora, todos nuestros servicios se están ejecutando y podemos continuar con la instalación de Drupal a través de la interfaz web.

Paso 6: Completar la instalación a través de la interfaz web

Completaremos la instalación mediante la interfaz web de Drupal.

En un navegador web, diríjase al dominio del servidor. Recuerde sustituir aquí your_domain por su propio nombre de dominio:

https://your_domain

Seleccione el idioma que utilizará:

Página Seleccionar idioma en la interfaz web de Drupal

Haga clic en Guardar y continuar. Llegaremos a la página de Perfil de instalación. Drupal tiene varios perfiles, así que seleccione el perfil Estándar y haga clic en Guardar y continuar.

Página Seleccionar perfil en la interfaz web de Drupal

Después de seleccionar el perfil, pasaremos a la página de Configuración de la base de datos. Seleccione el tipo de base de datos MySQL, MariaDB, Percona Server o equivalente y escriba los valores de nombre de la base de datos, usuario y contraseña que se corresponden con los valores de MYSQL_DATABASE, MYSQL_USER y MYSQL_PASSWORD, respectivamente, los cuales definimos en el archivo .env en el paso 2. Haga clic en Opciones avanzadas y utilice el nombre del contenedor de servicio mysql para el valor de Host. Haga clic en Guardar y continuar.

Página Configurar la base de datos en la interfaz web de Drupal

Después de configurar la base de datos, se comenzarán a instalar los módulos y los temas predeterminados de Drupal:

Página Instalar sitio en la interfaz web de Drupal

Cuando se haya instalado el sitio, llegaremos a la página de configuración del sitio de Drupal en donde podremos establecer el nombre del sitio, el correo electrónico, el usuario, la contraseña y la configuración regional. Complete la información y haga clic en Guardar y continuar:

Página Configurar sitio en la interfaz web de Drupal

Después de hacer clic en Guardar y continuar, podemos ver la página Bienvenido a Drupal que muestra que nuestro sitio de Drupal está listo y funciona correctamente.

Página Bienvenido a Drupal en la interfaz web de Drupal

Ahora que completamos la instalación de Drupal,debemos asegurarnos de que nuestros certificados SSL se renueven automáticamente.

Paso 7: Renovar certificados

Los certificados de Let’s Encrypt son válidos durante 90 días, por lo que tenemos que configurar un proceso de renovación automática para asegurarnos de que no caduquen. Una forma de hacerlo es crear una tarea con la utilidad de programación cron. En este caso, crearemos una tarea cron para que de forma periódica ejecute una secuencia de comandos que renovará nuestros certificados y volverá a cargar nuestra configuración de Nginx.

Vamos a crear el archivo ssl_renew.sh para renovar los certificados:

  1. nano ssl_renew.sh

Agregue el siguiente código: Recuerde sustituir el nombre del directorio por su non-root user:

~/drupal/ssl_renew.sh

#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew --dry-run && \
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

Esta secuencia de comandos cambia al directorio del proyecto ~/drupal y ejecuta los siguientes comandos docker-compose.

  • docker-compose run: iniciará un contenedor certbot y anulará el command proporcionado en nuestra definición de servicio certbot. En lugar de usar el subcomando de certonly, utilizaremos aquí el subcomando renew que renovará los certificados que caducarán pronto. En este caso, incluimos la opción --dry-run para probar nuestra secuencia de comandos.

  • docker-compose kill: enviará una señal de SIGHUP al contenedor webserver para volver a cargar la configuración de Nginx.

Cierre el archivo y utilice el siguiente comando para que sea ejecutable:

  1. sudo chmod +x ssl_renew.sh

A continuación, abra el archivo root crontab para ejecutar la secuencia de comandos de renovación en un intervalo especificado:

  1. sudo crontab -e

Si es la primera vez que edita este archivo, se le solicitará elegir un editor de texto para abrir el archivo:

Output
no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/nano 2. /usr/bin/vim.basic 3. /usr/bin/vim.tiny 4. /bin/ed Choose 1-4 [1]: ...

Al final del archivo, agregue la siguiente línea, en la que reemplazará sammy por su nombre de usuario:

crontab
...
*/5 * * * * /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

Esto fijará un intervalo de tarea de cinco minutos, para que podamos probar si la solicitud de renovación funciona como estaba previsto. También creamos un archivo de registro, cron.log para registrar el resultado pertinente de la tarea.

Pasados cinco minutos, utilice el comando tail para verificar cron.log y ver si la solicitud de renovación fue exitosa:

  1. tail -f /var/log/cron.log

Verá un resultado que confirme el éxito de la renovación:

Output
** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Presione CTRL+C para abandonar el proceso tail.

Ahora podemos modificar el archivo crontab para ejecutar la secuencia de comandos el segundo día de todas las semanas a las 2:00. Modifique la última línea de crontab como se muestra:

crontab
...
* 2 * * 2 /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

Guarde y cierre el archivo.

Ahora, eliminaremos la opción --dry-run de la secuencia de comandos ssl_renew.sh. Primero, ábralo:

  1. nano ssl_renew.sh

Luego, modifique el contenido como se muestra a continuación:

~/drupal/ssl_renew.sh
#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew && \
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

Ahora, nuestra tarea cron evitará el vencimiento de los certificados SSL al renovarlos cuando sean elegibles.

Conclusión

En este tutorial, utilizamos Docker Compose para crear una instalación de Drupal con un servidor web de Nginx. Como parte de este flujo de trabajo, obtuvimos los certificados TLS/SSL para el dominio que queríamos asociar a nuestro sitio de Drupal y creamos una tarea cron para renovar los certificados cuando sea necesario.

Si quiere obtener más información sobre Docker, consulte nuestra página temática dedicada a Docker.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
2 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Good tutorial, thank you! I suggest this improvement: The ssl_renew file creates a docker container every time it runs, so you will have a lot of docker containers useless, I added an extra line to that file to delete them:

#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew && \
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver && \
docker rm $(docker ps -qa --no-trunc --filter "ancestor=certbot/certbot")

Me manda el error 502 cuando intento acceder al dominio, estoy lanzando la aplicación en una instancia gratuita de aws. No em funciona el dominio…

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.