The Infrastructure Hub -- Parte 3

Infraestructura Multi-tenant — Una Plataforma, Muchos Clientes

#platform-engineering #backstage #multi-tenant #msp #infrastructure

El Problema

Trabajas en un proveedor de servicios gestionados. O quizás eres el equipo de DevOps dentro de una empresa que da servicio a varias unidades de negocio. En cualquier caso, gestionas infraestructura para más de un cliente.

El Cliente A está en Azure. El Cliente B está en Scaleway. El Cliente C tiene una mezcla de AWS y Azure. Cada cliente tiene sus propias suscripciones, sus propias convenciones de nombres, sus propios requisitos de seguridad y su propio proceso de aprobación para los cambios.

Hoy controlas todo esto con:

  • Una hoja de cálculo que siempre está desactualizada
  • Una página compartida en OneNote/Confluence que nadie mantiene
  • Tickets en ServiceNow o Jira que mezclan el contexto del cliente con los detalles de la tarea
  • Tu cabeza (y la de tus compañeros)

Cuando un nuevo ingeniero se une al equipo, pasa dos semanas aprendiendo qué cliente usa qué. Cuando necesitas responder “¿cuántos clusters de AKS gestionamos en total?”, abres 15 portales de Azure y cuentas a mano.

Esto no escala. Y es peligroso — porque un terraform apply en la suscripción equivocada puede afectar al cliente equivocado.

La Solución

Backstage ya soporta multi-tenancy a través de su modelo de catálogo. Los conceptos clave:

  • Systems representan los entornos de cada cliente (por ejemplo, client-acme-infrastructure)
  • Components son los módulos de Terraform desplegados para cada cliente
  • Groups representan los equipos responsables de cada cliente
  • Templates se pueden limitar por cliente — el golden path del artículo 2 ya tiene un campo client

La misma instancia de Backstage sirve a todos. Pero cada equipo ve sus clientes, sus módulos, su historial. Y tú — como equipo de plataforma — ves todo.

Para un equipo interno de DevOps (no un MSP), el mismo modelo funciona: cambia “cliente” por “unidad de negocio” o “equipo de producto”. La arquitectura es la misma.

Ejecución

Paso 1: Definir los systems de cada cliente

Cada cliente tiene una entidad System en el catálogo. Esto agrupa toda su infraestructura:

# catalog/systems/client-acme.yaml
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: client-acme-infrastructure
  title: "ACME Corp — Infrastructure"
  description: "All infrastructure managed for ACME Corp"
  tags:
    - client
    - acme
    - azure
  annotations:
    backstage.io/techdocs-ref: dir:.
  links:
    - url: https://portal.azure.com
      title: Azure Portal (ACME subscription)
spec:
  owner: team-acme
  domain: managed-services
---
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: client-globex-infrastructure
  title: "Globex — Infrastructure"
  description: "All infrastructure managed for Globex"
  tags:
    - client
    - globex
    - scaleway
spec:
  owner: team-globex
  domain: managed-services

Paso 2: Definir los equipos por cliente

Cada cliente tiene un equipo asignado. Esto controla la propiedad y la visibilidad:

# catalog/groups/teams.yaml
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-acme
  title: "Team ACME"
  description: "Engineers assigned to ACME Corp"
spec:
  type: team
  children: []
  members:
    - victor.zaragoza
    - sarah.chen
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-globex
  title: "Team Globex"
  description: "Engineers assigned to Globex"
spec:
  type: team
  children: []
  members:
    - victor.zaragoza
    - james.wilson
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-platform
  title: "Platform Team"
  description: "Manages the platform itself — sees all clients"
spec:
  type: team
  children: []
  members:
    - victor.zaragoza

Paso 3: Módulos por cliente

Cuando creas un módulo para un cliente (usando el template Golden Path), el campo client determina el system y los tags:

# This is what the scaffolder generates for client = acme
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: tf-azurerm-vnet
  title: "Azure Virtual Network for ACME"
  description: "Hub VNet with 3 subnets, peering to spoke VNets"
  tags:
    - terraform
    - azure
    - client-acme
  annotations:
    github.com/project-slug: victorZKov/tf-azurerm-vnet
spec:
  type: terraform-module
  lifecycle: production
  owner: team-acme
  system: client-acme-infrastructure

El system: client-acme-infrastructure enlaza este módulo con el sistema de ACME. En el catálogo, puedes filtrar por system para ver toda la infraestructura de ACME en un solo lugar.

Paso 4: Configuración específica por cliente

Cada cliente tiene sus valores por defecto — suscripciones de Azure, convenciones de nombres, regiones permitidas. Guárdalos como una entidad de configuración en el catálogo:

# catalog/clients/client-acme-config.yaml
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
  name: client-acme-config
  title: "ACME — Configuration"
  description: "Default configuration for ACME infrastructure"
  tags:
    - config
    - client-acme
spec:
  type: client-config
  owner: team-acme
  system: client-acme-infrastructure
  dependsOn: []
  profile:
    cloud: azure
    subscription: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    defaultRegion: westeurope
    namingPrefix: "acme"
    namingConvention: "{prefix}-{env}-{resource}-{instance}"
    environments:
      - name: production
        approvalRequired: true
        approvers: ["ciso@acme.com", "cto@acme.com"]
      - name: staging
        approvalRequired: false
      - name: development
        approvalRequired: false
    allowedRegions:
      - westeurope
      - northeurope
    tags:
      managed-by: "victorz-msp"
      client: "acme"

Esta configuración alimenta:

  • El scaffolder — cuando creas módulos para ACME, pre-rellena la suscripción, región y nombres
  • El AI enricher — cuando analiza el código de ACME, conoce las convenciones de nombres
  • El flujo CAB (artículo 6) — los cambios en producción necesitan aprobación del CISO + CTO

Paso 5: El dashboard del equipo de plataforma

El equipo de plataforma necesita una vista global. Extendemos el Governance Dashboard para mostrar métricas a nivel de cliente:

// plugins/ai-governance/src/components/ClientOverview.tsx
import React, { useEffect, useState } from 'react';
import {
  Table, TableColumn, InfoCard,
} from '@backstage/core-components';
import { useApi, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';

interface ClientSummary {
  client: string;
  cloud: string;
  moduleCount: number;
  lastChange: string;
  driftStatus: string;
}

export const ClientOverview = () => {
  const discoveryApi = useApi(discoveryApiRef);
  const fetchApi = useApi(fetchApiRef);
  const [clients, setClients] = useState<ClientSummary[]>([]);

  useEffect(() => {
    const load = async () => {
      const catalogUrl = await discoveryApi.getBaseUrl('catalog');
      const res = await fetchApi.fetch(
        `${catalogUrl}/entities?filter=kind=system,spec.domain=managed-services`,
      );
      const systems = await res.json();

      const summaries: ClientSummary[] = systems.map((s: any) => ({
        client: s.metadata.name.replace('-infrastructure', '').replace('client-', ''),
        cloud: s.metadata.tags?.find((t: string) =>
          ['azure', 'aws', 'scaleway', 'gcp'].includes(t)) || 'unknown',
        moduleCount: 0,
        lastChange: 'N/A',
        driftStatus: 'OK',
      }));

      setClients(summaries);
    };
    load();
  }, [discoveryApi, fetchApi]);

  const columns: TableColumn<ClientSummary>[] = [
    { title: 'Client', field: 'client' },
    { title: 'Cloud', field: 'cloud' },
    { title: 'Modules', field: 'moduleCount', type: 'numeric' },
    { title: 'Last Change', field: 'lastChange' },
    { title: 'Drift', field: 'driftStatus' },
  ];

  return (
    <InfoCard title="Client Overview">
      <Table
        columns={columns}
        data={clients}
        options={{ paging: false, search: true }}
      />
    </InfoCard>
  );
};

Cómo se ve en la práctica

Como ingeniero del Team ACME:

Abres Backstage. El catálogo muestra la infraestructura de ACME: 4 módulos de Terraform (VNet, AKS, SQL, Storage), todos bajo client-acme-infrastructure. Haces clic en “Create”, seleccionas “Golden Path Terraform Module”, y el campo client ya viene con “acme”. El módulo generado aterriza en el system correcto, con los tags correctos, las convenciones de nombres y la suscripción adecuada.

Como equipo de plataforma:

Abres el dashboard de Client Overview. Ves los 15 clientes en una tabla: qué nube usa cada uno, cuántos módulos tienen, cuándo fue el último cambio y si hay drift. Haces clic en “acme” y ves su catálogo completo de infraestructura. Haces clic en “globex” y ves el suyo.

Como responsable del MSP:

Necesitas responder “¿qué clientes usan clusters de AKS?”. Buscas en el catálogo por type:terraform-module + tag kubernetes. Tres clientes. Necesitas planificar una actualización de AKS en todos ellos. Sabes exactamente qué módulos actualizar y quién es responsable de cada uno.

La API del catálogo de Backstage soporta filtros. Consultas útiles para MSPs:

# All modules for a specific client
/api/catalog/entities?filter=spec.system=client-acme-infrastructure

# All Azure modules across all clients
/api/catalog/entities?filter=spec.type=terraform-module,metadata.tags=azure

# All production modules (any client)
/api/catalog/entities?filter=spec.type=terraform-module,spec.lifecycle=production

# All modules owned by a specific team
/api/catalog/entities?filter=spec.type=terraform-module,spec.owner=team-acme

Estas consultas también funcionan en la interfaz de Backstage — la página del catálogo tiene desplegables de filtro para kind, type, owner y tags.

Checklist

  • Systems de clientes definidos en el catálogo (kind: System)
  • Equipos asignados por cliente (kind: Group)
  • Los módulos creados con el scaffolder aterrizan en el system correcto
  • La entidad de configuración del cliente tiene cloud, suscripción, convenciones de nombres
  • El equipo de plataforma puede filtrar todos los clientes
  • La búsqueda del catálogo devuelve resultados correctos por cliente
  • TechDocs se renderiza por módulo de cliente

Reto

Antes del siguiente artículo:

  1. Crea dos systems de clientes en tu catálogo
  2. Crea un módulo con el scaffolder para cada cliente — verifica que aterrizan en el system correcto
  3. Usa los filtros del catálogo para responder: “¿qué módulos tiene el cliente X?”

En el siguiente artículo, construimos Pipelines desde Backstage — crear y gestionar pipelines de CI/CD para Azure DevOps, GitHub Actions y GitLab CI desde un solo sitio. Se acabó cambiar entre tres interfaces de pipelines diferentes.

El código completo está en GitHub.

Si esta serie te ayuda, puedes invitarme a un café.

Este es el artículo 3 de la serie Infrastructure Hub. Anterior: Golden Path Terraform Modules. Siguiente: Pipelines desde Backstage — una interfaz para todo tu CI/CD.

Comments

Loading comments...