Introducción #
En esta entrada, explicaré cómo lograr un despliegue automatizado tras un cambio en un sitio web creado con Hugo y desplegado en Ubuntu Server con Apache gracias a Webhook.
Existen muchos servicios de hosting que permiten desplegar gratuitamente una página web creada con Hugo. Esto ya se explica en la documentación oficial. Para la mayoría, usar un servicio de hosting externo es la mejor opción, pero ¿qué ocurre si ya tienes un servidor?
Mi plan era alojar mi sitio web personal en mi propio servidor: Nubecita. Este servidor utiliza Ubuntu Server 24.04 y Apache como servidor web. Una de las combinaciones más comunes para alojar contenido en internet. Además, tengo mi sitio web subido a un repositorio en GitHub. Mi objetivo era automatizar el proceso de despliegue, de modo que cada vez que subo un nuevo cambio al repositorio, se implemente en el servidor web de producción.
Esta guía está diseñada para este tipo de configuración. Espero que a alguien le resulte útil.
Empezando por el principio #
Antes de empezar a configurar los servicios y escribir los scripts, configuraremos nuestro sistema. Necesitaremos instalar Webhook. Optaré por la versión empaquetada de los repositorios, en lugar de la de la Snap Store:
sudo apt install webhookActualización: Necesitarás Webhook >= 2.8.2, ya que esa versión incluye todos los cambios necesarios para ejecutarse con Systemd.
Además, no recomiendo usar Hugo de la Snap Store, ya que te obligaría a tener tus scripts de despliegue en un directorio del sistema en lugar de tu directorio personal. Sin embargo, la decisión es tuya; ten en cuenta que no puedes usar Hugo de la Snap Store para seguir esta guía.
Script de deployment básico #
Una vez que tengamos nuestro sistema listo, podemos comenzar con la parte interesante. Necesitaremos un script de despliegue, que no es más que un script que automatiza todos los pasos que realizarías si hicieras un despliegue manual. Para esta guía, incluiré el script que utilizo, pero tendrás que modificarlo según tus necesidades.
#!/bin/bash
echo "Retrieve new content from the repo..."
git clone https://github.com/felipet/personal-site.git --depth 1
cd personal-site
echo "Build the static page"
hugo
cd public
echo "Deploy the new content"
rsync -r ./ /var/www/html/felipe-site
chown -R www-data:www-data /var/www/html/felipe-site
systemctl reload apache2
echo "Wiping temporal data..."
cd ../../
rm -rf personal-siteAspectos destacables del script:
-
Añade
--depth 1al comandoclone. Esto ahorrará tiempo si tu repositorio contiene una cantidad considerable de imágenes y otros archivos pesados. -
Tras sincronizar con
rsync, cambio el propietario de los nuevos datos. El usuariowww-datase usa con frecuencia en implementaciones de Apache. Considera si este usuario y grupo son la mejor opción para tu implementación. -
Reinicio el servicio
apache2después de la implementación, por lo que este script debe ser ejecutado por un usuario con privilegios.
Puedes colocar el script donde quieras. Yo elegí la ruta /var/scripts y asigné como propietario del directorio al usuario y grupo www-data. Pruébalo y asegúrate de que funciona correctamente.
Configura el Webhook #
Nuestro script de despliegue ya está listo, pero ¿cómo lo ponemos en práctica? Aquí es donde entra en juego Webhook. Añadiremos un nuevo servicio Systemd para gestionar Webhook y escribiremos el archivo de configuración que le indicará a Webhook qué webhook debe desplegarse para nuestro propósito.
Ejecutando Webhook como un servicio de Systemd #
Configuraremos Webhook para que se ejecute como un servicio de Systemd. Esto se explica detalladamente en la documentación oficial, así que solo mencionaré las pequeñas modificaciones que realicé.
En resumen, este es el descriptor del socket:
[Unit]
Description=Webhook server socket
[Socket]
## Listen on one specific interface only
ListenStream=127.0.0.1:9191
[Install]
WantedBy=multi-user.targetComo puedes ver, indiqué que el socket solo escuchará las solicitudes provenientes de la dirección local. Planeo usar un proxy inverso, lo que añade una capa adicional de seguridad, ya que no quiero exponerlo al público. Si decides prescindir del proxy inverso, tendrás que escuchar en 0.0.0.0:9191, pero recuerda que necesitarás configurar una capa de seguridad adicional.
Y el descriptor del servicio:
[Unit]
Description=Webhook server
[Service]
Type=exec
ExecStart=/usr/bin/webhook -nopanic -hooks /var/webhooks/hooks.json -verbose -port 9191
StandardOutput=append:/var/log/webhook
StandardError=append:/var/log/webhook
User=root
Group=rootMuy importante: El puerto de escucha y el puerto que se pasa como argumento al Webhook deben ser el mismo. Puede parecer un consejo básico, pero si no se utilizan los puertos estándar, es posible que se omita la especificación de algún puerto, lo que puede provocar una pérdida de tiempo.
Como ya mencioné, es necesario ejecutar este servicio como root para tener los privilegios suficientes para ejecutar systemctl reload apache2.
Configura un nuevo Webhook #
Quizás hayas notado que pasamos como argumento al Webhook el archivo /var/webhooks/hooks.json. Ahora, analizaremos este archivo.
Elegí la ruta /var/webhooks, pero puedes ubicarla donde prefieras. También le di permisos de lectura y escritura a www-data en esa ruta, ya que mi objetivo es desplegar mi repositorio allí cada vez que se llame al servicio.
Este es el contenido del archivo hooks.json:
[
{
"id": "deploy_felipe_site",
"execute-command": "/var/scripts/deploy_felipe-site.bash",
"command-working-directory": "/var/webhooks",
"trigger-rule":
{
"match":
{
"type": "payload-hmac-sha256",
"secret": "SOME_SECRET_STRING!!!",
"parameter":
{
"source": "header",
"name": "X-Hub-Signature-256"
}
}
}
}
]El valor de execute-command debe ser la ruta a tu script de despliegue. Los valores del campo match se explican en la documentación oficial de GitHub. No olvides reemplazar el valor de secret. Genera una cadena segura aleatoria e insértala. Guárdala por un tiempo, ya que necesitarás introducir la cadena secreta al configurar nuestro Webhook en GitHub.
Para terminar #
¿Listo para empezar? Entonces ejecuta estos comandos:
sudo systemctl enable webhook.socket
sudo systemctl start webhook.socketY comprueba que todo funciona correctamente:
systemctl status webhook.socketDeberías obtener algo similar a este resultado:
● webhook.socket - Webhook server socket
Loaded: loaded (/etc/systemd/system/webhook.socket; enabled; preset: enabled)
Active: active (listening) since Tue 2025-01-14 10:39:37 UTC; 6s ago
Triggers: ● webhook.service
Listen: 127.0.0.1:9191 (Stream)
Tasks: 0 (limit: 37781)
Memory: 8.0K (peak: 256.0K)
CPU: 820us
CGroup: /system.slice/webhook.socket
Jan 14 10:39:37 nubecita.eu systemd[1]: Listening on webhook.socket - Webhook server socket.Configura Apache como un proxy inverso #
Aquí, puede que te desvíes del camino que seguí. Si no te gusta usar un proxy inverso, abre directamente un puerto en tu firewall y ofrece tu servicio al público. En ese caso, te recomiendo que sigas la documentación de Webhook para habilitar HTTPS.
Yo uso Apache para alojar varios sitios web y como proxy inverso para algunos servicios web que ofrezco. Para mí, fue la elección obvia. Evito abrir otro puerto en mi firewall y Apache se encarga de la gestión de SSL, una situación ideal.
Para reenviar las solicitudes al socket de Webhook, añade un nuevo host virtual con el comando ProxyPass:
ProxyPass /webhook http://127.0.0.1:9191/hooks
ProxyPassReverse /webhook http://127.0.0.1:9191/hooksEsto redirigirá una solicitud a https://nubecita.eu/webhook/deploy_felipe_site al socket Webhook local. Si creó un nuevo host virtual, habilítelo. En cualquier caso, reinicie el servicio Apache.
sudo systemctl reload apache2Nuestro backend ya está listo, hagamos una solicitud sencilla para comprobar que responde. ¡Crucemos los dedos!
Podemos usar Curl para enviar una solicitud POST al endpoint donde instalamos nuestro webhook:
curl -X 'POST' \
'https://nubecita.eu/webhook/deploy_felipe_site'
Hook rules were not satisfied.%Como no enviamos nuestras credenciales ni ninguna otra información con la solicitud, nuestro servicio nos indica que no cumplimos con las reglas definidas en hooks.json. Perfecto, nuestro backend responde y bloquea las solicitudes ficticias. Completemos el proceso de implementación con el último paso.
Crea un webhook en GitHub #
Hemos llegado al último paso necesario. Debemos configurar un webhook en nuestro repositorio de GitHub.
El proceso se explica en esta página (https://docs.github.com/en/webhooks/using-webhooks/creating-webhooks). Sin embargo, resumamos los pasos:
- Ve a la sección de configuración del repositorio que contiene el contenido de tu sitio web.
- Abre Webhooks en el panel izquierdo y haz clic en «Añadir webhook».
- Introduce la URL de tu endpoint en «URL de carga útil» y selecciona «application/json» como «Tipo de contenido».
- ¿Recuerdas la cadena secreta? Ahora es el momento de usarla de nuevo. Añádela en el campo «Secreto».
- Por supuesto, activa la verificación SSL.
- Finalmente, elige cuándo quieres que se ejecute el webhook. Yo seleccioné «Envíos» y «Lanzamientos».
Guárdalo y ve a la pestaña «Entregas recientes». Verás que se ha iniciado una comprobación de estado. Si se ha realizado correctamente, ¡enhorabuena! El proceso de despliegue está listo.
Realiza cualquier cambio en el contenido del repositorio y envíalo. Verás que los cambios se han desplegado en tu sitio web de producción.