Introducción
WordPress es uno de los sistemas de gestión de contenidos (CMS) más populares que existen. Estadísticamente, impulsa más del 39% de todos los sitios web que ves en la red mundial. Es una opción popular debido a su extensibilidad a través de plugins y su flexible sistema de plantillas. Te permite cambiar su apariencia en segundos. Además, su administración se puede realizar a través de la interfaz web sin requerir muchos conocimientos técnicos.
Además, WordPress es gratuito y de código abierto y está construido sobre una MySQL base de datos con procesamiento PHP. Puedes desplegar WordPress en una pila LAMP (Linux, Apache, MySQL y PHP) o una pila LEMP (Linux, Nginx, MySQL y PHP). Sin embargo, resulta lento configurar la pila cada vez que se quiere desplegar.
Afortunadamente, los métodos modernos de entrega de software como la computación en la nube, Docker, y Docker Compose han facilitado la experiencia general del desarrollador. Estas herramientas simplifican el proceso de configuración de cualquier stack al evitar la sobrecarga de instalar y configurar componentes individuales cada vez que se desea implementar una aplicación. En su lugar, se escriben archivos de configuración que se utilizarán para descargar y crear imágenes y ejecutarlas en contenedores Docker, lo que permite implementar la aplicación con un solo comando.
Los contenedores son entornos estandarizados, ligeros, virtualizados, portátiles y definidos por software que permiten que el software se ejecute de forma aislada de otro software que se ejecuta en la máquina host física. Docker Compose le permite administrar múltiples contenedores y garantizar que se comuniquen. Por ejemplo, el código fuente de una aplicación y una base de datos deben comunicarse.
En este tutorial, estaremos construyendo una aplicación de WordPress multicontenedor. Una aplicación de WordPress completa requiere tres contenedores: la base de datos MySQL, el servidor Nginx y el código fuente de WordPress. Siendo la seguridad una prioridad en los sitios web modernos, obtendremos un certificado SSL de Let’s Encrypt para asegurar su instalación. Luego, configuraremos una tarea cron para comprobar y renovar periódicamente los certificados, de modo que la seguridad de su sitio web se mantenga de forma continua.
Requisitos previos
- Como este es un tutorial práctico, debe tener una instalación de Ubuntu 20.04 como su entorno operativo inicial. También debe tener un usuario no root con privilegios sudo. Aquí tiene un tutorial paso a paso para ayudarle a configurar su servidor Ubuntu.
- También necesita instalar Docker. Puede consultar este tutorial sobre cómo instalar y utilizar Docker en Ubuntu 18.04.
- Una instalación de Docker Compose. Puede seguir el Paso 1 del tutorial Cómo instalar y configurar Docker Compose en Ubuntu 20.04.
- Se requiere un nombre de dominio registrado para obtener un certificado TLS/SSL de Let’s Encrypt. Para los fines de este tutorial, utilizaremos
example.com. - Configure los registros DNS para dirigir el tráfico a su VPS. Necesita dos registros DNS:
- Un registro A con
example.comque apunte a la dirección IP pública de su servidor. - Un registro A con
www.example.comque apunte a la dirección IP pública de su servidor.
- Un registro A con
Paso 1: Definir las configuraciones para el servidor web
El servidor web aloja los archivos de su sitio web y permite a los usuarios acceder a su aplicación web. Por lo tanto, es lógico que en el primer paso definamos la configuración para el servidor web. Definiremos un archivo de configuración de servidor Nginx que incluirá bloques de ubicación específicos de WordPress. También incluiremos bloques de ubicación para dirigir las solicitudes de verificación de Let’s Encrypt al cliente Certbot para las renovaciones automáticas del certificado.
Comencemos por crear un directorio para el proyecto. Puede elegir el nombre de directorio que prefiera. Usaremos wordpress_docker para este tutorial. Introduzca el siguiente comando para crear el directorio y acceder a él:
|
1 |
mkdir wordpress_docker && cd wordpress_docker |
A continuación, cree un directorio para alojar los archivos de configuración de Nginx con el comando:
|
1 |
mkdir nginx-conf |
Utilice nano para abrir el archivo con el siguiente comando:
|
1 |
nano nginx-conf/nginx.conf |
En este archivo, definiremos directivas básicas para la configuración de un bloque de servidor Nginx. Estas incluyen directivas para el nombre del servidor, la raíz del documento y bloques de ubicación para dirigir las solicitudes del complemento Certbot para certificados, archivos estáticos y procesamiento de PHP. Puede leer nuestro tutorial sobre Cómo asegurar Nginx con Let’s Encrypt para obtener más información. Agregue el siguiente código al archivo, reemplazando example.com con su nombre de dominio registrado:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
servidor { escuchar 80; escuchar [::]:80; servidor_nombre ejemplo.com www.ejemplo.com; índice índice.php índice.html índice.htm; raíz /var/www/html; ubicación ~ /.well-known/acme-challenge { permitir todo; raíz /var/www/html; } ubicación / { intentar_archivos $uri $uri/ /index.php$es_argumentos$argumentos; } ubicación ~ \.php$ { intentar_archivos $uri =404; fastcgi_dividir_ruta_información ^(.+\.php)(/.+)$; fastcgi_pasar aplicación:9000; fastcgi_índice índice.php; incluir fastcgi_parámetros; fastcgi_parámetro SCRIPT_FILENAME $documento_raíz$fastcgi_script_nombre; fastcgi_parámetro PATH_INFO $fastcgi_ruta_información; } ubicación ~ /\.ht { denegar todo; } ubicación = /favicon.ico { registro_no_encontrado desactivado; acceso_registro desactivado; } ubicación = /robots.txt { registro_no_encontrado desactivado; acceso_registro desactivado; permitir todo; } ubicación ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { expira máx; registro_no_encontrado desactivado; } } |
Definamos las secciones que has agregado:
-
Directivas:
listen: le indica a Nginx que escuche en el puerto80. Esto permite el uso del webroot de Certbot para realizar solicitudes de certificados. Una vez que hayamos obtenido un certificado SSL, actualizaremos esta configuración para usar el puerto443.server_name: esto define el nombre de dominio para el cual debe responder esta configuración. El tráfico hacia el nombre de dominio definido aquí se dirigirá a este bloque de servidor en particular y, por lo tanto, al documentoraíz.root: define el directorio raíz para las solicitudes al nombre de dominio anterior. Generalmente es el directorio que contiene los archivos reales de nuestro sitio web. Hemos configurado el directorio en este/var/www/html. Se creará como un punto de montaje de Docker durante el tiempo de construcción del contenedor. Definiremos las instrucciones para este proceso dentro del Dockerfile de WordPress.index: esto define los archivos que se utilizarán como índices o el punto de entrada a su servidor web al procesar solicitudes. Hemos movido index.php antes de index.html para que Nginx prioriceindex.php.
-
Bloques Location:
location ~ /.well-known/acme-challenge: maneja las solicitudes al directorio conocido donde Certbot agrega un archivo temporal para validar que el DNS para el dominio especificado apunta al servidor particular del cual estamos solicitando los certificados SSL. Es por esto que debes agregar un dominio válido para que este paso funcione en lugar delexample.comque estamos usando en este tutorial.location /: toma las solicitudes URI y le da el control a WordPressindex.phppara solicitar argumentos para el procesamiento.location ~ \.php$: maneja el procesamiento de PHP y pasa la solicitud al contenedor de WordPress (definiremos un archivo de configuración para esto en un paso posterior). Hemos definido configuraciones específicas para el protocolo FastCGI aquí porque la imagen de Docker de WordPress se basará en la imagen php:fpm. Nginx utiliza un procesador PHP independiente para las solicitudes específicas de PHP. Utilizaremos el procesadorphp-fpmque viene con la imagen de Dockerphp:fpm.location ~ /\.ht: maneja los archivos.htaccessque Nginx no utiliza. La directivadeny allgarantiza que estos archivos nunca se sirvan a los visitantes del sitio web.location = /favicon.ico, location = /robots.txt: como se ve en la definición, esto evita el registro de solicitudes a/favicon.icoy/robots.txtarchivos.location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: desactiva el registro de solicitudes a archivos estáticos y garantiza que se almacenen en caché para reducir la carga en el servidor.
Ahora puede guardar y cerrar el archivo presionando CTRL+X, Y, luego ENTER. Eso completa el primer paso.
Paso 2: Definir variables de entorno
Las variables de entorno son necesarias para facilitar la comunicación entre la aplicación de WordPress y la base de datos. También garantizan que los datos de la aplicación se conserven. Las variables de entorno incluyen información confidencial, como las credenciales de la base de datos, e información no confidencial, como el nombre de la base de datos y el host.
Por motivos de seguridad, siempre es una buena idea no añadir información confidencial a los repositorios del proyecto. Por lo tanto, en lugar de establecer los valores confidenciales en el archivo Docker Compose, definiremos las credenciales de MySQL dentro del .env archivos que no se confirmarán en el repositorio del proyecto y corren el riesgo de quedar expuestos públicamente. Dentro del proyecto raíz ~/wordpress_docker abra el .env archivo:
|
1 |
nano .env |
|
1 2 3 |
MYSQL_ROOT_PASSWORD=su_contraseña_segura_de_root MYSQL_USER=su_usuario_de_base_de_datos_de_wordpress MYSQL_PASSWORD=contraseña_segura_de_base_de_datos_de_wordpress |
Lo siguiente que debe hacer es añadir el archivo .env a los archivos .gitignore y .dockerignore para asegurarse de que no se agregue a sus repositorios o imágenes de Docker respectivamente.
Esto no es necesario para este tutorial, pero si desea trabajar con Git para el control de versiones, introduzca el siguiente comando para inicializar el directorio actual como un repositorio de git:
|
1 |
git init |
Abra el archivo .gitignore con nano:
|
1 |
nano .gitignore |
Agrega la siguiente línea:
|
1 |
.env |
Guarda y cierra el archivo. A continuación, abre el .dockerignore con nano:
|
1 |
nano .dockerignore |
Agrega la siguiente línea:
|
1 |
.env |
Ya que estás en ello, opcionalmente puedes agregar otros archivos y directorios asociados con el desarrollo de tu aplicación:
|
1 2 3 |
.env .git docker-compose.yml |
Guarda y cierra el archivo cuando termines. Eso es todo para este paso. Pasemos a la definición de Docker Compose.
Paso 3: Configurar servicios con Docker Compose
Docker Compose utiliza un docker-compose.yml archivo para construir imágenes. Este archivo contiene definiciones de servicios para la configuración completa de una aplicación. Las definiciones de servicios son básicamente instrucciones sobre cómo se ejecutará un contenedor. Un servicio es un contenedor real en ejecución.
Docker Compose hace posible definir diferentes servicios para aplicaciones de múltiples contenedores vinculando los diversos servicios entre sí con redes y volúmenes compartidos. Verá esto en acción ya que definiremos tres contenedores para nuestra aplicación: servidor web, instalación de WordPress y base de datos. Añadiremos un cuarto contenedor para ejecutar el cliente Certbot para las renovaciones de certificados.
Introduzca el siguiente comando para crear el archivo docker-compose.yml:
|
1 |
nano docker-compose.yml |
La primera línea de un archivo docker-compose.yml es la definición de versión de línea. Hemos establecido 3 para la nuestra. Luego, puede comenzar a definir sus servicios. Agregue el siguiente fragmento de código en el archivo para definir el db servicio:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
versión: '3' servicios: #Servicio MySQL db: imagen: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network |
Analicemos lo que tenemos en las definiciones del servicio db a continuación:
image: determina la imagen en la que se basará el contenedor. Siempre es mejor especificar una versión específica (mysql:8.0) que usar la etiqueta latest (mysql:latest) ya que las versiones futuras de imágenes de MySQL pueden entrar en conflicto con nuestra aplicación si llegamos a reconstruir esta imagen. Puede encontrar más información sobre las buenas prácticas de Dockerfiles en la documentación oficial de Dockerfile.container_name: aquí especificamos el nombre del contenedor.restart: esta directiva determina el comportamiento de reinicio del contenedor. El valor predeterminado esnopero lo hemos configurado para que siempre sereiniciea menos que se detenga manualmente.env_file: esta directiva se utiliza para especificar la ubicación del archivo con las variables de entorno (.env) utilizado por nuestra aplicación.environment: se utiliza para especificar variables de entorno adicionales. En este tutorial, hemos especificado laMYSQL_DATABASEvariable para contener el nombre de la base de datos de nuestra aplicación. El nombre de la base de datos se puede incluir en eldocker-compose.yml.volumes: se utiliza para especificar las ubicaciones de montaje. En nuestro ejemplo, hemos montado un volumen llamado dbdata en el/var/lib/mysqldirectorio del contenedor, que suele ser el directorio de datos estándar para MySQL.command: esta directiva especifica un comando que sobrescribirá la instrucción CMD predeterminada para la imagen. Hemos añadido una opción al comandomysqldestándar de la imagen de Docker que inicia el servidor MySQL dentro del contenedor. La opción que hemos añadido es--default-authentication-plugin=mysql_native_password, que actualiza el plugin de autenticación predeterminado de MySQL para usar la autenticación por contraseña (mysql_native_password). Esto es necesario para que su aplicación PHP (WordPress) funcione, ya que utilizan un nombre de usuario y una contraseña para acceder a la base de datos. En las versiones más recientes de MySQL, el el plugin de autenticación predeterminado ha cambiado. Sin embargo, la mayoría de las aplicaciones utilizan la autenticación por contraseña. Por lo tanto, debe cambiar esta configuración para que la aplicación funcione.networks: esta directiva se utiliza para especificar que el serviciodbdebe unirse a laapp-network, que definiremos a medida que avancemos en el tutorial.
A continuación, definamos la configuración del servicio para nuestra aplicación WordPress. Llamaremos al servicio y container_name app. Agregue el siguiente fragmento de código debajo de la definición del servicio db, teniendo en cuenta la sangría adecuada:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#Servicio de código de aplicación de WordPress app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app restart: a menos que-detenido env_file: .env environment: - WORDPRESS_DB_HOST=db:3306 - WORDPRESS_DB_USER=$MYSQL_USER - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD - WORDPRESS_DB_NAME=wordpress volumes: - app:/var/www/html networks: - app-network |
Al igual que hicimos con el servicio db, hemos nombrado nuestro contenedor y definido la política de reinicio. Algunas opciones más que agregamos se definen a continuación:
depends_on: esta directiva asegura que los contenedores se inicien en orden de dependencia. En nuestro caso, el contenedorappdepende del contenedordb. Por lo tanto, se iniciará después del contenedordbel contenedor se haya iniciado. Esto debe suceder en este orden porque la aplicación WordPress depende de la disponibilidad de una base de datos MySQL para funcionar.image: como se ve en el fragmento de código, utilizaremos WordPress versión 5.1.1 fpm alpine imagen. Ya habíamos explicado sobre elphp-fpmprocesador que Nginx requiere para el procesamiento de PHP. Esta imagen se encarga de eso. La imagen alpine basada en el proyecto Alpine Linux ayuda a mantener el tamaño de la imagen más pequeño. Si necesita más información sobre las variaciones de la imagen, puede seguir este enlace para imágenes de WordPress en Docker Hub.env_file: especifica la ubicación del.envarchivo que contiene las credenciales de la base de datos.environment: esta directiva define variables de entorno adicionales. En nuestro caso, estamos definiendo las variables que WordPress espera y asignándoles los valores de las variables de nuestro.envarchivo. Estas sonWORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD, yWORDPRESS_DB_HOSTque se refiere al servidor MySQL que se ejecuta en eldbcontenedor, accesible desde el puerto predeterminado de MySQL3306. Finalmente, verás elWORDPRESS_DB_NAMEque hemos establecido en WordPress. El mismo valor se especifica en la definición del servicio MySQL en el contenedor db:MYSQL_DATABASE=wordpress.volumes: esta directiva monta un volumen llamado app en el punto de montaje/var/www/html, creado por la imagen de WordPress. El nombramiento de volúmenes permite compartir el código de la aplicación con otros contenedores.networks: finalmente, agregamos el contenedor app a laapp-networkpara asegurar que se comunique con otros contenedores en la red.
Eso será todo para el contenedor de servicio app para la imagen de WordPress. Ahora definamos el servicio webserver para la imagen de Nginx. Primero, agrega el siguiente fragmento de código debajo de la definición del servicio app en tu archivo docker-compose.yml:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#Servicio de servidor web Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
Ya hemos explicado el depends_on opción. En el caso de este servicio webserver, el contenedor se iniciará después de que el app contenedor se haya iniciado. El contenedor del servidor web se basa en la imagen alpine Nginx. Tiene una política de reinicio similar a las definiciones de servicio anteriores. Las otras opciones en la definición del servicio webserver incluyen:
ports: vincula los puertos entre la máquina host y el contenedor. En el Paso 1, habíamos definido el puerto80en el archivonginx.conf. Este puerto está mapeado al puerto80en el contenedor.volumes: tenemos una combinación de bind mounts y volúmenes con nombre bajo esta opción:app:/var/www/html: esta definición de volumen monta la aplicación WordPress en el/var/www/htmldirectorio que anteriormente habíamos establecido como raíz en el bloque de servidor de Nginx../nginx-conf:/etc/nginx/conf.d: esta definición monta mediante bind el directorio de configuración de Nginx en la máquina host al directorio de configuración de Nginx que definimos para el contenedor. Por lo tanto, cualquier cambio en la máquina host se refleja automáticamente en el contenedor.certbot-etc:/etc/letsencrypt: esta definición monta los certificados y claves de Let’s Encrypt para el dominio en el directorio correspondiente del contenedor.
redes: al igual que en las definiciones de servicios anteriores, la directivanetworksagrega el servicio webserver a laapp-networks.
Dado que hemos terminado con la definición de webserver, agreguemos instrucciones para el servicio Certbot. Esto se encargará de obtener sus certificados TLS/SSL de Let’s Encrypt. Si desea saber más sobre cómo proteger un servidor Nginx, este tutorial sobre cómo proteger Nginx con Let’s Encrypt es una buena fuente.
A continuación, agregue el siguiente fragmento de código debajo del servicio webserver. Recuerde configurar su nombre de dominio y dirección de correo electrónico correctos:
|
1 2 3 4 5 6 7 8 9 10 |
#servicio certbot certbot: depends_on: - servidor web imagen: certbot/certbot container_name: certbot volúmenes: - certbot-etc:/etc/letsencrypt - app:/var/www/html comando: certonly --webroot --webroot-ruta=/var/www/html --correo electrónico hackins@cloudsigma.com --aceptar-términos de servicio --no-eff-correo electrónico --staging -d ejemplo.com -d www.example.com |
La certbot imagen se iniciará solo después de que el webserver se haya iniciado, debido a la directiva depends_on . Docker Compose descargará la imagen de Certbot desde Docker Hub como se define.
Bajo la definición de volúmenes, el contenedor de Certbot compartirá los certificados de dominio y la clave en certbot-etc con el webserver de Nginx y el código de la aplicación con el contenedor app .
Bajo la definición command, hemos especificado un subcomando para ejecutar el comando certonly predeterminado de Certbot del contenedor con opciones adicionales como se enumeran a continuación:
-
--webroot: especifica el uso del complemento webroot que coloca archivos en la carpeta webroot para la autenticación.--webroot-path: especifica la ruta del directorio webroot.--agree-tos: especifica que acepta los Términos de servicio de ACME.--no-eff-email: especifica que no desea compartir su correo electrónico con EFF. Puede omitir esto si desea compartirlo.--staging: indica a Certbot que primero desea obtener certificados de prueba del entorno de staging de Let’s Encrypt para probar su configuración antes de obtener el certificado real. Let’s Encrypt tiene límites de frecuencia de solicitudes de dominio. Por lo tanto, probar primero su configuración le ayudará a evitar que su dominio sea limitado.-d: esta opción toma los nombres de dominio para la solicitud de certificado. En este tutorial, hemos incluidoexample.comywww.example.com. Por favor, especifique su dominio registrado real.
Nuestro docker-compose.yml está casi completo. Sin embargo, también debe agregar las definiciones de red y volumen debajo del servicio Certbot:
|
1 2 3 4 5 6 7 8 9 10 |
#Volúmenes volumes: certbot-etc: app: dbdata: #Redes networks: app-network: driver: bridge |
El volumes define los volúmenes que se compartirán con todos los servicios (contenedores) definidos en este archivo compose: certbot-etc, app, y dbdata. El contenido de los volúmenes que crea Docker se almacena en un directorio gestionado por Docker en el sistema de archivos del host: /var/lib/docker/volumes/. El contenido de cada volumen se monta luego en cualquier contenedor que use el volumen. Esto hace posible compartir datos y código entre contenedores.
La clave networks define la red de puente que permite la comunicación entre contenedores. Los contenedores en la misma red de puente como webserver y db pueden comunicarse de forma segura a través de puertos sin exponer el tráfico a la red externa. Solo exponemos el puerto 80 para permitir el acceso a las páginas web del front-end.
El archivo completo docker-compose.yml se verá así:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
version: '3' services: #Servicio MySQL db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network #Servicio de código de la aplicación WordPress app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app restart: unless-stopped env_file: .env environment: - WORDPRESS_DB_HOST=db:3306 - WORDPRESS_DB_USER=$MYSQL_USER - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD - WORDPRESS_DB_NAME=wordpress volumes: - app:/var/www/html networks: - app-network #Servicio de servidor web Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #Servicio certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com #Volúmenes volumes: certbot-etc: app: dbdata: #Redes networks: app-network: driver: bridge |
Puedes guardar y cerrar el archivo. En el siguiente paso, iniciaremos y probaremos el contenedor y las solicitudes de certificado.
Paso 4: Ejecución de los contenedores y obtención de certificados SSL
La mayor ventaja de Docker Compose es que, una vez que haya definido todos sus servicios en el docker-compose.yml archivo, puede iniciar todos los contenedores con un solo comando: docker-compose up. El comando ejecuta cada instrucción especificada. Si las solicitudes de dominio son correctas, debería poder ver el estado de salida correcto en su terminal. Introduzca el siguiente comando para crear los contenedores. El -d es un indicador para ejecutar los contenedores en segundo plano:
|
1 |
docker-compose up -d |
Si ve la salida como en la captura de pantalla de abajo, entonces los servicios se crearon correctamente:
Para confirmar el estado de los servicios, ejecute el docker-compose ps comando:
|
1 |
docker-compose ps |
La salida del comando es como se muestra a continuación si todo se realizó correctamente. El estado de los contenedores app, db, y webserver debería ser 'up', y el contenedor certbot debería tener el estado Exit0:
Si ve algo que no sea Up en la columna de estado para app, db o webserver, o un estado de Exit que no sea 0 para el certbot contenedor, entonces algo salió mal. Puedes revisar los logs de cada contenedor usando el comando docker-compose logs, y especifica el service_name:
|
1 |
docker-compose logs service_name |
Por ejemplo, puedes revisar los logs del certbot contenedor ingresando el siguiente comando:
|
1 |
docker-compose logs certbot |
Para comprobar si los certificados se montaron en el webserver contenedor, usa el docker-compose exec comando:
|
1 |
docker-compose exec webserver ls -la /etc/letsencrypt/live |
Si utilizó un nombre de dominio registrado real que no sea el example.com y las solicitudes de certificado se realizaron correctamente, debería ver una salida similar a esta:
Una vez que haya confirmado que la solicitud de certificado se realizó correctamente, puede editar el archivo docker-compose.yml y eliminar el indicador --staging. Abra el archivo con nano:
|
1 |
nano docker-compose.yml |
Desplácese hacia abajo hasta la sección de definición del servicio Certbot, en la opción de comando y reemplace el indicador --staging por --force-renewal flag. Esto le indica a Certbot que está solicitando la renovación de un certificado para el mismo dominio. La definición de su servicio Certbot ahora debería verse así:
|
1 2 3 4 5 6 7 8 9 10 |
#servicio certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com |
Guarde el archivo cuando haya terminado de editar.
Introduzca el siguiente comando para volver a crear el certbot contenedor. La bandera --no-deps incluida le indica a Compose que omita reiniciar el servicio del servidor web, ya que ya se está ejecutando:
|
1 |
docker-compose up --force-recreate --no-deps Certbot |
El comando muestra la siguiente captura de pantalla, que indica que la solicitud de certificado fue exitosa:
Eso es todo para este paso. En el siguiente paso, modificará el archivo de configuración de Nginx para incluir el certificado SSL.
Paso 5: Habilitar SSL en la configuración de Nginx y la definición del servicio
Para que Nginx sirva tráfico a través de SSL seguro, primero modificará el archivo de configuración de Nginx para agregar una HTTP redirección a HTTPS. Luego, debe especificar las ubicaciones del certificado y de la clave, y finalmente agregar parámetros de seguridad y encabezados.
Antes de modificar el archivo de configuración, debe obtener el parámetros de seguridad recomendados de Nginx desde el repositorio de GitHub de Certbot usando curl con el siguiente comando:
|
1 |
curl -sSLo nginx-conf/options-ssl-nginx.conf |
El comando se ejecuta y guarda los parámetros que obtiene en un archivo llamado options-ssl-nginx.conf, dentro del nginx-conf directorio. Elimine el archivo de configuración de Nginx para que podamos crear uno nuevo con los siguientes comandos:
|
1 2 |
rm nginx-conf/nginx.conf nano nginx-conf/nginx.conf |
En el archivo ahora vacío nginx.conf, añade el siguiente código que incluye una redirección desde HTTP a los protocolos de credenciales HTTPS, SSL y cabeceras de seguridad. Como has hecho anteriormente, reemplaza el dominio example.com por tu propio dominio registrado:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
servidor { escuchar 80; escuchar [::]:80; nombre_servidor ejemplo.com www.ejemplo.com; ubicación ~ /.bien-conocido/acme-desafío { permitir todo; raíz /var/www/html; } ubicación / { reescribir ^ https://$host$request_uri? permanente; } } servidor { escuchar 443 ssl http2; escuchar [::]:443 ssl http2; server_name example.com www.example.com; index index.php index.html index.htm; root /var/www/html; server_tokens off; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; include /etc/nginx/conf.d/options-ssl-nginx.conf; 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-Seguridad-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # habilite la seguridad de transporte estricta solo si comprende las implicaciones location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app: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 desactivado; } } |
En el primer bloque de servidor que maneja solicitudes no seguras utilizando el puerto 80, especificamos el webroot para las solicitudes de renovación de Certbot. También incluimos una directiva de redirección que redirige las solicitudes HTTP a HTTPS.
El segundo bloque de servidor maneja el tráfico seguro HTTPS que llega por el puerto 443. Como puedes ver, también habilitamos SSL y HTTP2. HTTP/2 mejora el rendimiento de tu servidor. Puedes leer más al respecto en la documentación oficial de Nginx sobre HTTP/2.
En este bloque, también hemos especificado que Nginx incluya las ubicaciones del certificado SSL y de la clave, así como los parámetros de seguridad recomendados de Certbot que curl guardó en el directorio nginx-conf/options-ssl-nginx.conf.
Los encabezados de seguridad adicionales sirven para mejorar las calificaciones de su sitio web en sitios de prueba de seguridad como Security Headers y SSL Labs. Puede seguir los enlaces de estos encabezados para obtener más información: X-Frame-Options, Referrer Policy, X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy. Hemos comentado el HTTP Strict Transport Security cabecera (HSTS). Eres libre de leer sobre su funcionalidad de precarga y decidir si deseas habilitarla.
El resto de las directivas, tales como root, index, los bloques de ubicación específicos de WordPress permanecen como se discutió en Paso 1. Ahora puedes guardar y cerrar el archivo cuando hayas terminado de editar.
Ahora que hemos habilitado el HTTPS tráfico que utiliza el puerto 443, también debemos habilitar el puerto en la definición de servicio del servidor web. Introduce el siguiente comando para abrir el docker-compose.yml archivo con nano:
|
1 |
nano docker-compose.yml |
En la sección del servidor web, bajo la opción de puertos, agregue un mapeo para el puerto 443 como se destaca a continuación:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt redes: - aplicación-red |
El archivo completo docker-compose.yml ahora debería verse así:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
versión: '3' servicios: #Servicio MySQL db: imagen: mysql:8.0 nombre_contenedor: db reiniciar: a menos que-detenido archivo_env: .env entorno: - MYSQL_DATABASE=wordpress volúmenes: - dbdata:/var/lib/mysql comando: '--default-authentication-plugin=mysql_native_password' redes: - app-red #Servicio de código de aplicación de WordPress app: depende_de: - db imagen: wordpress:5.1.1-fpm-alpine nombre_contenedor: app reiniciar: a menos que-detenido archivo_env: .env entorno: - WORDPRESS_DB_HOST=db:3306 - WORDPRESS_DB_USER=$MYSQL_USER - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD - WORDPRESS_DB_NAME=wordpress volumes: - app:/var/www/html networks: - app-network #Servicio de servidor web Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #servicio certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com #Volúmenes volumes: certbot-etc: app: dbdata: #Redes networks: app-network: driver: bridge |
Una vez que hayas confirmado que todo es correcto, guarda y cierra el archivo. Después de eso, ejecuta el siguiente comando para recrear el servicio webserver:
|
1 |
docker-compose up -d --force-recreate --no-deps webserver |
|
1 |
docker-compose ps |
Ahora que todos tus contenedores se están ejecutando, es posible proceder con la configuración de WordPress desde la interfaz web.
Paso 6: Completa tu configuración de WordPress desde la interfaz web
Navega al nombre de dominio de tu servidor para continuar con la instalación. Deberías ver la página de inicio de configuración de WordPress. Te da la bienvenida para que elijas tu idioma antes de continuar:
Elige tu idioma y haz clic en Continuar para pasar a la siguiente página:
En esta página, completa el título de tu sitio web, elige un nombre de usuario memorable y una contraseña segura. Se recomienda no usar Admin como tu nombre de usuario por razones de seguridad. Introduce tu correo electrónico y haz clic en el botón Instalar WordPress para comenzar a instalar WordPress.
Una vez que se complete la instalación, se le dirigirá a la pantalla de inicio de sesión donde proporcionará el nombre de usuario y la contraseña que había establecido. Cuando introduzca las credenciales válidas, debería poder ver su escritorio de WordPress:
¡Ha instalado WordPress con éxito! A continuación, debe seguir los pasos para asegurarse de que los certificados SSL se renueven automáticamente.
Paso 7: Configuración de la renovación automática del certificado SSL
Los certificados TLS/SSL de Let’s Encrypt son válidos únicamente por 90 días. Depende de usted crear una configuración de renovación automática para asegurarse de que no expiren. Puede lograr esto creando un script y programándolo con la cron job utilidad. En este paso, le mostraremos cómo crear un script que renovará los certificados. Luego lo programaremos con la utilidad cron job para ejecutarlo periódicamente y renovar los certificados si se están acercando a la fecha de vencimiento.
Dentro del wordpress_docker directorio del proyecto, abra un script llamado ssl_renewer.sh con nano:
|
1 |
nano ssl_renewer.sh |
Agrega el siguiente código al script para manejar la renovación automática y la recarga de la configuración de Nginx. Recuerda reemplazar el nombre de usuario resaltado con tu nombre de usuario no root:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose –ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
En este script, asignamos el docker-compose binario a una variable llamada COMPOSE. También incluimos la –ansi never opción que le dice al script que ejecute docker-compose comandos sin control ANSI caracteres. Además, asignamos el binario de Docker a una variable llamada DOCKER.
A continuación, el script se desplaza al directorio de nuestro proyecto wordpress_docker y ejecuta los siguientes comandos:
docker-compose run: inicia el contenedor certbot y anula el comando que habíamos proporcionado en la definición del servicio certbot. En lugar de ejecutar el subcomando certonly, ejecuta el subcomando renew, que renovará los certificados SSL/TLS de Let’s Encrypt si están a punto de expirar.docker-compose kill: envía una señal SIGHUP al contenedorwebserverpara recargar las configuraciones de Nginx. Es posible que desees consultar este tutorial de Docker sobre cómo usar la imagen oficial de Docker de Nginx.docker system prune: este comando elimina todos los contenedores e imágenes no utilizados.
Guarde y cierre el archivo cuando termine de editarlo. Luego, ejecute el siguiente comando para hacerlo ejecutable:
|
1 |
chmod +x ssl_renewer.sh |
Una vez que lo haya hecho ejecutable, abra su archivo crontab de root para ejecutar el script periódicamente en los intervalos que especificaremos:
|
1 |
sudo crontab -e |
El crontab le pedirá que elija su editor preferido si es la primera vez que lo usa:
Elija su editor preferido y presione Enter para abrir el archivo. Al final del archivo, agregue la siguiente línea:
|
1 |
*/5 * * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
Esto establece el intervalo en cinco minutos para permitirnos probar si nuestro script de renovación funcionará o no. También hemos especificado un archivo de registro que contendrá la salida de la tarea: cron_docker.log.
Espere cinco minutos y revise el cron.log para ver si el script tuvo éxito con la solicitud de renovación:
|
1 |
tail -f /var/log/cron_docker.log |
Deberías ver algo similar a la captura de pantalla de abajo si las solicitudes fueron exitosas:
Ahora que lo hemos probado y confirmado que funciona, puedes modificar el archivo crontab para especificar una renovación diaria. Por ejemplo, es posible que desees especificar que el script se ejecute todos los días a las 6 PM. Para hacer eso, modifica la última línea del crontab para que se vea así:
|
1 |
0 18 * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
Además, debes eliminar la opción –dry-run del ssl_renewer.sh script para asegurar que la renovación real ocurra cuando se ejecute. Debería verse así:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
A continuación, guarde y cierre el archivo. Una vez hecho esto, el cron job mantendrá sus scripts válidos, renovándolos antes de que terminen los 90 días.
Conclusión
Si ha llegado hasta aquí en el tutorial, puede considerarse un paso más cerca de ser un DevOps Engineer. Pudo crear un script de configuración de Nginx, creó un docker-compose.yml archivo, y definió varios servicios necesarios para ejecutar una aplicación WordPress con Docker y Docker Compose. Obtuvo certificados SSL/TLS de Let’s Encrypt para garantizar que su servidor web sea seguro. Finalmente, creó una tarea cron para asegurarse de que los certificados no expiren. ¡Buen trabajo!
Si está intentando profundizar en DevOps, eche un vistazo a más recursos sobre contenedores de nuestro blog:
- Conociendo Kubernetes
- Cómo desplegar una aplicación Node.js (Express.js) con Docker en Ubuntu 20.04
- Desplegar una aplicación PHP en un clúster de Kubernetes con Ubuntu 18.04.
- Desplegando Laravel, Nginx y MySQL con Docker Compose
¡Feliz computación!










Comentarios
Aún no hay comentarios. Sea el primero.