Infraestructura del nuevo blog

Pipeline

Como publicamos ayer, no sólo hemos cambiado la imagen del blog, sino practicamante toda la infraestructura del mismo.

Hace bastante tiempo que tenía en la cabeza la idea de mover el blog de Wordpress a un sitio estático por las razones que ya comentamos.

El primer intento de migración fue usando Jekyll (la versión 3 acababa de salir), quizás el generador de sitiios web estático más popular. Éste tiene gran soporte y la plataforma esta muy bien, pero el problema es que tardaba en generar el blog unos 45 minutos. Todo esto corriendo en un sistema con Ubuntu Linux, 16Gb de RAM, SSD y un procesador Intel i7 quad core. Luego probamos Octopress, con prácticamente el mismo resultado. Ambas plataformas están escritas en Ruby. También probé otra, que no recuerdo el nombre, que estaba escrita en Python, a parte de no mejorar el rendimiento, no era tan versátil como Jekyll. Aquí el problema estaba claro, el languaje de programación. No es secreto alguno, que los lenguajes interpreatados son mucho más lentos que los compilados.

Hugo

Poco después descubrí Hugo, un “Jekyll”, pero escrito en Go. Inicialmente, aunque los resultados no fueron satisfactorios, Hugo era capaz de generar el sitio web en unos 25 minutos. Por lo que le seguí la pista por un tiempo, e iba probando cada versión que se liberaba, hasta que sobre la versión 0.18, Hugo era capaz de generar el sitio web en unos 6 minutos. En ese momento decidimos que era hora de hacer la migración.

Lo siguiente era encontrar un tema que nos gustara y se adaptara al blog. Encontramos Hugo Phlat Theme, el cual nos gustó. Lo adoptamos y lo modificamos un poco a nuestro gusto.

Generación de los ficheros .md a partir de la base de datos de Wordpress

Ya tenemos generador y tema, hora de hacer una migración del wordpress actual y generar el contenido para que Hugo nos genere el sitio. Inicialmente probamos un exporador de Wordpress para Jekyll (recuerda que Hugo está basado en Jekyll y es capaz de entendar las entradas de Jekyll), pero el resultado no fue de mi agrado. Tuvimos problemas con ciertos caracteres en los tags, el título, etc. Empecé a modificar el código PHP, para corregir los problemas que encontraba, pero no me estaba llevando más tiempo del que deseaba pasar arreglando. Así que decidí escribir una pequeña rutina de exportación accediendo directamente a la base de datos. Para ello hice un backup de la base de datos actual de cyberhades, me cree un contenedor Docker con MySQL, restauré el backup en el contenedor y ejecuté el código de exportación y listo. En el directorio target tenía todos los ficheros .md para Hugo.

Genración del sitio web

Una vez hemos copiado todos los ficheros .md del directorio target al direction content/post dentro de nuestro proyecto de Hugo, todo lo que hacemos es ejecutar el comando hugo. Este genera un directorio llamado public el cual contiene todos los ficheros necesarios (HTML, Javascript y CSS). Dependiendo de la configuración de tu sistema y del número de entradas del blog y configuración del mismo, la duración puede vairar. En nuestro caso, como comenté antes tarda unos 6 minutos, actualmente tenemos más de 6.000 entradas.

Arquitectura

Ahora que tenemos el sitio ya generado, es hora de deplegarlo y crear un pipeline de despliegue automático, y aquí es la parte más interesante (para mi) del proyecto. En nuestro caso usamos Nginx como servidor web (Hugo dispone de su propio servidor interno), todo encapsulado en un contenedor Docker (Alpine Linux).

El proyecto completo lo tenemos en un repositorio privado en Gitlab.com. El proceso de despliegue es el siguiente:

  • Creamos una entrada nueva o simplemente hacemos alguna modificación, hacemos un commit/push.
  • Luego tenemos un servidor Jenkins, dónde tenemos varios trabajos creados, entre ellos uno para el despliegue del blog:

    1. Recoger los cambios de gitlab.com.
    2. Generación del sitio web.

      cd $WORKSPACE
      hugo --config=config-prod.toml
      
    3. Generación de una imagen Docker con el contenido del sitio que acabamos de generar. La imagen está etiquetada con el número del build de Jenkins. Esto nos permite fácilmente hacer un rollback del blog en caso que la publicación del mismo tenga algíun problema.

      docker image build -t cyberhades/blog:$BUILD_NUMBER .
      
    4. La imagen Docker que acabamos de crear se manda a Docker Hub.

      docker push cyberhades/blog:$BUILD_NUMBER
      
    5. Jenkins se conecta al servidor (Digital Ocean)

    6. Se descarga la imagen que acabamos de subir desde Docker Hub

      docker pull cyberhades/blog:$BUILD_NUMBER
      
    7. Para el contenedor actual que corre el blog y lo borra

      docker container stop blog-container
      docker container rm blog-container
      
    8. Crea un contenedor nuevo con la imagen que nos hemos descargado

      docker container run --name blog-container --restart=always -d -p 1313:80 cyberhades/blog:$BUILD_NUMBER
      

Todo el proceso de despligue tarda unos 17 minutos y todo está automatizado a través de un servidor privado (Ubuntu server, Intel i7 quad core, 8Gb RAM y SSD) con Jenkins.

El blog además tiene certifcados digitales de Let´s Encrypt, por lo que el servidor está disponible por HTTP y HTTPS. Los comentarios están integrado con Disqus y el RSS lo tenemos en Feedburner.