Introducción
Al decidir publicar en un sitio, después de mucho tiempo de no hacerlo, quise probar Nikola. Como iba a funcionar en un droplet que ya se utiliza para otro sitio, quería algo de bajo consumo. Sobre todo, porque no quería ni comentarios, ni ninguna función. Posts con información, puros y duros. Un cambio de cuando usaba Wordpress, que todavía está instalado por ahí, desactivado.
Como este sitio es compartido, no me parecía razonable que todo el que quisiera publicar tuviera que además ocuparse de hacer el build y deploy.
Tampoco quería hacerlo yo. Y resulta que es un caso perfecto para un sistema de integración continua.
Roberto Alsina es un referente en Python, y hace un tiempo viene comentando sobre Gitea y Drone. Así que allá fuimos. Esto es el trabajo de un par de tardes, principalmente por algunas pruebas que bajan imagenes de docker que tardan media hora. Mi conexión hogareña es un Speedy de 2MB nominal, imaginense un fin de semana de cuarentena.
Objetivos
Brindar un editor simple, web, que pueda usar una persona normal, y que el resultado se publique en la web en un tiempo razonable. El sitio no es de misión crítica y cualquier problema tendría que resolverlo yo.
El editor de Gitea cumple muy bien esta función. Y Drone se encarga de compilar y desplegar al sitio. Estos corren en un Intel NUC, con Docker. El despliegue es a un Apache en un droplet de DigitalOcean.
Implementación
Gitea
Para Gitea, seguí la documentación de Docker. El único cambio fueron los puertos, para no tener conflictos con otros servicios. Ver aquí.
Una vez instalado, creé usuarios para todos. Después, una organización para separar el repositorio, y creé un equipo para los usuarios que vamos a editar el sitio.
Creé un usuario especial para Drone, y le dí permisos al repositorio. Durante las pruebas lo hice admin, no sé si es realmente necesario. Si es importante crearlo antes de instalar Drone, porque necesitamos la autorización de app para que ambos sistemas se conecten.
Drone
Para instalar Drone, seguí la guía, con algunos cambios importantes.
Para hacer el despliegue al servidor de Apache, Drone usa un paso con ssh. Para que esto funcione, el repositorio en Drone debe estar marcado como Trusted. Me costó entender por qué no me aparecía. Resulta que la documentación indica cómo configurar Drone para integrarse con Gitea, pero no crea un usuario admin. Si no es admin, no aparece la opción Trusted.
En la configuración de Docker, agregar en las environment variables la opción para crear un usuario admin. El usuario debería ser el mismo que creamos antes en Gitea.
DRONE_USER_CREATE=username:<drone>,admin:true
Esto va a hacer que el usuario (drone, en mi caso) pueda marcar el repositorio como Trusted. Como usuario normal puede activar un repositorio, pero no aparece la opción Trusted, que es importante para después usar ssh.
Cambié también la configuración de puertos, para evitar conflictos con otros servicios.
Una vez que está corriendo Drone, navegar a la dirección del servidor. Esto va a redirigir primero a Gitea para autorizar el acceso, y luego mostrará los repositorios a los que el usuario Drone tiene acceso, para activarlos. Una vez activados, configurarlos como Trusted.
Para que Drone pueda empezar a hacer algo con ese repositorio, necesita un archivo .drone.yaml en el repositorio, que tiene la configuración de los pasos. Esto, más adelante.
Apache
En el servidor donde funciona Apache, creé un usuario drone. Le creé una llave ssh y copié la clave pública de ssh para que pueda conectarse sin necesidad de contraseña, y de manera más segura. Copié la clave privada al servidor donde está Drone.
Lo copié a /root/drone/id_rsa, y cambié los permisos a solo root (600)
sudo mkdir /root/drone
sudo cp id_rsa /root/drone/
sudo chown root:root /root/drone/id_rsa
sudo chmod 600 /root/drone/id_rsa
Esto va a ser importante después, porque el plugin ssh de Drone tiene un problema y sólo toma la clave privada desde un archivo, no desde la configuración de secrets en el repositorio. Da error y uno no sabe por qué hasta buscar que a todos les pasa lo mismo.
Además, en mi caso creé una carpeta bajo /var/www/ que va a tener el sitio, y activé el sitio en /etc/apache2/sites-available. Le dí permisos al usuario para escribir en esa carpeta.
La configuración de Apache para el sitio es muy simple.
<VirtualHost *:443>
ServerName qasl.com.ar:443
DirectoryIndex index.html
DocumentRoot /var/www/qasl.com.ar
ErrorLog /var/log/apache2/qasl-error.log
CustomLog /var/log/apache2/qasl-access.log combined
ServerAlias qasl.com.ar
SSLCertificateFile /etc/letsencrypt/live/qasl.com.ar/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/qasl.com.ar/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
También agregué un certificado de Lets Encrypt. Una vez configurado Apache y actualizado el dns, corrí certbot con el plugin de Apache y dejé que lo detecte automático. En pocos minutos estaba corriendo el sitio de prueba de Nikola.
.drone.yaml
Este archivo es el que indica los pasos que tiene que seguir Drone para compilar y desplegar. Acá está el actual para el sitio:
---
kind: pipeline
name: default
steps:
- name: build
image: python:3
commands:
- apt update && apt install -y node-less
- pip install -r requirements.txt
- pip install lessc
- nikola build
- name: deploy
image: appleboy/drone-scp
volumes:
- name: ssh_key
path: /root/id_rsa
settings:
host:
from_secret: server_url
username:
from_secret: server_user
key_path: /root/id_rsa
target: /home/drone/qasl.com.ar/
source: output/*
rm: true
- name: final deploy
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/id_rsa
settings:
host:
from_secret: server_url
username:
from_secret: server_user
key_path: /root/id_rsa
script:
- rm -Rf /var/www/qasl.com.ar/*
- cp -Rf /home/drone/qasl.com.ar/output/* /var/www/qasl.com.ar/
volumes:
- name: ssh_key
host:
path: /root/drone/id_rsa
La secuencia es:
- En una imagen de Python3, actualizar apt e instalar node-less. Esto es una dependencia para el tema bnw que usa el sitio
- Instalar nikola y sus dependencias, detallados en requirements.txt. Esto lo obtuve instalando Nikola en un virtualenv y corriendo pip freeze -l > requirements.txt
- Instalar lessc desde pip
- Compilar el sitio con Nikola
- Copiar el resultado, que está en output/ al servidor de Apache, a una carpeta en el home de drone
- Copiar de esa carpeta a la carpeta en /var/www/
Algunas consideraciones
Al final de la configuración está volumes. Esto apunta a donde está la clave pública en el host. Luego hace referencia a ese volume por nombre cuando necesita incluir la key para conectarse por ssh.
En varios puntos aparece from_secret:. Esto hace referencia a secrets definidos en el repositorio. Es muy útil, justamente cuando uno quiere compartir la configuración, pero no los datos privados
Esto copia todos los archivos cada vez, así que no es escalable en el tiempo si el sitio crece en tamaño. Lo siguiente es usar rsync, para copiar sólo lo que cambió.