💼 Experiencia
- Despliegue de aplicaciones contenerizadas en clústeres EKS usando manifiestos de Kubernetes, Helm charts o pipelines CI/CD.
- Evaluación y configuración de servicios AWS con Terraform para cumplir requisitos de proyecto e integrarlos en la arquitectura cloud.
- Diseño de arquitecturas cloud y diagramas de infraestructura con servicios AWS, implementados con Terraform.
- Creación de módulos Terraform reutilizables para encapsular patrones y buenas prácticas de infraestructura en AWS.
- Liderazgo técnico y mentoría a un equipo DevOps, supervisando su trabajo y fomentando un entorno colaborativo y de alto rendimiento.
- Diseño, implementación y mantenimiento de sistemas automatizados de build, despliegue y gestión de configuración.
- Dockerización de aplicaciones para crear contenedores portables y escalables que funcionen en cualquier entorno.
- Implementación y mantenimiento de soluciones de monitorización y logging.
- Evaluación de proveedores cloud (AWS, Azure, Google Cloud) y selección de servicios adecuados para cada proyecto.
- Despliegue de apps iOS y Android mediante pipelines de Azure DevOps.
- Monitorización, mantenimiento y resolución de incidencias de software, hardware y red.
- Desarrollo de procesos automatizados para administración de sistemas y software.
- Uso de AWS ECS para autoescalar aplicaciones docker según métricas de CPU.
- Resolución de incidencias en puestos de usuario y red.
🚀 Proyectos
Portfolio bilingüe en Python (FastAPI · Docker · Terraform · AWS · Caddy TLS · SQLite · GitHub Actions)
Descripción general
Portfolio bilingüe en producción construido con FastAPI y servido con HTTPS en AWS. Objetivo: simplicidad, seguridad y automatización completa (infra + despliegue).
Funcionalidades visibles
- Enrutado bilingüe (EN/ES): portada
/
y CV en/cv
- Páginas de proyecto en Markdown (incluida esta) con código y listas
- Tarjetas colapsables, diseño limpio y centrado, y botones activables/desactivables desde configuración
- Sección de certificaciones donde cada elemento enlaza a su Credly (nombre y badge clicables)
- Página de freelance en
/freelance
con calendario/horas y tarifa; “Reservar” pre‑rellena el flujo de contacto
Disponibilidad y reservas
- Horario semanal aplicado en cliente y servidor:
- Lun–Jue 18–23, Vie 15–23, Sáb/Dom 0–23 (configurable en YAML)
- Fechas/horas pasadas deshabilitadas (cliente) y filtradas en servidor
- Fechas bloqueadas configurables en YAML y respetadas de extremo a extremo
- Prevención de doble reserva con tabla
slots
(clave única fecha+hora) - Creación atómica: inserta reserva + slots en una transacción; en conflicto devuelve 409
Panel de administración (/admin)
- Protegido con Basic Auth (variables de entorno ADMINUSER/ADMINPASS)
- Reservas:
- Listar/filtrar/buscar; actualizar estado (new/contacted/confirmed/cancelled)
- Exportar CSV; exportación ICS por reserva (calendario)
- Editor de configuración (/admin/config):
- Visibilidad de secciones
- Botones del hero: activar/desactivar y URL por botón (escribe en el YAML origen correcto)
- Certificaciones: activar/desactivar y URL de Credly por elemento
- Panel de diagnóstico: muestra archivos de config detectados, origen por botón, archivo de certificaciones y metadatos de build (imagen, SHA, hora)
Modelo de configuración (YAML, “último gana”)
- Fusión de
python/config.yaml
conpython/config.d/*.yaml
- Claves:
sections.*
— alterna visibilidad de UIhero_buttons.*
— activar/URL/etiquetas por botón (contact/freelance/github/linkedin/certifications/CV)certifications[]
—name
,badge
,url
,enabled
,order
freelance.*
— tarifa, horario y fechas bloqueadas- El panel administra y escribe en el YAML de origen (seguimiento por clave), así los cambios persisten como fuente de verdad
Persistencia de datos
- Base de datos SQLite con dos tablas:
bookings
— datos de la reserva + selecciones serializadasslots
— una fila por (fecha, hora) reservada, PK (date,hour)- El directorio de datos se resuelve automáticamente; en producción se usa
/data
montado desde el host
Arquitectura
- App: FastAPI + Uvicorn (puerto 8000 dentro del contenedor)
- Proxy inverso: Caddy publica 80/443, termina TLS (Let’s Encrypt), redirige
www
→ apex, añade HSTS y proxifica a la app - Contenerización: una imagen para la app; Caddy como contenedor separado en la misma red Docker
Infraestructura (Terraform)
- EC2 (Amazon Linux 2023) en VPC por defecto
- Elastic IP asociada a la instancia
- Security Group con 22/80/443
- Route 53 zona hospedada para
adrianmagarola.click
: - A (apex) → Elastic IP
www
CNAME → apex- Backend de estado remoto: S3 (+ bloqueo DynamoDB)
Estado de Terraform (boceto)
terraform {
backend "s3" {}
}
resource "aws_instance" "web" { /* Amazon Linux 2023, key pair, SG */ }
resource "aws_eip" "web" { instance = aws_instance.web.id }
resource "aws_route53_zone" "primary" { name = var.domain_name }
resource "aws_route53_record" "root_a" { /* apex A → EIP */ }
resource "aws_route53_record" "www" { /* CNAME www → apex */ }
CI/CD (GitHub Actions)
- Provisionar EC2 (Terraform): inicializa backend y aplica infra
- Build y despliegue a EC2 (Docker): build/push de la imagen y despliegue por SSH
- Limpieza de contenedores/imagenes/cache para evitar falta de disco
- Caddyfile enviado en base64 para evitar problemas de shell/SSH
- Montajes en el host EC2:
/opt/portfolio/data
→ contenedor/data
conPORTFOLIODATADIR=/data
(SQLite persistente)/opt/portfolio/config.d
→ contenedor/app/python/config.d
(config persistente)- Seedeo de archivos de config que falten desde la imagen; sincronización forzada de
50‑certifications.yaml
(con backup) para alinear URLs/estructura - Metadatos de build expuestos:
APPBUILDSHA
,APPBUILDIMAGE
,APPBUILDTIME
(visibles en Diagnóstico)
Caddyfile (esencial)
{
email admin@adrianmagarola.click
}
www.adrianmagarola.click {
redir https://adrianmagarola.click{uri} 301
}
adrianmagarola.click {
encode gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
reverse_proxy portfolio:8000
}
Seguridad y operabilidad
- TLS automático (Let’s Encrypt)
- HSTS y redirección
www
→ apex - Admin con Basic Auth (definir
ADMINUSER
/ADMINPASS
) - La app corre sin privilegios; Caddy gestiona 80/443
- Limpieza de disco en despliegues para mayor previsibilidad
Endpoints (selección)
GET /
— portada (EN/ES)GET /cv
— CV; certificaciones con enlaces a CredlyGET /freelance
— UI de reservasPOST /api/bookings
— reserva atómica (cumple horario, bloqueos y pasado)
Resultado
- Live: https://adrianmagarola.click
- Repo: https://github.com/amagarola/portfolio
Rastreador Tickets Supermercados (Flask · Gunicorn · OCR · PDF · Multi-Tienda · Docker · AWS · Caddy)
Visión General
Una aplicación web lista para producción que rastrea y analiza compras de supermercados desde tickets PDF y fotos móviles. Construida con Flask y desplegada en AWS EC2 con CI/CD automatizado.
Características Principales
Soporte Multi-Supermercado
- Mercadona, DIA y Alcampo con parsers OCR inteligentes
- Detección automática de tienda desde el formato del ticket
- Selección manual de tienda cuando sea necesario
- Tolerancia a errores de OCR comunes
Procesamiento Inteligente de Tickets
- Soporte de PDF y fotos con OCR Tesseract
- Integración de cámara móvil para captura instantánea
- Extracción automática de productos con precio, cantidad y categoría
- Seguimiento de número de factura con deduplicación para prevenir re-subidas
Gestión de Productos
- Categorización automática de productos
- Adición manual de productos con selección de tienda
- Lista de productos con filtrado por rango de fechas, categoría y tienda
- Análisis de gastos e historial de productos
Organización de Datos
- Aislamiento de datos por usuario con autenticación Flask-Login
- Seguimiento de facturas para prevenir duplicados
- Agregación de productos a través de múltiples tickets
- Filtrado y análisis basado en fechas
Stack Técnico
Backend
- Flask framework web con servidor WSGI Gunicorn
- OCR: Tesseract + pytesseract para extracción de texto
- Procesamiento PDF: pdfplumber y pdf2image para parseo de tickets
- Procesamiento de Imágenes: Pillow (PIL) para manipulación de fotos
- Autenticación: Flask-Login con hashing bcrypt de contraseñas
Arquitectura de Parsers
- Sistema modular de parsers con herencia de clase base
- Parsers específicos por tienda (Mercadona, DIA, Alcampo)
- Patrones de tolerancia a errores OCR (maneja espacios, caracteres especiales en precios)
- Logging detallado para depuración y mejora
Despliegue
- Servidor de Producción: Gunicorn con 4 workers, timeout 120s
- Containerización: Docker con build multi-etapa
- Reverse Proxy: Caddy para terminación HTTPS y TLS automático
- Cloud: AWS EC2 con despliegue automatizado vía GitHub Actions
- CI/CD: Pipeline automatizado de build, test y deploy
Características de Producción
Rendimiento
- Configuración multi-worker de Gunicorn para peticiones concurrentes
- Optimización de imagen Docker con caché de capas
- Servicio de archivos estáticos a través de reverse proxy Caddy
Seguridad
- Autenticación de usuarios con gestión de sesiones
- Hashing de contraseñas con bcrypt
- Aislamiento de datos por cuenta de usuario
- Solo HTTPS con renovación automática de certificados
Gestión de Datos
- Almacenamiento basado en JSON (gitignored por privacidad)
- Productos, tickets y facturas por usuario
- Funcionalidad de reseteo para fase de pruebas
- Deduplicación de facturas al eliminar tickets
Procesamiento OCR
Desafíos Resueltos
- Artefactos OCR: comas → espacios/+/% en precios
- Espacios faltantes antes de letras de impuesto (A/B)
- Formatos inconsistentes de líneas de producto entre tiendas
- Extracción de fecha y número de factura desde varios formatos
Mejoras del Parser
- Coincidencia de múltiples patrones de precio (regex con alternativas)
- Detección de letra de impuesto con espaciado flexible
- Validación de línea de producto con logging detallado
- Reglas y patrones de parseo específicos por tienda
Diseño Mobile-First
- Layout responsive para captura de tickets móvil
- Integración API de cámara para subida instantánea de fotos
- Vista previa de foto antes de envío
- Soporte para subida de fotos en lote
Demo
En vivo en: https://mercadona.adrianmagarola.click
Terraform Yaml-Config
Infraestructura Terraform con YAML Configs
📌 Descripción
Este proyecto proporciona un módulo de configuración centralizado para gestionar infraestructura multi-entorno usando plantillas YAML.
En lugar de codificar parámetros en múltiples módulos de Terraform, todas las configuraciones se definen en un único módulo yaml-config que otros módulos pueden referenciar.
Concepto clave: El sistema usa una estrategia de fusión jerárquica donde las configuraciones base se definen una vez, y cada entorno (dev
, qa
, pre
, prod
) solo sobrescribe lo diferente. Esto elimina duplicación y mantiene las definiciones DRY.
🗂️ Estructura del Proyecto
terraform/
├── yaml-config/ # Módulo de configuración central
│ ├── main.tf # Punto de entrada
│ ├── outputs.tf # Expone configs jerárquicas
│ ├── variables.tf # Entradas del módulo
│ ├── modules/
│ │ └── yaml-config/ # Lógica de fusión
│ │ ├── main.tf
│ │ └── variables.tf
│ └── config/
│ └── env/
│ ├── ec2.yaml # Config base EC2
│ ├── storage.yaml # Config base S3
│ ├── database.yaml # Config base RDS
│ ├── network.yaml # Config base VPC
│ ├── security.yaml # Config base security groups
│ ├── eks.yaml # Config base EKS
│ ├── dev/ # Sobrescrituras dev
│ ├── qa/ # Sobrescrituras QA
│ ├── pre/ # Sobrescrituras pre-prod
│ └── prod/ # Sobrescrituras producción
│
├── workload-ec2/ # Módulo workload EC2
├── workload-storage/ # Módulo workload S3
├── workload-database/ # Módulo workload RDS
├── workload-network/ # Módulo VPC/networking
├── workload-security/ # Módulo Security Groups
└── workload-eks/ # Módulo cluster EKS
---
⚙️ Cómo Funciona
1. Estructura YAML
Cada archivo YAML sigue esta estructura:
context:
environment: "${env}"
terraform:
vars:
storage: # Tipo de recurso
s3_buckets: # Categoría de recurso
first-s3: # Clave del recurso
name: "first-s3-${env}" # Propiedades
versioning: true
encryption: true
2. Fusión Jerárquica
- Config base (
config/env/*.yaml
) aplica a todos los entornos - Sobrescrituras de entorno (
config/env/{env}/*.yaml
) se fusionan encima - Módulo yaml-config usa
templatefile()
para interpolar variables - Resultado: Configuración limpia y fusionada por entorno
3. Flujo de Datos
YAML Base + YAML Entorno → yaml-config → Config Fusionada → Módulos Workload → Recursos AWS
Ejemplo:
storage.yaml + qa/storage.yaml → merge → module.yaml_config.env.qa.storage → for_each → Buckets S3
---
🚀 Ejemplo de Uso
En un Módulo Workload
Paso 1: Referenciar yaml-config
module "yaml_config" {
source = "../yaml-config"
environment = var.environment
}
Paso 2: Acceder a configuraciones
locals {
s3_buckets = try(
module.yaml_config.env[var.environment].storage.s3_buckets,
{}
)
}
Paso 3: Crear recursos dinámicamente
resource "aws_s3_bucket" "this" {
for_each = local.s3_buckets
bucket = each.value.name
# ... otras propiedades del YAML
}
Añadir Nuevos Recursos
¡No se requieren cambios en código Terraform!
---
- Editar
config/env/storage.yaml
(definición base) - Sobrescribir en
config/env/qa/storage.yaml
si es necesario - Ejecutar
terraform apply -var="environment=qa"
- Recursos auto-creados via
for_each
✨
✅ Ventajas
- Principio DRY → Definir una vez, sobrescribir solo diferencias
- Centralizado → Única fuente de verdad
- Dinámico → Auto-escalado con
for_each
- Tipado seguro → Esquema YAML estructurado
- Flexible → Interpolación de plantillas (
${env}
) - Separación clara → Configuración vs. lógica
- Auditoría fácil → Diffs claros en git por entorno