Viernes, 3 pm. Tu equipo intenta sacar un hotfix crítico y GitHub Actions decide meter tu build en cola. Pasa 15 minutos. Luego 20. La ventana de despliegue se cierra, las personas interesadas presionan y la factura mensual de Actions supera los 800 dólares.
Esa fue mi realidad hace seis meses. Y como casi todo lo que te desvela a las 2 am, empezó pequeño e inocente.
El problema de los 800 dólares que no paraba de crecer
El equipo pasó de 3 a 15 desarrolladores, aumentó la frecuencia de despliegues y el consumo de GitHub Actions se disparó. De 100 al mes a 400, luego 600 y finalmente pasó el umbral de 800. Y lo peor ni siquiera era el coste, era la espera. En picos de despliegue, los jobs se quedaban en cola 10 o 15 minutos. La gente lanzaba el build y se iba por un café, charlaba o peor todavía cambiaba de tarea y perdía el foco. El ciclo de feedback se volvió lentísimo y la productividad cayó.
Miraba la cola pensando: tiene que haber una forma mejor.
La luz al final del túnel: Actions Runner Controller ARC
Tras muchas noches investigando encontré Actions Runner Controller. La idea es simple: en lugar de runners siempre encendidos, ARC crea pods efímeros que aparecen cuando hay trabajo, ejecutan el job y desaparecen al terminar. Es pagar solo por lo que usas, con control total del entorno y capacidad de escalar de 0 a 100 o más runners al instante. Adiós a la cola de GitHub.
Por supuesto, llegar hasta ahí vino con varios tropiezos espectaculares.
Instalando el controlador de ARC y mi primer gran fallo
El fiasco del firewall
Monté todo un fin de semana y nada hablaba con nada. Los webhooks fallaban, los runners no se registraban y yo cuestionando mis decisiones. El culpable era obvio: olvidé abrir en el firewall los rangos de IP de los webhooks de GitHub. Primer gran aprendizaje: la red no es un detalle de última hora. Si usas webhooks, tu clúster debe ser accesible desde internet y GitHub tiene que poder alcanzar el controlador de ARC.
Como se comunica ARC en la práctica
GitHub envía eventos por webhook cuando se dispara un workflow y el controlador de ARC crea o elimina pods de runner según la cola. Los pods se registran con GitHub, consultan trabajos, ejecutan y reportan estado de vuelta al controlador. Punto clave: la comunicación la inicia GitHub, así que necesitas reglas de firewall permitiendo los rangos de webhook, un balanceador exponiendo el endpoint y DNS correcto para la URL del webhook.
Lo que finalmente funcionó
Primero, cert manager para emitir certificados y forzar HTTPS en los webhooks. Después, instalar el controlador de ARC en su propio namespace arc-systems y probar conectividad a conciencia: IP externa accesible, certificado válido, GitHub alcanzando la URL y revisión de los logs de entrega de webhooks.
Base sólida: preparando el clúster de GKE
La apuesta por instancias spot
Decidí usar nodos preemptibles de Google Cloud. Cuestan un 60 a 70 por ciento menos y pueden desaparecer con 30 segundos de aviso. Con ARC, cuando un nodo muere, los pods se reprograman y listo. El ahorro fue inmediato sin sacrificar estabilidad.
Almacenamiento y estado compartido
Ignorar el almacenamiento compartido fue un error. Sin cachés compartidas, cada job empezaba desde cero y los builds iban más lentos que en los runners alojados de GitHub. La solución fue un Filestore con 250 GB para cachés y organización inteligente por repositorio y rama. El resultado fue una bajada del 40 por ciento en tiempos de build.
Imágenes personalizadas: aprendiendo Docker a base de golpes
El monstruo de 8 GB
Mi primera imagen de runner tenía de todo: múltiples Node, Python 2 y 3, infinidad de CLIs y paquetes. Pesaba 8 GB y tardaba 15 minutos en descargarse con cada pod. En pods efímeros, el tamaño de imagen es felicidad de desarrollador. Tocó rediseñar.
Estrategia de imágenes
Separé bases por propósito: una ubuntu 22 04 ligera con Node, Python y herramientas esenciales; otra centrada en infraestructura con Terraform, kubectl y CLIs de nube; y una orientada a QA con navegadores, Selenium y frameworks de pruebas. Optimizaciones clave: multi stage builds, elegir paquetes con criterio, ordenar capas para maximizar cache y limpiar residuos de apt y temporales. El punto dulce quedó en 1.5 a 2 GB por imagen y descarga en menos de 60 segundos. El setup de job bajó de 3 o 4 minutos a menos de 30 segundos.
Runners efímeros: entendiendo el ciclo de vida
El pod nace cuando ARC ve un job en cola, se registra con GitHub, ejecuta el workflow y se elimina al terminar. La arquitectura usa dos contenedores: uno para el runner y un sidecar Docker in Docker para builds de contenedores con aislamiento.
El descubrimiento de Docker in Docker
Probar con el socket de Docker del host funcionó en test, pero en producción era un riesgo grave de seguridad. Con DinD logramos aislamiento y builds fiables sin exponer el host.
El desastre de la asignación de recursos
Lancé a producción con límites mínimos esperando que Kubernetes se apañara. Resultado: OOMKills, jobs fallando y el clúster bajo presión. La receta que funcionó fue definir peticiones y límites claros: CPU petición 1 core y límite 4, memoria petición 1 GB y límite 2 GB, caché compartida para todos los runners. Consejo directo: define siempre requests y limits.
El punto de escalado
Tras meses de ajuste fino, lo ideal fue 1 runner mínimo siempre listo, 100 runners máximo y escalar a cero cuando no hay carga. Así hay recogida inmediata para cambios pequeños y gran paralelización cuando se necesita.
Resultados cuando todo encaja
Coste: de más de 800 al mes a unos 200, alrededor de un 70 por ciento de ahorro y sin sustos. Rendimiento: tiempo en cola de 15 minutos a 30 segundos, builds un 40 por ciento más rápidos gracias a la caché y despliegues mucho más fiables. La mayor victoria fue la satisfacción del equipo: ciclos de feedback rápidos y menos cambios de contexto.
Un sistema antifrágil
El sistema escala cuando hay picos, reprograma pods cuando preemptan nodos y ofrece trazabilidad fina para diagnosticar fallos. Cada tropiezo enseñó algo: el firewall nos hizo planificar la red, el almacenamiento demostró la importancia del estado compartido y los límites de recursos evitaron la contención.
Deberías dar el salto
Si gastas más de 500 al mes en GitHub Actions y sufres colas, los runners autogestionados en GKE pueden ser la respuesta. Requiere 2 o 3 semanas de puesta a punto, curva de aprendizaje si no dominas Kubernetes y algo de mantenimiento, pero el ahorro llega y el control compensa. Empieza simple, mide todo y añade complejidad de forma gradual.
La conclusión
Hace seis meses pagaba 800 al mes por esperar en la cola de GitHub. Hoy pago 200 por despliegues instantáneos y entornos a medida.
Como encaja Q2BSTUDIO en esta historia
En Q2BSTUDIO ayudamos a empresas a construir plataformas de CI CD robustas sobre GKE y a modernizar su stack con aplicaciones a medida y software a medida que aceleran el time to market. Combinamos buenas prácticas de infraestructura con inteligencia artificial, ciberseguridad, agentes IA y automatización para que tus equipos entreguen más y mejor. Si quieres optimizar costes, gobernanza y resiliencia, nuestros servicios cloud aws y azure cubren desde diseño de arquitectura hasta observabilidad y FinOps. Y si buscas orquestar pipelines, RPA o flujos de datos sin fricción, podemos ayudarte a escalar tu automatización de procesos con estándares de seguridad nivel enterprise.
Mas allá del CI CD, nuestro equipo impulsa ia para empresas con casos reales que integran modelos, agentes IA y servicios inteligencia de negocio. Trabajamos con power bi para poner datos en manos del negocio con dashboards accionables, y reforzamos tu postura de ciberseguridad con auditorías y pentesting para prevenir riesgos sin frenar la innovación.
Si tu organización está lista para reducir costes, eliminar tiempos de espera y ganar control, hablemos. Llevemos tu plataforma de delivery a un siguiente nivel con prácticas cloud nativas y automatización inteligente.