Operar la IDP: Docker, kind, OpenTofu, Crossplane, observabilidad

Objetivos del capítulo
  • Componer la IDP en local con Docker Compose y kind.

  • Aprovisionar infra reproducible con OpenTofu y Crossplane.

  • Monitorizar y observar la IDP.

  • Preparar disaster recovery y plan de upgrades.

  • Aplicar multi-tenant y aislamiento.

Operar la cocina: del dev al cluster

Una IDP no es un experimento: hay que servirla todos los días, con la misma fiabilidad que el plato del mediodía. Este capítulo es el que te enseña a llevar el restaurante: neveras (Docker), hornos (Kubernetes), pedidos al proveedor (IaC), bandejas de control de calidad (observabilidad) y el plan anti-incendios (backup + DR).

Stack local con Docker Compose

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: backstage
      POSTGRES_USER: backstage
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U backstage"]
      interval: 5s

  backstage:
    build:
      context: ..
      dockerfile: Dockerfile
    environment:
      POSTGRES_HOST: postgres
      POSTGRES_PORT: "5432"
      POSTGRES_USER: backstage
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "7007:7007"
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  pgdata:
Healthcheck obligatorio

El :code:`healthcheck` de Postgres evita que Backstage arranque antes de que la DB esté lista. Sin él verás crashes intermitentes al levantar el compose.

Cluster local con kind

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 3000
        hostPort: 3000
      - containerPort: 7007
        hostPort: 7007
  - role: worker
  - role: worker
kind no es para producción

kind usa contenedores Docker como nodos. Sirve para CI y desarrollo local. Para producción usa EKS, AKS, GKE o tu distribución on-prem preferida. Los YAMLs son los mismos; cambian los manifiestos del cluster (CNI, ingress, RBAC).

IaC con OpenTofu

terraform {
  backend "s3" {
    bucket         = "sazon-tofu-state"
    key            = "idp/terraform.tfstate"
    region         = "eu-west-1"
    dynamodb_table = "sazon-tofu-locks"
    encrypt        = true
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.50"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.30"
    }
  }
}
Nunca :code:`tofu apply` directo en CI

El flujo correcto es: PR con plan, revisión humana, merge, apply desde CI con OIDC. Si aplicas sin revisión, todo cambio queda fuera del audit trail. Ver :apéndice C sobre buenas prácticas de IaC.

Crossplane: CRDs como infra

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws-rds
spec:
  package: xpkg.upbound.io/upbound/provider-aws-rds:v0.43.0
  controllerConfig:
    name: aws-config
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.com
spec:
  group: database.example.com
  names:
    kind: XPostgreSQLInstance
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    storageGB:
                      type: integer
                      default: 20
                    instanceClass:
                      type: string
                      default: db.t3.micro
              required: [storageGB, instanceClass]
Diagram
Figure 22. Arquitectura cloud: OpenTofu + Crossplane

Desplegar Backstage en Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backstage
  namespace: backstage
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backstage
  template:
    metadata:
      labels:
        app: backstage
    spec:
      containers:
        - name: backstage
          image: ghcr.io/sazon-foods/backstage:1.31.0
          ports:
            - containerPort: 7007
          envFrom:
            - secretRef:
                name: backstage-secrets
          readinessProbe:
            httpGet:
              path: /healthcheck
              port: 7007
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: backstage
  namespace: backstage
spec:
  selector:
    app: backstage
  ports:
    - port: 80
      targetPort: 7007
Helm vs YAMLs pelados

Los YAMLs sirven para entender; Helm sirve para operar. El chart oficial backstage en https://backstage.github.io/charts parametriza imágenes, secrets, ingress, y RBAC. En producción usa Helm.

Observabilidad: métricas, logs y traces

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: backstage
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: backstage
  endpoints:
    - port: http
      path: /metrics
      interval: 30s
Diagram
Figure 23. Stack de observabilidad
SLOs recomendados
  • Disponibilidad: 99.5% mensual (Backstage no es crítico, pero no debe estar caído).

  • Latencia p95: < 500 ms en /catalog, < 1.5 s en scaffolder (incluye clonado de repo).

  • Tasa de error: < 1% en endpoints del catalog.

Logs y alertas

Logging stack

Loki + Promtail para logs. Fluent Bit si ya tienes ELK. El plugin @backstage/plugin-search-backend puede indexar logs por entityRef.

Alertas que importan
  • Caída de Backstage (up == 0).

  • Latencia p95 > 2 s sostenida.

  • Errores 5xx > 1% en 5 min.

  • Postgres: connections > 80% del pool, replication lag > 30 s.

Las alertas deben ser actionable: cada alerta tiene un runbook.

Backup y disaster recovery

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR=${BACKUP_DIR:-/var/backups/backstage}
TS=$(date -u +%Y%m%dT%H%M%SZ)
DEST="${BACKUP_DIR}/${TS}"
mkdir -p "${DEST}"

echo "[$(date)] Dumping Postgres..."
pg_dump -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -d backstage \
  --format=custom -f "${DEST}/backstage.dump"

echo "[$(date)] Copying uploaded TechDocs..."
rsync -a /backstage/techdocs/ "${DEST}/techdocs/"

echo "[$(date)] Uploading to S3..."
aws s3 cp "${DEST}" "s3://sazon-backstage-backups/${TS}/" --recursive

# Retention: keep 30 days
find "${BACKUP_DIR}" -maxdepth 1 -mindepth 1 -mtime +30 -exec rm -rf {} +

echo "[$(date)] Backup done at ${DEST}"
3-2-1

3 copias, 2 medios distintos, 1 off-site. En la nube: S3 + Glacier + cross-region. En on-prem: NAS + cinta +异地复制. Testea el restore mensualmente; un backup no testeado es un backup inexistente.

Upgrades: el baile de versiones

Codemods y cadence
  • Backstage publica codemods (scripts JS) que migran APIs deprecadas.

  • Cadencia: actualizar dentro de 2 versiones de cada release. Si vas 6 versiones atrás, el upgrade es doloroso.

  • Estrategia: branch dedicado + runbook + ventana de mantenimiento + rollback automático.

Multi-tenant: aislamiento por namespace

Diagram
Figure 24. Multi-tenant con namespaces, Systems y Domains
Sistemas vs Dominios
  • System = unidad de producto (sazon-restaurant).

  • Domain = área de negocio (restaurant, payments).

  • Namespace = aislamiento físico (prod, staging, dev).

Multi-tenant = combina las tres para evitar que dos equipos pisen entidades comunes.

Plugins extra útiles

El ecosistema crece rápido
  • TechDocs: docs-as-code desde el repo.

  • ArgoCD: vista de deployments GitOps.

  • Kubernetes: vista de workloads + logs.

  • Cost Insights: costes de cloud por equipo.

Evalúa plugins con criterios: maintenance status, versiones soportadas, contributor count. Un plugin abandonado es una trampa.

Example 15. Receta del capítulo
  1. Levanta el stack local con docker compose up.

  2. Crea un cluster kind create cluster --config kind-config.yaml.

  3. Aplica el Deployment y el Service de Backstage.

  4. Configura OpenTofu con backend S3 + DynamoDB lock.

  5. Instala Crossplane con un provider de tu cloud.

  6. Configura ServiceMonitor + Prometheus + Grafana.

  7. Programa el backup y testea el restore.

Resumen

  • Compose y kind valen para dev; Kubernetes gestionado vale para producción.

  • OpenTofu + S3 backend + DynamoDB lock = state compartido con concurrencia segura.

  • Crossplane da CRDs para AWS RDS, S3, etc., con self-service.

  • Observabilidad: Prometheus para métricas, Loki para logs, OpenTelemetry para traces.

  • Backup con 3-2-1 + restore testeado mensualmente.

  • Upgrades: codemods + cadence (dentro de 2 versiones).

Glosario del capítulo

Docker Compose

Herramienta para correr multi-contenedor en un solo host.

kind

Kubernetes-in-Docker. Cluster K8s en contenedores Docker locales.

OpenTofu

Fork open source de Terraform, mantenido por la Linux Foundation.

Crossplane

Plataforma K8s que expone recursos cloud como CRDs.

ServiceMonitor

CRD de Prometheus Operator para descubrir métricas.

SLO

Service Level Objective. Objetivo medible (latencia, disponibilidad).

Codemod

Script automatizado que reescribe código entre versiones.

Próximo capítulo

Backstage vs Port vs Compass vs Cortex: guía de compra —comparativa honesta y argumentario para una CTO.

Parte VI: Parte 6 — Comparativa y cierre

Backstage contra Port, Compass y Cortex. Métricas de adopción, ROI y guía de decisión para CTOs.