Blog de Obi Madu
Volver a todos los artículos
InfrastructureDevOpsProjects

Proyecto Self-Hosting (Infraestructura DevOps desde Cero)

Una guía completa para construir una infraestructura DevOps autoalojada desde cero utilizando Terraform, Ansible y Docker.

Proyecto Self-Hosting (Infraestructura DevOps desde Cero)

Acerca de este Proyecto

Una Historia de Fondo

Así que, antes de vender finalmente mi alma a Kubernetes, decidí que procedía hacer un proyecto IaaS razonablemente completo. Quería hacer un buen uso de conceptos sólidos y básicos de Redes, Seguridad, Scripting, luego Terraform, Ansible, Docker, Traefik y Wireguard (y todos los conceptos que representan), para un proyecto completo. Ya que Kubernetes ha sido la herramienta principal en mi lista de "cosas por aprender" durante un tiempo, pensé: bueno, ¿por qué no desplegar todas las herramientas necesarias que necesitaría para mi transición a usarlo en un proyecto precursor? ¡Me pareció una gran idea!

El pequeño giro fue que tuve esa idea por un tiempo, hasta que tuve motivos para poner rápidamente en marcha una infraestructura que me permitiera alojar mi propio gestor de contraseñas Vaultwarden, ya que mi suscripción de Bitwarden de aquel momento estaba a punto de expirar. Bitwarden en línea es económico, pero no tenía los $10 para renovarlo en ese momento. Lo que sí tenía eran algunos créditos en la nube en DigitalOcean, un nombre de dominio, Cloudflare gratuito, la idea para este proyecto y habilidades razonables de infraestructura de TI.

Puede que te interese saber que, en mi implementación de este proyecto, creé cada archivo con Neovim (LazyVIM), que comencé a aprender a finales de agosto. En general, creo que hizo que todo el proceso fuera aún más divertido y gratificante.

Así que para este proyecto, había 5 objetivos de suma importancia (y un 6to):-

  1. AUTOMATIZACIÓN COMPLETA (Del tipo configúralo de extremo a extremo y vete a dormir)
  2. VISIBILIDAD COMPLETA (Nada se mueve ni un centímetro en el entorno sin ser detectado y registrado)
  3. REPRODUCIBILIDAD COMPLETA (Todos los datos importantes respaldados por completo. Acompañado de una política de restauración por fechas, de tipo configúralo y listo)
  4. BUENA SEGURIDAD (VPN privada y claves SSH como únicos medios de acceso. Bloqueo total del firewall, comunicación segura de Docker a host)
  5. Hacerlo de la manera más BARATA, AUTOALOJADA y de CÓDIGO ABIERTO posible (Que encaje en mi presupuesto en un solo VPS)
  6. Hacerlo desde CERO

Antes de que sigas leyendo, debo señalar que este proyecto NO es adecuado para entornos de alta disponibilidad. A pesar de la naturaleza altamente repetible de sus entornos, estar en un solo servidor significa cierto tiempo de inactividad operativo (aunque no de la carga de trabajo) para las operaciones de restauración. Dicho esto, las organizaciones pequeñas, emergentes, con presupuesto limitado y que necesiten inicializar infraestructura a la velocidad del rayo podrían salirse totalmente con la suya con esto.

En este proyecto, he desplegado los 5 servicios más comunes que uno típicamente necesitaría para manejar un entorno k8s (y algunas cosas personales adicionales, incluyendo un frontend de LLM que llamo Jarvis 😉).

  1. Un servicio de alojamiento de Git privado (Gitea)
  2. Una herramienta CI/CD (Jenkins)
  3. Una herramienta de Gestión de Secretos (Harshicorp Vault)
  4. Un Artifactory privado (JFrog Container Registry)
  5. Una herramienta de escaneo de vulnerabilidades (SonarQube)

Con estos, podrías alojar código privado en Gitea, compilar con Jenkins, escanear (escaneos de código e imágenes) con SonarQube y Trivy, lanzar a tu Artifactory autoalojado y desplegar en Kubernetes o cualquier otro entorno. Todo esto, mientras aseguras tus secretos tanto dentro de tus Pipelines como de tu(s) entorno(s) de Kubernetes a través de una instancia central de Vault.

Para lograr el segundo objetivo, desplegué una Pila de Monitoreo y Observabilidad que proporciona una visión completa tanto del Servidor Host como de todos los entornos de los Contenedores.

  1. Prometheus y cAdvisor para la recopilación de Métricas
  2. Loki y Promtail para la agregación de Registros (Logs)
  3. Grafana para la Visualización tanto de Métricas como de Registros

En cuanto a la Seguridad, dado que esta Infraestructura está destinada a ser la columna vertebral de las operaciones DevOps de una organización y, por lo tanto, Interna, su entorno está completamente aislado del Internet público y es accesible solo a través de una VPN. El proyecto también instrumenta un puerto SSH personalizado, protegiendo efectivamente el entorno de la mayoría de los ataques aleatorios y automatizados de Internet. Además, se implementa una comunicación segura entre Docker y el Host a través de reglas de IPTables adecuadas, garantizando que se mantenga un aislamiento adecuado entre los entornos del Contenedor y del Host.

¿Dónde está el código fuente?

Estoy muy disponible (muy ansioso) por ayudar a tu Organización a poner en marcha esta Infraestructura o un representante cercano a la misma. Portaré esto a cualquier entorno (nube o en las instalaciones), con la garantía de que ninguna de las características principales enumeradas anteriormente faltará o estará incompleta. Traed esos encargos :cool:

¿Qué sigue para mí?

Ahora, con el poder que me ha sido conferido (por alguien), me declaro oficialmente un Kubestronauta. De ahora en adelante vivo y respiro Kubernetes, jajaja. Así que a continuación, seguiré con algunos proyectos jugosos de k8s que estoy cocinando con algunos amigos. Portaremos una aplicación de microservicios bastante complicada a k8s desde cero (a través de DevSecOps). Creando clústeres de k8s, equipándolos con ArgoCD y volviéndonos locos con GitOps para noviembre.

Si te encantó este proyecto, no olvides dejar un Me gusta y Seguir para más 'contenido' como este. También hazme saber en los comentarios qué piensas del proyecto y cómo crees que podría haberlo hecho mejor.

SI te gustaría repetir este proyecto

  1. Ten en cuenta, no he escrito instrucciones sobre cómo hacer nada.
  2. Todo lo que encontrarás son instrucciones sobre qué hacer. Anoté estos pasos mientras trabajaba en este proyecto con amigos. Idealmente, estas instrucciones deberían ayudarte a lograr esto de una manera sensata.
  3. Toda la diversión está en descubrir el cómo de cualquier parte del proyecto con la que no estés familiarizado.

Requisitos Previos del Proyecto

  1. Un nombre de dominio
  2. Acceso a alojamiento en la Nube (Cloud)
  3. Un almacén de claves (Key store) en la Nube
  4. Uno (o más) bucket(s) de almacenamiento en la Nube
  5. Algunos conocimientos de TI

Notas importantes sobre la estructura y organización del Proyecto

Las Cuentas Cloud Controladora y de Destino

Este proyecto asume la existencia de dos cuentas diferentes de plataforma Cloud. Una para los recursos de destino y la otra para el acceso de administrador definitivo sobre esos recursos desplegados en la cuenta de destino.

Las partes más importantes de este proyecto son concretamente;

  1. El State de Terraform (por razones obvias)
  2. Las Claves SSH del Servidor, que Ansible u otra persona necesitaría para conectarse al Servidor.
  3. Los Archivos de Copia de Seguridad (Backups)
  4. Las claves de Desbloqueo Automático (Auto-Unseal) de Hashicorp Vault

Con estas cuatro cosas y la implementación del código, el sistema completo podría reproducirse en su totalidad, desde cero.

Como resultado, durante este proyecto asumí que habría un Individuo (o un grupo de Individuos) con Acceso de Súper Administrador sobre toda la Infraestructura base (operativa) de la empresa. Estos serían (idealmente) partes interesadas clave, como el CEO o el Jefe de Ingeniería. Serían las personas con las llaves definitivas (proverbiales) del reino.

En la práctica, las cuentas "controladora" y de "recursos" pueden tomar varias formas y configuraciones, dependiendo de tu configuración y requisitos específicos. Estas cuentas podrían representar diferentes entidades en múltiples aspectos de tu infraestructura en la nube. Aquí hay algunos escenarios posibles:

1. Multi-nube (Multi-cloud): Las cuentas podrían pertenecer a diferentes plataformas en la nube en su totalidad. Por ejemplo, podrías tener tu cuenta controladora alojada en Azure, mientras que tus recursos se despliegan en un proveedor de nube separado como Digital Ocean. Esto te permite aprovechar las características y servicios únicos que ofrece cada plataforma.

2. Misma plataforma en la nube, cuentas diferentes: Las cuentas podrían ser cuentas separadas dentro del mismo proveedor de nube. Esta configuración es común cuando deseas mantener el aislamiento entre diferentes entornos, como producción y desarrollo, o entre diferentes departamentos o proyectos dentro de tu organización.

3. Misma plataforma en la nube, diferentes unidades organizativas: En algunas plataformas en la nube, como Azure con sus grupos de recursos o Google Cloud con sus proyectos, las cuentas podrían representar diferentes agrupaciones lógicas dentro de la misma cuenta en la nube. Esto te permite organizar y gestionar los recursos basándote en criterios específicos, como proyecto, entorno o aplicación.

Yo tomé el enfoque Multi-nube, mis recursos están desplegados en Digital Ocean, mientras que mi Cuenta Controladora está en Azure.

Sobre el Despliegue de Otros Servicios (Extras)

Como probablemente hayas notado, además de las herramientas principales, desplegué las aplicaciones VaultWarden Password Manager y AnythingLLM. Estas herramientas fueron desplegadas para mí. Eres libre de deshacerte de ellas y desplegar cualquier otra herramienta de tu elección.

Descripción General

Con el espíritu de ofrecer una visión general, a continuación se detallan las funciones de cada uno de los tres componentes principales de este proyecto;

Terraform para desplegar;

  • Recursos del servidor (VPC, Firewall/SG)
  • Servidor en DO/AWS/etc, con script Userdata
  • Registros DNS en Cloudflare/etc
  • Playbook de Ansible

Playbook de Ansible para;

  • Configurar servidor (Hostname, Reglas de Red/Seguridad)
  • Instalar Utilidades (s3cmd, prometheus-node-exporter)
  • Instalar y Configurar Docker
  • Instalar y Configurar WireGuard VPN
  • Desplegar Docker compose (Pilas de Servicios y Monitoreo)
  • Scripts y trabajos (jobs) para copias de seguridad de volúmenes Docker
  • Scripts para la restauración de volúmenes Docker

Docker Compose para Desplegar;

  • Puerta de enlace (Gateway) Traefik (HTTP, SSL, Redirecciones HTTP-HTTPS, Lista Blanca de IPs)
  • Gitea
  • Jenkins
  • Hashicorp Vault
  • JFrog Container Registry
  • SonarQube OSS
  • Prometheus
  • Loki y Promtail
  • Grafana
  • Vaultwarden
  • AnythingLLM

Agrupaciones de Tareas

Estas agrupaciones de tareas contribuyeron de manera clave al hecho de que terminara este proyecto. Las trazaba cada día que trabajaba en el proyecto con mis amigos. Son una gran razón por la que no me rendí. En la mayoría (si no en todos los casos) representan objetivos diarios. Hazlas en orden y tendrás una Infraestructura completamente funcional.

Tarea 1

Objetivo: Inicializar (Bootstrap) la Infraestructura vía Terraform

  1. Un solo servidor en DO/AWS/etc vía local state
  2. Firewall/Grupo de Seguridad (Security Group)
  3. Registros DNS en Cloudflare/Route53, etc., para futuros servicios (jenkins, etc.)

Tarea 2

Objetivo: Configurar el remote state para Infra. Asegurarse de que Ansible pueda acceder y desplegar en el recurso del servidor.

  1. Configurar el remote state para Terraform
  2. Configurar Terraform para desplegar un playbook de Ansible simulado (dummy)
    2.1 Crear el playbook de Ansible simulado
    2.2 Crear el par de claves ssh para el acceso al servidor
    2.2 Configurar la cuenta de usuario de Ansible en el servidor a través del script cloud-init userdata. El script userdata crea una cuenta de usuario 'ansible' y configura una clave de host conocido (known-host key) (con la clave pública generada en el paso 2.2).
    2.3 Usar nuestra clave privada para ejecutar el playbook simulado en el servidor a través de un local-provisioner de Terraform.

Tarea 3

Objetivo: Aprender a desplegar Docker Compose mediante Ansible. Aprender los conceptos básicos de Traefik.

  1. Crear un archivo docker-compose que despliegue una instancia temporal de Jenkins en la url jenkins.tu-dominio.com utilizando un contenedor proxy inverso de Traefik. Desplegar solo en http. No hay necesidad de persistencia mediante volúmenes de Docker todavía.
  2. Desplegar el archivo docker-compose a través de un rol de Ansible. El despliegue a través de un rol es obligatorio. Recuerda deshacerte del despliegue simulado anterior ahora, ya no es necesario.

Tarea 4

Objetivo: Implementar la seguridad básica del sistema.

  1. Configurar nuestro servidor para usar un puerto SSH diferente.
  2. Deshabilitar el inicio de sesión con contraseña SSH para todos los Usuarios
  3. Deshabilitar el inicio de sesión de Root por SSH
  4. Modificar nuestras reglas de firewall para cerrar el puerto 22 y abrir el nuevo puerto SSH.
  5. Configurar Ansible para conectarse a través del nuevo puerto ssh.
  6. Añadir https a nuestro despliegue de Docker compose.

Tarea 5

Objetivo: Configurar el monitoreo del servidor host. Actualizar nuestro entorno de cargas de trabajo de contenedores.

  1. Instalar un Prometheus node exporter en el servidor host mediante Ansible.
  2. Instalar Prometheus y Grafana en nuestras cargas de trabajo de contenedores.
  3. Crear una nueva red Docker con el controlador (driver) bridge, asignar cualquier dirección /24 privada como su subred. Asignar todas las cargas de trabajo de contenedores a esta red.
    (Si lo deseas, crea diferentes archivos compose para la pila de monitoreo y la pila de servicios. De hecho, esto es aconsejable, ya que todos estos servicios en un solo archivo compose se verán quizás un poco desordenados al final).
  4. Crear los volúmenes Docker apropiados para cada servicio compose, y asignar estos volúmenes a los servicios.
  5. Añadir nuestra máquina host como un objetivo (target) de métricas en el servicio Prometheus, a través de Ansible.
  6. Configurar Grafana para obtener nuestras Métricas de Prometheus.
  7. Añadir un panel (dashboard) de Grafana para monitorear nuestro servidor host. Esto, por ahora, es solo para asegurar que la configuración funciona. Será temporal.

Tarea 6

Objetivo: Implementar copias de seguridad.

  1. Escribir un script para hacer copias de seguridad de todos los volúmenes docker y subirlos a un bucket remoto.
    1.1 El script debe montar cada volumen en un contenedor temporal
    1.2 Comprimir (Tar y Gzip) su contenido. Manteniendo intacta toda la información de propiedad de archivos y directorios.
    1.3 Guardar el archivo en un directorio 'backups'. Usar una convención de nombres de archivo 'nombre-volumen-timestamp.tar.gz'.
    1.4 Cuando todos los volúmenes estén archivados. Subir todo lo que hay en la carpeta 'backups' al bucket de almacenamiento en la nube que hayas designado para copias de seguridad.
  2. Usar un cron-job para hacer que este script de copia de seguridad se ejecute dos veces al día, a las dos horas que elijas, a través de Ansible.

Tarea 7

Objetivo: Crear una política de retención de copias de seguridad.

  1. Escribir un script para eliminar todas las copias de seguridad que tengan más de una cantidad específica de días de antigüedad. El número de días debe ser un parámetro pasado al script en el momento de la llamada.
    1.1 Por ejemplo, si el script se llama con un parámetro '5', debería eliminar todas las copias de seguridad que no estén dentro del marco de tiempo de los últimos 5 días. Solo debería mantener los últimos 5 días de copias de seguridad recientes.
    Opcionalmente, podrías elegir mantener siempre las copias de seguridad más recientes de cada volumen.
  2. Mediante Ansible, crear un cron-job para ejecutar este script de 'limpieza' una vez al día. Mantener 7 días de copias de seguridad recientes.

Tarea 8

Objetivo: Configurar una Infraestructura de Registro (Logging).

  1. Modificar tus scripts de copia de seguridad y limpieza para registrar las acciones relevantes en stdout.
  2. Modificar tus cron-jobs de copia de seguridad y limpieza para canalizar (pipe) todas las salidas y errores, con etiquetas relevantes, al syslog.
  3. Desplegar Promtail y Grafana Loki a través de Docker compose. Montar el directorio '/var/log' de solo lectura (read-only) en el contenedor de Promtail.
  4. Usando Ansible, escribir un archivo de configuración de Promtail para extraer (scrape) registros del directorio /var/log, y enviarlos al servicio Loki.
  5. Instalar el plugin docker de Loki en el servidor host usando Ansible. Configurar el demonio (daemon) Docker para que use Loki como el controlador (driver) de registros predeterminado editando adecuadamente el archivo /etc/docker/daemon.json. Recuerda reiniciar el servicio Docker y recrear todos los contenedores después de este paso.
  6. Añadir Loki como origen de datos (data-source) en Grafana. Confirmar que puedes visualizar los registros desde el directorio /var/log y todos los contenedores docker ejecutándose en el host a través de la pestaña 'Explore' de Grafana.

Tarea 9

Objetivo: Completar el Despliegue de Servicios.

  1. Desplegar Gitea vía docker-compose
  2. Desplegar Hashicorp Vault vía docker-compose
  3. Desplegar JFrog Container Registry OSS vía docker-compose
  4. Desplegar SonarQube vía docker-compose
  5. Desplegar cAdvisor para monitorear el uso de recursos del host por servicio de contenedor
  6. Exponer métricas de Prometheus desde el servicio Traefik
  7. Crear paneles de Grafana para Traefik y cAdvisor
  8. ¡Desplegar cualquier otro servicio que desees!

Tarea 10

Objetivo: Implementar una política de restauración de copias de seguridad.

  1. Crear un script de restauración de copias de seguridad que se ejecute solo cuando el valor de una variable 'restore' sea igual a 'true'. El script debe tomar el nombre del bucket, la fecha de la copia de seguridad específica que se va a restaurar, y un valor '1' o '2' indicando si se debe restaurar la primera o segunda copia de seguridad del día.

Tarea 11

Objetivo: Privatizar el acceso a la Infraestructura a través de WireGuard VPN.

  1. Instalar Wireguard en el servidor Host mediante Ansible.
  2. A través de una combinación de pasos de Ansible y Terraform, configurar y establecer una Interfaz de Wireguard en el servidor, generar un archivo de configuración apropiado para esta Interfaz, configurar la Interfaz con el archivo de configuración, y luego generar archivos de configuración de cliente apropiados para usar al conectarse a esta Interfaz.
  3. Configurar Traefik, mediante una Lista Blanca de IP, para permitir el tráfico a todos los servicios únicamente en conexiones iniciadas a través de la subred VPN de Wireguard.

Tarea 12

Objetivo: Desplegar y Configurar un VPS Agente de Jenkins mediante Terraform y Ansible.

  1. Usando Terraform, desplegar un servidor VPS adicional de cualquier tamaño de tu elección para actuar como agente de compilación (build agent) para tu instancia de Jenkins.
  2. Configurar tu agente con Ansible, instalando cualquier herramienta que Jenkins necesite, como un paquete JDK, y Trivy para el Escaneo de Imágenes de Contenedores.
  3. Usando Ansible, instalar Wireguard en tu agente y configurarlo como un Peer (par) en tu VPN privada.
  4. (Opcional) Si deseas poder acceder a tus servicios en Traefik utilizando sus FQDNs desde el Agente, configurar los registros DNS apropiados usando Ansible en el Agente.

Conclusión

¡Voila! Si tus habilidades te han llevado hasta aquí 😉 Entonces ya tienes una copia funcional de esta Infraestructura. Si te encantó este proyecto, no olvides dejar un Me gusta. También hazme saber en los comentarios qué piensas del proyecto y cómo crees que podría haberlo hecho mejor.