Plataforma interna de despliegue con Backstage, Argo CD y Kubernetes
Backstage, Argo CD y Kubernetes pueden convertir el despliegue en autoservicio. Guía práctica con plantillas, GitOps, promoción entre entornos y recuperación ante fallos.

¿Qué es una plataforma interna de desarrollo (IDP) y por qué la necesitas?
Una plataforma interna de desarrollo (IDP) es la capa que convierte tareas repetitivas de infraestructura en flujos de autoservicio. En lugar de que cada equipo pida a DevOps crear un repositorio, un pipeline y un entorno, los equipos lo solicitan desde un portal y reciben una estructura estándar ya conectada a Kubernetes.
Backstage es el portal: aporta el catálogo de servicios, las plantillas del Scaffolder y la documentación con TechDocs. Argo CD y Kubernetes ejecutan y sincronizan lo que la plataforma declara.
El problema que resuelve es concreto. Cuando varios equipos configuran por su cuenta repositorios, pipelines, entornos y despliegues, aparecen tres síntomas:
- Dependencia del equipo DevOps para lo repetitivo, que deja de trabajar en lo que sí requiere criterio experto.
- Diferencias de seguridad y operación: algunos servicios con pruebas, otros sin; unos con límites de recursos y otros no.
- Despliegues difíciles de auditar: no queda claro qué versión corre en cada entorno ni quién la aprobó.
Una IDP no es "una herramienta más". Es un contrato: los equipos piden un servicio con una plantilla, la plataforma genera el repositorio con estándares y el despliegue pasa por un flujo conocido y trazable.
[COMPLETAR: describe el punto de partida real — cuántos equipos y servicios había, qué dolores concretos de despliegue o seguridad se repetían y cómo se aprobaban los cambios — sin cifras que no puedas respaldar.]
Cuándo NO construir una IDP
- Pocos servicios y un solo equipo. Si despliegas tres aplicaciones, un pipeline compartido y una convención escrita bastan.
- Sin Kubernetes ni contenedores. La IDP se apoya en un modelo declarativo; si tu despliegue es manual, primero moderniza eso.
- Sin dueño para mantenerla. Un portal es software propio: tiene backlog, versiones y soporte.
- Si el cuello de botella es otro. Si los incidentes vienen de pruebas insuficientes o de mala observabilidad, ataca eso primero.
Arquitectura de referencia: Backstage, Argo CD, Kubernetes y tu stack
La arquitectura se apoya en separar el repositorio de aplicación del repositorio de configuración. Esa separación permite que CI construya y GitOps despliegue, sin que ningún pipeline tenga credenciales de clúster.
Diagrama de arquitectura
┌──────────────────────┐
Dev ──pide──▶ │ Backstage (portal) │──genera──▶ Repo de app
│ · Scaffolder │
│ · Catálogo │──registra──▶ catalog-info.yaml
│ · TechDocs │
└──────────────────────┘
▲
│ anotaciones (enlaces operativos)
│
Repo de app ──push──▶ CI (GitHub Actions / GitLab CI)
│ tests · SAST · build
▼
Registro de imágenes (tag inmutable: sha-xxxx)
│
│ commit de la etiqueta
▼
Repo GitOps (declarativo)
│
▼
Argo CD ──sync──▶ Kubernetes (dev / staging / prod)Diagrama del flujo de despliegue
[1] Scaffold ─▶ [2] Repo + pipeline ─▶ [3] Tests + imagen ─▶ [4] Commit del tag en GitOps
│
▼
[8] Catálogo/ownership ◀── [7] Argo CD sincroniza dev ──▶ [6] PR a staging/prod (aprobación)Repositorios y responsabilidades
- Repo de la app: código,
Dockerfile, pipeline,catalog-info.yamly documentación. - Repo GitOps: solo manifiestos y overlays, un path por entorno. Sin código de aplicación.
- Registro de imágenes: imágenes con etiqueta inmutable, nunca
latest.
Qué hace cada herramienta y qué no
| Herramienta | Hace | No hace |
|---|---|---|
| Backstage | Catálogo, plantillas, docs, ownership | No despliega ni ejecuta contenedores |
| CI (Actions / GitLab CI) | Tests, análisis de seguridad, build y push de imagen | No decide el estado deseado del clúster |
| Argo CD | Sincroniza el estado declarado en Git con el clúster | No construye imágenes ni corre pruebas |
| Kubernetes | Ejecuta y programa las cargas | No organiza el catálogo de servicios |
Creando plantillas de servicios y pipelines de autoservicio
La plantilla de Backstage
Una plantilla del Scaffolder declara parámetros y pasos. Este ejemplo crea un servicio con repositorio, pipeline y estructura GitOps:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: servicio-laravel
title: Servicio Laravel
description: Crea repositorio con app Laravel, pipeline y estructura GitOps.
spec:
owner: plataforma
type: service
parameters:
- title: Datos del servicio
required: [name, owner]
properties:
name:
title: Nombre
type: string
pattern: '^[a-z][a-z0-9-]{2,30}$'
owner:
title: Equipo responsable
type: string
ui:field: OwnerPicker
steps:
- id: fetch
name: Obtener esqueleto
action: fetch:template
input:
url: ./skeleton/laravel
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
- id: publish
name: Publicar repositorio
action: publish:github
input:
repoUrl: github.com?owner=mi-org&repo=${{ parameters.name }}
defaultBranch: main
- id: register
name: Registrar en el catálogo
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yamlEl esqueleto del repositorio queda así:
skeleton/laravel/
├── catalog-info.yaml # metadatos, owner, enlaces
├── Dockerfile
├── .github/workflows/build.yml
├── gitops/ # valores que el CI actualizará
└── docs/index.md # fuente de TechDocsPara React cambia el esqueleto y los comandos de build, pero los pasos del Scaffolder y el flujo CI/CD son los mismos. Esa reutilización es la ventaja central de estandarizar.
Pipeline de CI: pruebas, seguridad e imagen inmutable
name: build
on:
push:
branches: [main]
env:
IMAGE: ghcr.io/mi-org/${{ github.event.repository.name }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Pruebas
run: |
composer install --no-interaction --prefer-dist
php artisan test
- name: Análisis de dependencias
run: composer audit
- name: Login al registro
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build y push con tag inmutable
run: |
TAG="sha-${GITHUB_SHA::12}"
docker build -t "$IMAGE:$TAG" .
docker push "$IMAGE:$TAG"
echo "TAG=$TAG" >> "$GITHUB_ENV"La etiqueta sha-<commit> es inmutable: identifica exactamente un artefacto y no puede reescribirse. latest rompe la trazabilidad y la estrategia de recuperación.
Actualizar la configuración declarativa en Git
El mismo pipeline hace commit en el repo GitOps cambiando solo la etiqueta de la imagen:
- name: Actualizar repo GitOps
run: |
git clone https://x-access-token:${{ secrets.GITOPS_TOKEN }}@github.com/mi-org/gitops-config.git
cd gitops-config
sed -i "s|image: .*|image: $IMAGE:$TAG|" "apps/${{ github.event.repository.name }}/dev/deployment.yaml"
git commit -am "chore: ${{ github.event.repository.name }} $TAG"
git pushIntegración con entornos existentes y seguridad
Aislamiento de entornos
Cada entorno vive en su propio namespace, con cuotas de recursos, límites y políticas de red que impiden tráfico lateral entre entornos. Los overlays de dev, staging y producción solo cambian parámetros, no la estructura.
Permisos mínimos en Argo CD
Un AppProject limita qué repos y namespaces puede tocar cada equipo:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: equipo-pagos
namespace: argocd
spec:
sourceRepos:
- https://github.com/mi-org/gitops-config.git
destinations:
- namespace: pagos-dev
server: https://kubernetes.default.svc
clusterResourceWhitelist: []El detalle clave es clusterResourceWhitelist: []: sin esto, un equipo podría crear recursos de clúster y saltarse el aislamiento.
Gestión segura de secretos
Nada de secretos en el repositorio. Se inyectan desde el gestor de la nube con External Secrets Operator:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: laravel-secrets
spec:
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: laravel-secrets
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/laravel/db
property: passwordControles para producción
- Sin
automateden el overlay de producción: el sync requiere aprobación explícita. - PR obligatorio con revisión para cualquier cambio en producción.
- RBAC del clúster que impida a los equipos escribir directamente en namespaces de producción; todo pasa por Git.
La aplicación de Argo CD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: laravel-dev
namespace: argocd
spec:
project: equipo-pagos
source:
repoURL: https://github.com/mi-org/gitops-config.git
targetRevision: main
path: apps/laravel/dev
destination:
server: https://kubernetes.default.svc
namespace: laravel-dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=truePara producción se omite el bloque automated.
Promoción a staging y producción
La promoción no vuelve a construir: mueve la misma imagen que pasó las pruebas.
- CI valida la imagen en dev.
- Se abre un PR que copia la etiqueta a
apps/laravel/staging. - Al aprobarse y hacer merge, Argo CD sincroniza staging.
- Para producción, un segundo PR más un revisor adicional (configurable con environment protection rules de GitHub o GitLab).
- Argo CD sincroniza producción en el momento aprobado o según ventana de cambio.
Versiones asumidas: plantillas de Backstage
scaffolder.backstage.io/v1beta3, Argo CD 2.x conargoproj.io/v1alpha1y External Secrets Operatorexternal-secrets.io/v1beta1. Verifica la versión de tu instalación antes de copiar.
Un despliegue fallido: diagnóstico y recuperación
Supón que dev queda en ImagePullBackOff porque la etiqueta publicada no existe o el registro cambió de repositorio.
argocd app get laravel-dev
kubectl -n laravel-dev get pods
kubectl -n laravel-dev describe pod laravel-7d9c8f-xxxxSi el origen es la aplicación y el contenedor arranca y muere:
kubectl -n laravel-dev logs laravel-7d9c8f-xxxx --previousLa recuperación en GitOps es determinista: el estado deseado vive en Git. Se revierte el commit que cambió la etiqueta:
cd gitops-config
git revert HEAD
git push
argocd app sync laravel-devArgo CD vuelve a llevar el clúster a la versión anterior. En un incidente real también sirve argocd app rollback, pero úsalo como acción temporal: el arreglo permanente es el revert en Git, para que el repositorio no quede desincronizado del clúster.
Centralizando el servicio en Backstage
Cada repositorio lleva un catalog-info.yaml que relaciona el servicio con su equipo y sus enlaces:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: laravel
annotations:
argocd/app-name: laravel-dev
backstage.io/techdocs-ref: dir:.
spec:
type: service
lifecycle: production
owner: equipo-pagosCon eso, el portal muestra quién es responsable, la documentación y el enlace a la aplicación en Argo CD. Cuando alguien pregunta "¿quién mantiene esto?", la respuesta está en el catálogo, no en un chat.
Lecciones y métricas de adopción
- Empieza con una plantilla. Laravel y React con un flujo claro vale más que veinte plantillas a medias.
- El owner es obligatorio. Un servicio sin dueño en el catálogo genera deuda, no autoservicio.
- La plataforma es producto. Necesita dueño, roadmap y ciclo de versiones.
- Progresión de autonomía. Primero self-service de repo y pipeline; después, promoción con aprobaciones; al final, automatizaciones más agresivas solo si el equipo las entiende.
Métricas útiles para medir adopción, todas orientativas y dependientes del contexto:
- Tiempo desde "pido un servicio" hasta el primer despliegue en dev.
- Porcentaje de servicios creados por plantilla frente a los creados a mano.
- Despliegues con etiqueta inmutable frente al total.
- Tiempo medio de recuperación ante un despliegue fallido.
No hay umbrales universales: define tu línea base al inicio y compárala contigo mismo, no con otro equipo.
[COMPLETAR: comparte cifras reales de adopción o de reducción de tiempo de entrega que hayas medido, con su período de referencia y la forma en que las obtuviste.]
Resumen
Una plataforma interna de despliegue con Backstage, Argo CD y Kubernetes no es magia: es una plantilla que genera un repositorio estándar, un CI que construye una imagen inmutable, un repositorio GitOps que declara el estado deseado y Argo CD que lo sincroniza. La seguridad se apoya en permisos mínimos, entornos aislados, secretos externos y aprobación para producción.
Empieza por un equipo y un tipo de servicio, mide, y amplía cuando el flujo sea aburrido de tan predecible.
Si estás montando algo parecido, escríbeme y cuéntame en qué punto estás y qué te cuesta más: el catálogo, las plantillas o la promoción entre entornos. Con gusto lo revisamos juntos.


