WordPress es un sistema de administración de contenido (CMS) gratuito y de código abierto que se desarrolla sobre una base de datos de MySQL con procesamiento PHP. Gracias a su arquitectura de complemento extensible, a su sistema de plantillas y al hecho de que la mayor parte de su administración pueda realizarse a través de la interfaz web, WordPress es una opción popular al crear diferentes tipos de sitios web, desde blogs hasta páginas de productos y sitios de comercio electrónico.
Ejecutar WordPress normalmente implica instalar una pila LAMP (Linux, Apache, MySQL y PHP) o LEMP (Linux, Nginx, MySQL y PHP), lo cual puede llevar mucho tiempo. Sin embargo, utilizando herramientas como Docker y Docker Compose, puede simplificar el proceso de configuración de su pila preferida e instalar WordPress. En lugar de instalar de forma manual componentes individuales, puede utilizar imágenes, que estandarizan aspectos como bibliotecas, archivos de configuración y variables de entorno y ejecutan estas imágenes en contenedores, que son los procesos aislados que se ejecutan en un sistema operativo compartido. Además, al utilizar Compose, puede coordinar varios contenedores, como una aplicación y una base de datos, para comunicarse entre sí.
A través de este tutorial, creará una instalación de WordPress con varios contenedores. Sus contenedores incluirán una base de datos de MySQL, un servidor web de Nginx y el propio WordPress. También protegerá su instalación obteniendo certificados TLS y SSL con Let´s Encrypt para el dominio que desee asociar a su sitio. Por último, configurará una tarea cron
para renovar sus certificados de modo que su dominio permanezca seguro.
Para este tutorial, necesitará lo siguiente:
Un servidor con Ubuntu 18.04, un usuario no root 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.
Docker Compose instalado en su servidor conforme el paso 1 de Cómo instalar Docker Compose en Ubuntu 18.04.
Un nombre de dominio registrado. En este tutorial, se utilizará example.com 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:
example.com
orientado a la dirección IP pública de su servidor.example.com
orientado a la dirección IP pública de su servidor.Antes de ejecutar cualquier contenedor, nuestro primer paso será 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 WordPress, junto con 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, cree un directorio de proyecto para su configuración de WordPress llamado wordpress
y diríjase a este:
- mkdir wordpress && cd wordpress
A continuación, cree un directorio para el archivo de configuración:
- mkdir nginx-conf
Abra el archivo con nano
o su editor favorito:
- nano nginx-conf/nginx.conf
En este archivo, añadiremos un bloque de servidor con directivas para el nombre del servidor y el root de documento, bloques de ubicación para dirigir la solicitud de certificados del cliente de Certbot, procesamiento de PHP, y solicitudes de recursos estáticos.
Pegue el siguiente código en el archivo. Asegúrese de sustituir example.com
por su propio nombre de dominio:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
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;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress: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á usar 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 su servidor y el bloque de servidor que debería utilizarse para las solicitudes a este último. Asegúrese de sustituir example.com
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 su servidor. En este caso, modificamos el orden de prioridad predeterminado moviendo index.php
en frente 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 instrucciones de nuestro WordPress Dockerfile. Estas instrucciones de Dockerfile también garantizan que los archivos de la versión de WordPress se monten en este volumen.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 a fin de obtener certificados para nuestro dominio.location /
: en este bloque de ubicación, utilizaremos una directiva try_files
para buscar archivos que coincidan con solicitudes de URI individuales. Sin embargo, en lugar de devolver un estado 404 Not Found
por defecto, pasaremos el control al archivo index.php
de WordPress 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 de wordpress
. Debido a que nuestra imagen de Docker de WordPress se basará en la imagen php:fpm
, también incluiremos opciones de configuración que sean 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 WordPress que se ejecuten en nuestro contenedor wordpress
, establecerán el índice preferido para la URI de la solicitud analizada y analizarán las solicitudes de URI.location ~ /\.ht
: este bloque se encargará de 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|gifBlog|tox0101jpeg|jpgs|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. Si utiliza nano
, podrá hacerlo presionando CTRL+X
, Y
y luego ENTER
.
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.
Sus contenedores de bases de datos y aplicaciones de WordPress necesitarán acceso a determinadas variables de entorno en tiempo de ejecución a fin de que los datos de su aplicación persistan y esta pueda acceder a ellos. Estas variables incluyen información confidencial y no confidencial: valores confidenciales para su contraseña root de MySQL, y usuario y contraseña de base de datos de aplicación, e información no confidencial para el nombre y host de la base de datos de su aplicación.
En lugar de establecer todos estos valores en nuestro archivo de Docker Compose, el archivo principal que contiene información sobre cómo se ejecutarán nuestros contenedores, podemos establecer los valores confidenciales en un 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 de su proyecto, ~/wordpress
, abra un archivo llamado .env
:
- nano .env
Entre los valores confidenciales que configuraremos en este archivo se incluyen una contraseña para nuestro usuario root de MySQL y un nombre de usuario y contraseña que WordPress usará para acceder a la base de datos.
Añada los siguientes nombres y valores de variables al archivo. Recuerde proporcionar aquí sus propios valores para cada variable:
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password
Hemos incluido una contraseña para la cuenta administrativa root, además de nuestro nombre de usuario y contraseña preferidos para la base de datos de nuestra aplicación.
Guarde y cierre el archivo cuando concluya la edición.
Debido a que su archivo .env
contiene información confidencial, le convendrá asegurarse de que esté incluido en los archivos .gitignore
y .dockerignore
de su proyecto, los cuales indican a Git y Docker qué archivos no deben copiarse a sus repositorios Git e imágenes Docker, respectivamente.
Si planea trabajar con Git para el control de versiones, inicialice su directorio de trabajo actual como un repositorio con git init
:
- git init
A continuación abra un archivo .gitignore
:
- nano .gitignore
Añada .env
al archivo:
.env
Guarde y cierre el archivo cuando concluya la edición.
Asimismo, es una buena medida de precaución añadir .env
a un archivo .dockerignore
, para que no termine en sus contenedores cuando esté usando este directorio como su contexto de compilación.
Abra el archivo:
- nano .dockerignore
Añada .env
al archivo:
.env
Debajo de esto, puede añadir de manera opcional archivos y directorios asociados con el desarrollo de su aplicación:
.env
.git
docker-compose.yml
.dockerignore
Guarde y cierre el archivo cuando termine.
Ahora que estableció su información confidencial, podrá definir sus servicios en un archivo docker-compose.yml
.
Su archivo docker-compose.yml
contendrá las definiciones de servicios para su configuración. Un servicio en Compose es un contenedor en ejecución y las definiciones de servicios especifican información sobre cómo se ejecutará cada contenedor.
Mediante Compose, puede definir diferentes servicios para ejecutar aplicaciones en varios contenedores, ya que Compose le permite establecer un enlace entre estos servicios y redes y volúmenes compartidos. Esto será útil para nuestra configuración actual, ya que crearemos diferentes contenedores para nuestra base de datos, nuestras aplicaciones de WordPress y nuestro servidor web. También crearemos un contenedor para ejecutar el cliente de Certbot, a fin de obtener certificados para nuestro servidor web.
Para comenzar, abra el archivo docker-compose.yml
:
- nano docker-compose.yml
Añada el siguiente código para definir su versión del archivo de Compose y el servicio de base de datos db
:
version: '3'
services:
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
La definición del servicio db
contiene las siguientes opciones:
image
: indica a Compose la imagen que debe obtener para crear el contenedor. En este caso, fijaremos la imagen de mysql:8.0
para evitar futuros conflictos, ya que la imagen mysql:latest
continúa en proceso de actualización. Para obtener más información sobre la fijación de versiones y evitar conflictos de dependencias, consulte la documentación de Docker sobre prácticas recomendadas de Dockerfile.container_name
: especifica un nombre para el contenedor.restart
: define la política de reinicio del contenedor. El valor predeterminado es no
, pero configuramos el contenedor para reiniciarse a menos que se detenga de manera manual.env_file
: esta opción indica a Compose que deseamos añadir variables de entorno de un archivo llamado .env
, ubicado en nuestro contexto de compilación. En este caso, el contexto de compilación es nuestro directorio actual.environment
: esta opción le permite añadir variables de entorno, más allá de las definidas en su archivo .env
. Estableceremos la variable MYSQL_DATABASE
de modo que sea igual a wordpress,
a fin de proporcionar un nombre para la base de datos de nuestra aplicación. Debido a que se trata de información no confidencial, podemos incluirla de forma directa en el archivo docker-compose.yml
.volumes
: aquí montaremos un volumen con nombre llamado dbdata
en el directorio /var/lib/mysql
del contenedor. Este es el directorio de datos estándar para MySQL en la mayoría de las distribuciones.command
: esta opción especifica un comando para anular la instrucción CMD predeterminada para la imagen. En nuestro caso, añadiremos una opción al comando estándar de mysqld
de Docker, que inicia el servidor de MySQL en el contenedor. Esta opción, --default-authentication-plugin=mysql_native_password
, fija la variable de sistema --default-authentication-plugin
en mysql_native_password y especifica
el mecanismo de autenticación que debe regir las nuevas solicitudes de autenticación al servidor. Debido a que PHP y, por lo tanto, nuestra imagen de WordPress no son compatibles con la nueva autenticación predeterminada de MySQL, debemos realizar este ajuste para autenticar el usuario de base de datos de nuestra aplicación.networks
: especifica que nuestro servicio de aplicación se unirá a la red app-network
, que definiremos al final del archivo.A continuación, debajo de su definición de servicio db
, agregue la definición para el servicio de su aplicación wordpress
:
...
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
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:
- wordpress:/var/www/html
networks:
- app-network
En esta definición de servicio, nombraremos nuestro contenedor y definiremos una política de reinicio, como hicimos con el servicio db
. También añadiremos algunas opciones específicas de este contenedor:
depends_on
: esta opción garantiza que nuestros contenedores se inicien por orden de dependencia; el contenedor de wordpress
comenzará después del contenedor de db
. Nuestra aplicación de WordPress depende de la existencia de nuestra base de datos y usuario, por lo que expresar este orden de dependencia permitirá que nuestra aplicación se inicie de forma adecuada.image
: para esta configuración, usaremos la imagen de WordPress 5.1.1-fpm-alpine
. Como se mencionó en el paso 1, usar esta imagen garantiza que nuestra aplicación tenga el procesador php-fpm
que Nginx requiere para manejar el procesamiento de PHP. Esta imagen también es alpine
, derivada del proyecto Alpine Linux, y permitirá que el tamaño de nuestra imagen sea en general reducido. Para obtener más información sobre las ventajas y desventajas del uso de imágenes alpine
y si esto tiene o no sentido para su aplicación, consulte el análisis completo en la sección Variantes de imágenes de la página de imagenes de WordPress de Docker Hub.env_file
: nuevamente, especificaremos que queremos introducir valores de nuestro archivo .env
, ya que es aquí donde definimos el usuario y la contraseña de la base de datos de nuestra aplicación.environment
: aquí, usaremos los valores que definimos en nuestro archivo .env
, pero los asignaremos a los nombres de las variables previstas por la imagen de WordPress: WORDPRESS_DB_USER
y WORDPRESS_DB_PASSWORD
. También definiremos un WORDPRESS_DB_HOST
, que será el servidor de MySQL que se ejecutará en el contenedor db
al que se puede acceder en el puerto predeterminado 3306
de MySQL. Nuestro WORDPRESS_DB_NAME
será el mismo valor que especificamos en la definición del servicio de MySQL para nuestra MYSQL_DATABASE
: wordpress
.volumes
: montaremos un volumen llamado wordpress
en el punto de montaje /var/www/html
creado por la imagen de WordPress. Usar un volumen con nombre de esta manera nos permitirá compartir el código de nuestra aplicación con otros contenedores.networks
: también añadiremos el contenedor wordpress
a la red app-network
.A continuación, debajo de la definición del servicio de la aplicación wordpress
, agregue la siguiente definición para su servicio de Nginx webserver
:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
De nuevo, nombraremos nuestro contenedor y haremos que dependa del contenedor de wordpress
por orden de inicio. También usaremos una imagen alpine
, la imagen 1.15.12-alpine
de Nginx.
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í definiremos una combinación de volúmenes nombrados y montajes “bind”:
wordpress:/var/www/html
: montará el código de nuestra aplicación de WordPress en el directorio /var/www/html
, el que configuramos como root
en nuestro bloque de servidor de Nginx../nginx-conf:/etc/nginx/conf.d
: vinculará mediante montaje “bind” el directorio de configuración de Nginx en el host con el 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 en el directorio correspondiente del contenedor.Una vez más, agregamos este contenedor a la red app-network
.
Por último, debajo de su definición de webserver
, agregue su última definición de servicio para el servicio certbot
. Asegúrese de sustituir la dirección de correo electrónico y los nombres de dominio que aparecen aquí por su propia información:
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com
Esta definición indica a Compose que introduzca la imagen certbot/certbot
de Docker Hub. También utiliza volúmenes nombrados para compartir recursos con el contenedor de Nginx, incluidos los certificados de dominio y la clave de certbot-etc
, y el código de la aplicación en wordpress
.
Una vez más, usamos depends_on
para especificar que el contenedor certbot
debe iniciarse una vez que el servicio webserver
esté en ejecución.
También incluimos una opción de command
que especifica un subcomando que debe ejecutarse con el comando de certbot
predeterminado del contenedor. El subcomando certonly
obtendrá un certificado con las siguientes opciones:
--webroot:
indica a Cerbot que utilice el complemento webroot a fin de colocar archivos en la carpeta webroot para la autenticación. Este complemento depende del método de validación HTTP-01, que utiliza una solicitud HTTP para probar que Certbot puede acceder a los recursos de un servidor que responda a un nombre de dominio determinado.--webroot-path
: esto especifica la ruta del directorio webroot.--email
: su correo electrónico preferido para el registro y la recuperación.--agree-tos
: especifica que acepta el Acuerdo de suscripción de ACME.--no-eff-email
: indica a Certbot que usted no desea compartir su correo electrónico con la Electronic Frontier Foundation (EFF). Puede omitirlo si lo prefiere.--staging
: indica a Certbot que desea utilizar el entorno de configuración de Let’s Encrypt para obtener certificados de prueba. Utilizar esta opción le permite probar sus opciones de configuración y evitar posibles límites vinculados a solicitudes de dominio. Para obtener más información sobre estos límites, consulte la documentación sobre los límites de tasas de Let’s Encrypt.-d:
le permite especificar los nombres de dominio que desee aplicar a su solicitud. En este caso, incluimos example.com
y www.example.com
. Asegúrese de sustituirlos por su propio dominio.Debajo de la definición de servicio certbot
, agregue sus definiciones de red y volumen:
...
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Nuestra clave de alto nivel volumes
define los volúmenes certbot-etc
, wordpress
y dbdata
. Cuando Docker crea volúmenes, el contenido del volumen se almacena en un directorio del sistema de archivos host, /var/ib/docker/volume/
, 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.
La red de puente definida por el usuario app-network
permite la comunicación entre nuestros contenedores, ya que están en el mismo host de demonio de Docker. Esto agiliza el tráfico y la comunicación dentro de la aplicación, ya que abre todos los puertos entre contenedores en la misma red de puente sin exponer ningún puerto al mundo exterior. Por lo tanto, nuestros contenedores db
, wordpress
y webserver
pueden comunicarse entre sí y solo debemos exponer el puerto 80
para el acceso de front-end a la aplicación.
El archivo docker-compose.yml
terminado tendrá este aspecto:
version: '3'
services:
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
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
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:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Guarde y cierre el archivo cuando concluya la edición.
Una vez configuradas las definiciones de servicio, estará listo para iniciar los contenedores y probar las solicitudes de su certificado.
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 webserver
.
Cree los contenedores con docker-compose up
y el indicador -d
, que ejecutarán los contenedores db
, wordpress
y webserver
en segundo plano:
- docker-compose up -d
Verá un resultado que confirmará la creación de sus servicios:
OutputCreating db ... done
Creating wordpress ... done
Creating webserver ... done
Creating certbot ... done
Mediante docker-compose ps
, compruebe el estado de sus servicios:
- docker-compose ps
Si todo se realizó correctamente, el estado de sus servicios de db
, wordpress
y webserver
será Up
y el contenedor certbot
se habrá cerrado con el mensaje de estado 0
:
Output Name Command State Ports
-------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Si ve algo diferente de Up
en la columna State
para los servicios db
, wordpress
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
:
- docker-compose logs service_name
Ahora podrá verificar que sus certificados se hayan montado en el contenedor webserver
con docker-compose exec
:
- docker-compose exec webserver ls -la /etc/letsencrypt/live
Si sus solicitudes de certificado se completan de forma correcta, verá un resultado como este:
Outputtotal 16
drwx------ 3 root root 4096 May 10 15:45 .
drwxr-xr-x 9 root root 4096 May 10 15:45 ..
-rw-r--r-- 1 root root 740 May 10 15:45 README
drwxr-xr-x 2 root root 4096 May 10 15:45 example.com
Ahora que sabe que su solicitud será correcta, puede editar la definición de servicio certbot
para eliminar el marcador --staging
.
Abra docker-compose.yml
:
- nano docker-compose.yml
Encuentre la sección del archivo con la definición de servicio certbot
y sustituya el indicador --staging
en la opción command
por el indicador --force-renewal
, el cual indicará a Certbot que desea solicitar un nuevo certificado con los mismos dominios que un certificado existente. Ahora, la definición de servicio certbot
tendrá este aspecto:
...
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com
...
Ahora podrá ejecutar docker-compose
para recrear el contenedor 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:
- docker-compose up --force-recreate --no-deps certbot
Verá un resultado que indicará que su solicitud de certificado fue exitosa:
OutputRecreating 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 example.com
certbot | http-01 challenge for www.example.com
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/example.com/fullchain.pem
certbot | Your key file has been saved at:
certbot | /etc/letsencrypt/live/example.com/privkey.pem
certbot | Your cert will expire on 2019-08-08. 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 implementados sus certificados, podrá modificar su configuración de Nginx para incluir SSL.
Habilitar SSL en nuestra configuración de Nginx implicará añadir un redireccionamiento HTTP a HTTPS especificando nuestras ubicaciones de certificados y claves SSL, y agregando parámetros y encabezados de seguridad.
Debido a que recreará el servicio webserver
para incluir estas adiciones, puede detenerlo ahora:
- docker-compose stop webserver
Antes de modificar el propio archivo de configuración, primero obtendremos los parámetros de seguridad de Nginx recomendados de Certbot usando curl
:
- curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
Con este comando se guardará estos parámetros en un archivo llamado options-ssl-nginx.conf
, ubicado en el directorio nginx-conf
.
A continuación, elimine el archivo de configuración de Nginx que creó anteriormente:
- rm nginx-conf/nginx.conf
Abra otra versión del archivo:
- 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 example.com
por su propio dominio:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]: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-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress: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 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.
En este bloque también se incluyen las ubicaciones de nuestros certificados y claves SSL, junto con los parámetros de seguridad de Certbot recomendados que guardamos en nginx-conf/options-ssl-nginx.conf
.
Además, incluimos algunos encabezados de seguridad que nos permitirán obtener las calificaciones A en aspectos como los sitios de prueba de servidores de SSL Labs y Security Headers. Entre estos encabezados se incluyen X-frame-Options
, X-Content-Type-Options
, Referer Policy
, Content-Security-Policy
y X-XSS-Protection
. El encabezado HTTP Strict Transport Security (
HSTS) no se incluye: habilítelo solo si comprende las implicaciones y evaluó su funcionalidad de “precarga”.
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 WordPress analizados en el paso 1.
Una vez que finalice la edición, guarde y cierre el archivo.
Antes de recrear el servicio webserver
, deberá añadir una asignación del puerto 443
a su definición de servicio webserver
.
Abra su archivo docker-compose.yml
:
- nano docker-compose.yml
En la definición de servicio webserver
, agregue la siguiente asignación de puerto:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
El archivo docker-compose.yml
tendrá este aspecto al terminar la operación:
version: '3'
services:
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
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
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:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Guarde y cierre el archivo cuando concluya la edición.
Recree el servicio webserver
:
- docker-compose up -d --force-recreate --no-deps webserver
Compruebe sus servicios con docker-compose ps
:
- docker-compose ps
Debería ver un resultado que indique que sus servicios db
, wordpress
y webserver
se encuentran en ejecución:
Output Name Command State Ports
----------------------------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db 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
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Una vez que sus contenedores se encuentren en ejecución, podrá completar su instalación de WordPress a través de la interfaz web.
Al contar con nuestros contenedores en ejecución, podemos terminar la instalación a través de la interfaz web de WordPress.
En su navegador web, acceda al dominio de su servidor. Recuerde sustituir aquí example.com
por su propio nombre de dominio:
https://example.com
Seleccione el idioma que desee utilizar:
Después de hacer clic en** Continue**, accederá a la página de configuración principal, en la que deberá elegir un nombre para su sitio y un nombre de usuario. Se recomienda elegir aquí un nombre de usuario fácil de recordar (en lugar de “admin”) y una contraseña segura. Puede usar la contraseña que WordPress genera de manera automática o crear una propia.
Por último, deberá ingresar su dirección de correo electrónico y decidir si quiere evitar que los motores de búsqueda indexen su sitio:
Al hacer clic sobre Install WordPress en la parte inferior de la página, accederá a una solicitud de inicio de sesión:
Una vez que inicie sesión, tendrá acceso al panel de administración de WordPress:
Una vez completada su instalación de WordPress, podrá tomar medidas para asegurarse de que sus certificados SSL se renueven de forma automática.
Los certificados de Let’s Encrypt son válidos durante 90 días, por lo que le convendrá configurar un proceso de renovación automática para asegurarse 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.
Primero, abra una secuencia de comandos llamada ssl_renew.sh
:
- nano ssl_renew.sh
Agregue el siguiente código a la secuencia de comandos para renovar sus certificados y volver a cargar la configuración de su servidor web. No se olvide de sustituir el nombre de usuario de ejemplo por su nombre de usuario no root:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Esta secuencia de comandos primero asigna el binario docker-compose
a una variable llamada COMPOSE
y especifica la opción --no-ansi
, que ejecutará comandos docker-compose
sin los caracteres de control ANSI. Luego hace lo mismo con el binario docker
. Luego, cambia el posicionamiento al directorio del proyecto ~/wordpress
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
, usaremos aquí el subcomando renew
que renovará 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. Si desea obtener más información sobre el uso de este proceso para volver a cargar su configuración de Nginx, consulte este post del blog de Docker sobre la implementación de la imagen oficial de Nginx con Docker.Luego ejecuta docker system prune
para eliminar todos los contenedores y las imágenes que no se utilizan.
Cierre el archivo cuando finalice la edición. Haga que sea ejecutable:
- chmod +x ssl_renew.sh
A continuación, abra su archivo root crontab
para ejecutar la secuencia de comandos de renovación en un intervalo especificado:
- sudo crontab -e
Si es la primera vez que edita este archivo, se le solicitará elegir un editor:
Outputno crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed
Choose 1-4 [1]:
...
Al final del archivo, añada la siguiente línea:
...
*/5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Esto fijará un intervalo de tarea de cinco minutos, de modo que puede probar si su solicitud de renovación ha funcionado como estaba previsto. También creamos un archivo de registro, cron.log
para registrar el resultado pertinente de la tarea.
Una vez que transcurran cinco minutos, revise cron.log
para comprobar si la solicitud de renovación se realizó con éxito o no:
- tail -f /var/log/cron.log
Debería 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/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ahora podrá modificar el archivo de crontab
para establecer un intervalo diario. Para ejecutar la secuencia de comandos cada día al mediodía, por ejemplo, se debería modificar la última línea del archivo de modo que tenga el siguiente aspecto:
...
0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
También le convendrá eliminar la opción --dry-run
de su secuencia de comandos ssl_renew.sh
:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Su tarea cron
controlará que sus certificados de Let´s Encrypt no caduquen al renovarlos cuando reúnan las condiciones. También puede configurar la rotación de registros con la utilidad Logrotate para rotar y comprimir sus archivos de registro.
A través de este tutorial, utilizó Docker Compose para crear una instalación de WordPress con un servidor web de Nginx. Como parte de este flujo de trabajo, obtuvo certificados TLS y SSL para el dominio que quiere asociar con su sitio de WordPress. Además, creó una tarea cron
para renovar estos certificados cuando sea necesario.
Como pasos adicionales para mejorar el rendimiento y la redundancia del sitio, puede consultar los siguientes artículos sobre la entrega y el respaldo de activos de WordPress:
Si está interesado en explorar un flujo de trabajo en contenedor con Kubernetes, también puede consultar lo siguiente:
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
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!
Excelente guía. Quería saber si esta implementación se puede usar en producción o si debería agregarle algo más.
Primero muchas gracias por el tutorial, soy nuevo con docker y todo este tipo de cosas. Mi consulta es sobre el paso 4. Al correr: docker-compose ps Name Command State Ports
certbot certbot certonly --webroot … Exit 1
Certbot tiene Exit con 1.
Cambié los domains a mis dominios y el error que me devuelve certbot al correr es: certbot | - The following errors were reported by the server: certbot | certbot | Domain: estudiointegrala.com.ar certbot | Type: dns certbot | Detail: DNS problem: NXDOMAIN looking up A for certbot | estudiointegrala.com.ar - check that a DNS record exists for this certbot | domain certbot | certbot | Domain: www.estudiointegrala.com.ar certbot | Type: dns certbot | Detail: DNS problem: NXDOMAIN looking up A for certbot | www.estudiointegrala.com.ar - check that a DNS record exists for certbot | this domain
¿Que tengo que configurar localmente para poder usar los dominios con certbot?
Hola, buen tutorial Hace un mes lo seguí y tengo mi wordpress funcionando, ahora me gustaría añadir otro en el mismo VPS. Habría algún problema en replicar el mismo tutorial en una ruta distinta ? y cambiando usuario de la base de datos.
Muchas gracias
Gracias por compartir tus conocimientos con este post tan detallado
Podrías decirme que habría que hacer para ver wordpress en la red local a través de cualquier navegador (Ej: 192.168.0.120)
También me gustaría definirle una ip fija al contenedor para luego abrirlo en el router y hacerlo público.
Saludos
Hola, buen tutorial!
Está todo perfecto pero parece que el enlace a los parámetros de seguridad de Nginx recomendados de Certbot está roto.
(edito): lo he localizado en la siguiente ruta: https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf