Infraestructura
Si crees que una buena arquitectura es cara, prueba con una mala arquitectura.
El mensaje de Slack de Diego llegó a las 4:45 PM de un viernes.
“Hola Javi, ¡excelentes noticias! El negocio acaba de aprobar nuestra expansión a Europa para cumplir con el GDPR. Necesitamos desplegar las APIs de user-service y checkout a una región de la UE. ¿Puede el equipo simplemente cambiar la bandera region en nuestros manifiestos de despliegue y hacer push?”
Javi, el ingeniero líder de plataformas en Mountain Lab, se quedó mirando la pantalla. Escribió, borró y volvió a escribir su respuesta tres veces.
“Eh, bueno… no,” respondió Javi finalmente. “No es tan sencillo.”
El indicador de que Diego estaba escribiendo parpadeó de inmediato. “¿Por qué no? Pensé que el objetivo de esta Plataforma Interna (IDP) era que nos permitiera desplegar en cualquier lugar sin abrir tickets. Dijiste que la infraestructura estaba abstraída.”
Javi suspiró y abrió un enlace de videollamada. Cuando Diego se unió, Javi compartió su pantalla, que mostraba una enredada telaraña de ventanas de la consola de AWS y pestañas de hojas de cálculo. “El despliegue de la aplicación está abstraído,” explicó Javi, frotándose las sienes. “El problema es que la infraestructura subyacente aún no es dinámica. Si levantamos una nueva región, tengo que asignar manualmente un nuevo bloque CIDR sin superposiciones para la VPC europea. Si hago mal los cálculos, tendremos colisiones de IP cuando conectemos las redes.”
“Vale, entonces asigna el bloque. ¿Cuánto toma eso?” preguntó Diego.
“Ese es solo el paso uno,” continuó Javi. “Luego tengo que configurar el Transit Gateway para enrutar el tráfico entre eu-central-1 y us-east-1 para que el servicio de checkout aún pueda acceder a la base de datos heredada de inventario. Después necesito crear un nuevo API Gateway, configurar las reglas regionales del WAF y enviar un ticket de Jira a InfoSec para que las aprueben. Después de eso, tenemos que generar un nuevo certificado SSL comodín, validarlo y actualizar manualmente el enrutamiento global del DNS en Cloudflare.”
Diego se quedó en silencio por un momento. “Marketing ya anunció el lanzamiento en Europa para el próximo martes.”
Javi abrió una nueva épica en Jira. “Dame tres semanas. Si InfoSec aprueba las reglas del WAF al primer intento.”
Marta, la Vicepresidenta de Ingeniería de Plataforma, había estado escuchando la llamada en silencio. Activó su micrófono. “No hemos construido una plataforma,” dijo rotundamente. “Solo hemos construido un sistema de tickets muy eficiente donde Javi es la única API.”
La realidad los golpeó duro. Habían abstraído con éxito los pipelines de CI/CD, pero la infraestructura base (redes, DNS, SSL y el aprovisionamiento de cómputo) seguía arraigada en “ClickOps” y coordinación manual. Para soportar un verdadero autoservicio, necesitaban un enfoque de infraestructura donde agregar una región, establecer segmentos de red seguros y gestionar el enrutamiento de borde fueran procesos completamente declarativos y automatizados por la plataforma.
A menudo pensamos en las Plataformas Internas (IDPs) puramente en términos de portales de desarrollo o pipelines de despliegue. Pero si la infraestructura subyacente depende de un aprovisionamiento manual, sistemas de tickets o configuración a través de interfaces de usuario, la plataforma es simplemente una fachada brillante sobre un cuello de botella de TI tradicional. Este trabajo tedioso restringe el flujo y evita escalar.
En este capítulo, exploraremos las primitivas de infraestructura necesarias para soportar una plataforma interna escalable. Cubriremos cómo establecer una base declarativa con Infraestructura como Código (IaC), implementar segmentación de red física, imponer guardarailes mediante cuotas de recursos y automatizar el enrutamiento de borde a través de proveedores de nube pública.
Infraestructura como Código
Antes de que podamos hablar sobre patrones de red o abstracciones de cómputo, debemos establecer la base absoluta para la ingeniería de plataformas: todo debe ser código. La Infraestructura como Código (IaC) utilizando herramientas como Terraform, OpenTofu o Pulumi no es negociable.
El aprovisionamiento manual de infraestructura —“ClickOps”— lleva directamente a la desviación de la configuración. Cuando un ingeniero configura manualmente una red virtual de Azure (VNet) o un AWS Transit Gateway, ese estado existe solo en la base de datos propietaria del proveedor de nube. No puede ser revisado por pares, no puede ser auditado para buscar regresiones de seguridad y, de manera crucial, no puede ser replicado de manera confiable en una nueva región o entorno.
Infraestructura como Máquina Expendedora
Para un equipo de plataforma, la IaC no se trata solo de escribir scripts para levantar servidores; se trata de crear módulos reutilizables y con opinión. El equipo de plataforma actúa como un proveedor de componentes de infraestructura.
Los desarrolladores no deberían escribir bloques crudos del proveedor de nube. En su lugar, el equipo de plataforma provee un módulo estándar (un “camino dorado”) que encapsula los requisitos de seguridad, cumplimiento y alta disponibilidad de la organización. Además, estos módulos imponen automáticamente requisitos no funcionales como Convenciones de Nomenclatura de Recursos y Estrategias de Etiquetado.
# Los desarrolladores consumen un módulo simplificado, provisto por la plataforma.
# El módulo impone el etiquetado, las convenciones de nomenclatura y los valores predeterminados de seguridad.
module "database" {
source = "git::https://git.mountainlab.io/tf-modules//rds-postgres?ref=v2.1.0"
# Los recursos se mapean directamente a la estrategia de segmentación
sector = "ecommerce"
tier = "live"
region = "eu01"
tenant = "payments"
environment = "production"
instance_class = "db.r6g.large"
# El módulo fuerza internamente storage_encrypted = true,
# multi_az = true, genera nombres de recursos estandarizados,
# y aplica etiquetas obligatorias de asignación de costos basadas en las coordenadas.
}
Al abstraer la complejidad, garantizas que cada base de datos desplegada en la plataforma encripte los datos en reposo, envíe los registros al agregador central, resida en el segmento de red correcto y posea las etiquetas estandarizadas que requiere el equipo de finanzas para la atribución de costos.
Convenciones de Nomenclatura de Recursos
Uno de los aspectos más tediosos del aprovisionamiento de infraestructura es adherirse a las convenciones de nomenclatura. Algunos proveedores de nube imponen límites estrictos de caracteres o requieren nombres globalmente únicos (por ejemplo, los buckets de S3 en AWS o las Storage Accounts en Azure).
El equipo de plataforma debería abstraer esto por completo del desarrollador. Recomiendo un enfoque de nomenclatura estructurado para los recursos que no necesitan ser globalmente únicos (por ejemplo, vnet-ecommerce-live-eu01), combinado con hashes aleatorios o sufijos para los recursos que requieren unicidad global (por ejemplo, bucket-payments-live-eu01-a7x9).
Los desarrolladores nunca deberían tener que pensar en nombrar los recursos de la nube.
Estrategia de Etiquetado
Cada recurso desplegado por la plataforma debe estar etiquetado con un conjunto estándar de metadatos. Esto permite que tanto los humanos como la automatización consulten, identifiquen y gobiernen los recursos sin depender del nombre físico del recurso. Esto es fundamental tanto para la gestión de costos como para la eficiencia operativa.
Para evitar conflictos con las etiquetas generadas por el proveedor de nube o herramientas de terceros, deberías poner un prefijo en todas las etiquetas personalizadas con el dominio de tu empresa. Sin embargo, el formato no es homogéneo entre los proveedores. Debes adaptar la sintaxis al contexto:
- Etiquetas en AWS / Azure / GCP:
<empresa>:<clave>(por ejemplo,mountainlab.io:owner) - Labels en Kubernetes:
<empresa>/<clave>(por ejemplo,mountainlab.io/owner)
La clave es un prefijo consistente y una taxonomía bien definida que se imponga globalmente. Un conjunto estándar de etiquetas obligatorias provistas a través de tus módulos de IaC debería incluir:
mountainlab.io:tenant— El equipo de producto que es dueño de la carga de trabajo.mountainlab.io:sector— El contexto delimitado o dominio de negocio.mountainlab.io:tier— La clasificación del entorno (por ejemplo,Tier("live"),Tier("sandbox")).mountainlab.io:region— La región de la plataforma.mountainlab.io:tf_module— El nombre del módulo de Terraform que creó el recurso.mountainlab.io:tf_module_version— La versión específica del módulo, invaluable para rastrear la desviación de la infraestructura y las campañas de actualización.
Más allá de estas etiquetas estructurales, recomiendo implementar un conjunto más amplio de etiquetas de mejores prácticas para el gobierno y las operaciones:
- Propiedad:
mountainlab.io:owner(alias de equipo o correo electrónico) ymountainlab.io:business-unit. - Finanzas:
mountainlab.io:cost-centerpara reembolsos precisos ymountainlab.io:projectpara el seguimiento de iniciativas. - Seguridad:
mountainlab.io:data-classification(por ejemplo,public,confidential,pci) ymountainlab.io:criticality(por ejemplo,tier-1,mission-critical). - Automatización:
mountainlab.io:maintenance-window(por ejemplo,sun-0400-0800-utc) ymountainlab.io:created-by(por ejemplo,terraform,crossplane).
El Modelo de Responsabilidad Compartida
Con una base declarativa establecida, debemos definir a quién le pertenece qué. Tratar la infraestructura como un producto de autoservicio requiere un Modelo de Responsabilidad Compartida claro y codificado. Este modelo actúa como un contrato entre tres partes distintas. Cada nivel abstrae la complejidad subyacente, ofrece soporte y permite que el siguiente nivel construye de forma segura sobre él:
flowchart TD
Product["Equipos de Producto<br>(Lógica de Negocio, Configuración de la App, Microsegmentación)"]
Platform["Equipo de Plataforma<br>(APIs de Autoservicio, Red Base, Guardarailes, Módulos IaC)"]
Cloud["Proveedor de Nube<br>(Centros de Datos Físicos, Hardware, Servicios Gestionados)"]
Product -->|Consume Servicios y Módulos de la Plataforma| Platform
Platform -->|Abstrae y Gobierna| Cloud
- El Proveedor de Nube (AWS/GCP/Azure): Abstrae los centros de datos físicos, el hardware y la red, ofreciendo servicios gestionados y sus planos de control (por ejemplo, EKS, GKE, AKS, Cloud SQL). Prefiero los servicios gestionados siempre que sea posible para reducir el trabajo tedioso operativo del equipo de plataforma.
- El equipo de plataforma: Construye sobre los servicios gestionados del proveedor de nube creando plataformas internas que proporcionan capacidades de autoservicio, automatización y guardarailes. Por ejemplo, el equipo de plataforma implementa controles de seguridad de base (como imponer encriptación en tránsito o centralizar registros), aprovisiona redes base, gestiona el DNS global y provee los módulos estandarizados de IaC.
- Los Equipos de Producto (Equipos de Desarrollo): Consumen esas plataformas internas para construir productos de cara al cliente. Utilizan las capacidades de autoservicio de la plataforma para desplegar una nueva funcionalidad sin tener que esperar a un equipo de operaciones centralizado. Son responsables de la configuración a nivel de aplicación, definir variables de entorno, seleccionar “tallas de camiseta” de cómputo y aplicar microsegmentación para sus cargas de trabajo específicas (por ejemplo, escribiendo recursos
NetworkPolicyen Kubernetes).
Este contrato asegura que los equipos de producto no necesiten entender el enrutamiento BGP o las negociaciones de certificados TLS para desplegar un servicio de manera segura, mientras que el equipo de plataforma no se carga con la configuración de la lógica de negocio específica de la aplicación.
Cloud Primitives
La segmentación, como discutimos en el Capítulo 4, depende en gran medida de primitivas de nube para establecer límites estrictos entre los diferentes segmentos. Esto es lo que usamos para aislar cargas de trabajo, establecer guardarailes, aplicar cuotas de recursos e imponer controles de seguridad estrictos. Utilizando la Notación de Plataforma, podemos definir formalmente estos límites de infraestructura como tipos:
- Definición de Tipo:
CloudPrimitive(Sector, Tier) - Definición de Tipo:
Network(Sector, Tier, Region) - Definición de Tipo:
Spoke(Sector, Tier, Region) - Definición de Tipo:
Space(Sector, Tier, Region, Tenant)
flowchart TD
Org["Organización Raíz / Tenant"]
Folder["Unidad Organizativa / Carpeta / Grupo de Gestión"]
Boundary["CloudPrimitive / Cuenta / Proyecto / Suscripción"]
Group["Grupo de Recursos"]
Compute["Namespace en Kubernetes / Space"]
Org --> Folder
Folder --> Boundary
Boundary --> Group
Group --> Compute
Las arquitecturas modernas aprovechan las jerarquías estructurales nativas proporcionadas por los proveedores de nube para gestionar el gobierno y la escala. Aunque cada proveedor utiliza una terminología diferente, todos siguen un enfoque de capas que separa el control de políticas amplio del aislamiento de recursos estricto:
- AWS:
Organization->Unidades Organizativas (OUs)->Cuentas (Accounts). Las OUs proporcionan control jerárquico de políticas a través de Service Control Policies (SCPs), mientras que las Accounts sirven como el límite de aislamiento más fuerte para la identidad, la facturación y las cuotas de API. - GCP:
Organización->Carpetas (Folders)->Proyectos (Projects). Las Folders permiten la herencia de políticas anidadas y la agrupación lógica, mientras que los Projects son la unidad fundamental para el aislamiento de recursos, IAM y gestión de cuotas. - Azure:
Tenant->Grupos de Gestión (Management Groups)->Suscripciones (Subscriptions)->Grupos de Recursos (Resource Groups). Los Management Groups agregan suscripciones para la aplicación de políticas a gran escala, mientras que las Subscriptions proporcionan el límite principal de facturación y cuotas. Los Resource Groups se utilizan para la delegación granular de RBAC y la gestión del ciclo de vida de los recursos relacionados.
Es crucial distinguir entre las herramientas utilizadas para organización y las herramientas utilizadas para aislamiento. Construcciones como OrganizationalUnit en AWS, Folder en GCP y ManagementGroup en Azure son principalmente para la organización jerárquica y la aplicación de políticas de gobierno amplias; no proporcionan aislamiento de cargas de trabajo.
Mapeo Estructural por Proveedor
Para implementar la Notación de Plataforma en un entorno real, debemos mapear nuestras dimensiones abstractas a estas estructuras concretas de los proveedores de nube. No existe un único mapeo “correcto”; su organización debe elegir en función de la tolerancia al radio de impacto y la capacidad operativa.
Los siguientes ejemplos muestran un diseño común y probado para cada proveedor principal utilizando nuestra notación funcional:
Mapeo Estructural en Microsoft Azure
Azure proporciona una jerarquía bien definida de alcances de gestión. Un enfoque común asigna el Sector al ManagementGroup y el Tier a la Subscription:
AzureManagementGroup(Sector)implementaCloudPrimitive(Sector)AzureSubscription(Sector, Tier)implementaCloudPrimitive(Sector, Tier)AzureResourceGroup(Sector, Tier, Region, Tenant)implementaSpace(Sector, Tier, Region, Tenant)AzureVNet(Sector, Tier, Region)implementaNetwork(Sector, Tier, Region)
graph TD
Root["TenantRootGroup"]
Root --> PMG["AzureManagementGroup('platform')"]
Root --> ECMG["AzureManagementGroup('ecommerce')"]
PMG --> PS["AzureSubscription('platform', 'sandbox')"]
PMG --> PL["AzureSubscription('platform', 'live')"]
ECMG --> ECS["AzureSubscription('ecommerce', 'sandbox')"]
ECMG --> ECL["AzureSubscription('ecommerce', 'live')"]
ECS --> RG1["AzureResourceGroup('ecommerce', 'sandbox', 'eu01', 'payments')"]
ECL --> RG2["AzureResourceGroup('ecommerce', 'live', 'eu01', 'payments')"]
Los Management Groups hacen cumplir las políticas de Azure y RBAC a nivel del Sector. Las Subscriptions proporcionan facturación, identidad y aislamiento del límite de API; aquí mapeados a (Sector, Tier). Los Resource Groups proporcionan delegación RBAC y agrupación de recursos; aquí mapeados a la coordenada completa (Sector, Tier, Region, Tenant).
Para organizaciones que requieren un aislamiento de tenant más estricto, una alternativa es crear suscripciones por (Sector, Tier, Tenant) —por ejemplo, una suscripción dedicada payments-live.
Mapeo Estructural en Amazon Web Services
AWS se basa principalmente en cuentas aisladas unidas por una Organization. Debido a que AWS no tiene un equivalente a los ResourceGroup de Azure, el límite de la cuenta a menudo absorbe la dimensión del Tenant:
AWSOrganizationalUnit(Sector)implementaCloudPrimitive(Sector)AWSAccount(Sector, Tier, Tenant)implementaCloudPrimitive(Sector, Tier, Tenant)AWSVPC(Sector, Tier, Region, Tenant)implementaNetwork(Sector, Tier, Region, Tenant)
graph TD
Root["AWSOrganizationRoot"]
Root --> POU["AWSOrganizationalUnit('platform')"]
Root --> ECOU["AWSOrganizationalUnit('ecommerce')"]
POU --> PSA["AWSAccount('platform', 'sandbox')"]
POU --> PLA["AWSAccount('platform', 'live')"]
ECOU --> SBOU["SubOU('sandbox')\n(Tier grouping)"]
ECOU --> LOU["SubOU('live')\n(Tier grouping)"]
SBOU --> PaySb["AWSAccount('ecommerce', 'sandbox', 'payments')"]
LOU --> PayLv["AWSAccount('ecommerce', 'live', 'payments')"]
PayLv --> VPC["AWSVPC('ecommerce', 'live', 'eu01', 'payments')"]
Las Organizational Units aplican Políticas de Control de Servicio a nivel del Sector. Las Accounts proporcionan el límite de aislamiento más duro: IAM, facturación y límites de API tienen el alcance de la cuenta; aquí se asignan como (Sector, Tier, Tenant), con recursos específicos de la región como AWSVPC creados dentro de cada cuenta.
Para organizaciones más pequeñas, una alternativa más simple es usar cuentas solo por (Sector, Tier) (por ejemplo, ecommerce-sandbox y ecommerce-live) con el aislamiento de tenant manejado a través de límites IAM dentro de la cuenta.
Mapeo Estructural en Google Cloud Platform
GCP utiliza una jerarquía de recursos de Organization, Folder y Project. Los proyectos son el primitivo de aislamiento primario, análogo a las cuentas de AWS:
GCPFolder(Sector)implementaCloudPrimitive(Sector)GCPProject(Sector, Tier, Tenant)implementaCloudPrimitive(Sector, Tier, Tenant)GCPResource(Sector, Tier, Region, Tenant)implementaSpace(Sector, Tier, Region, Tenant)GCPVPC(Sector, Tier, Region)implementaNetwork(Sector, Tier, Region)
graph TD
Root["GCPOrganization"]
Root --> PF["GCPFolder('platform')"]
Root --> ECF["GCPFolder('ecommerce')"]
PF --> PP["GCPProject('platform', 'sandbox')"]
PF --> PPL["GCPProject('platform', 'live')"]
ECF --> SBF["GCPFolder('sandbox')\n(Tier grouping)"]
ECF --> LF["GCPFolder('live')\n(Tier grouping)"]
SBF --> PSP["GCPProject('ecommerce', 'sandbox', 'payments')"]
LF --> PLP["GCPProject('ecommerce', 'live', 'payments')"]
Las Folders proporcionan la herencia de OrganizationPolicy y agrupación IAM. Los Projects son el límite duro: la facturación, el IAM y las cuotas tienen alcance de proyecto. Los recursos específicos de la región se crean dentro de los proyectos, mapeando a la coordenada completa (Sector, Tier, Region, Tenant).
Aislamiento de Tenants
Dentro de estos límites, debemos aislar a los tenants individuales. La forma en que se logra esto depende del proveedor de nube:
- Azure: Los
AzureResourceGroupactúan como un límite estricto donde se puede aplicar el Control de Acceso Basado en Roles (RBAC). - AWS: Los
AWSResourceGroupson estrictamente para el etiquetado organizativo y no proporcionan aislamiento de seguridad; por lo tanto, el aislamiento de tenants en AWS a menudo requiere cuentas de AWS completamente separadas. - GCP: El aislamiento de tenants se logra utilizando recursos
GCPProjectseparados.
Independientemente de la primitiva utilizada, los tenants nunca deberían gestionar estos límites por sí mismos. El equipo de plataforma aprovisiona y gobierna el Grupo de Recursos, Cuenta o Proyecto para asegurar que haya políticas estrictas de seguridad y permisos implementados. Al tenant simplemente se le otorga acceso para desplegar en él. Abajo, en la capa de cómputo, el equipo de plataforma proporciona Namespaces de Kubernetes para aislar los recursos del clúster del tenant.
Redes
La red es la manifestación física de tu estrategia de segmentación. Una red plana donde cada carga de trabajo puede enrutar tráfico hacia cualquier otra carga de trabajo es un anti-patrón de seguridad crítico.
La Arquitectura Hub and Spoke
Para imponer límites estrictos a nivel de infraestructura, el estándar de la industria en todas las nubes públicas es la topología de red Hub and Spoke.
El Hub está diseñado para VPN, conectividad global e inspección centralizada. Es un punto de enrutamiento central y único (usando AWS Transit Gateway, Azure Virtual WAN o GCP Network Connectivity Center) que conecta a la organización a nivel mundial.
En lugar de desplegar todos los servicios en el Hub, las cargas de trabajo viven en redes Spoke aisladas (VPCs en AWS/GCP, VNets en Azure). Una buena práctica es aprovisionar un Spoke para cada combinación de (Sector, Tier, Region).
Por defecto, las redes Spoke no pueden comunicarse entre sí. Si la checkout-api en el Spoke de ecommerce necesita hablar con la ledger-db en el Spoke de finance, el tráfico debe atravesar el Hub, donde el equipo de plataforma puede imponer reglas de firewall centralizadas.
Crucialmente, esta arquitectura depende de una asignación de direcciones IP sin superposiciones. Para que el Hub pueda enrutar el tráfico entre los Spokes, cada red debe poseer un bloque CIDR único. Si dos Spokes comparten el mismo rango de IPs, no pueden conectarse al mismo Hub, lo que lleva a un estado de red “dividida” (split-brain) que es extremadamente difícil de remediar.
Las cargas de trabajo de los tenants generalmente están aisladas en sus propios namespaces dentro de un Spoke compartido. En algunos casos con necesidades estrictas de cumplimiento o de referencia de datos, un tenant específico podría requerir su propio Spoke dedicado, aunque este compromiso aumenta significativamente la sobrecarga operativa.
Además, debemos diseñar para el Egress. Existen dos enfoques principales:
- Egress Centralizado: Todo el tráfico de salida se enruta desde los Spokes de vuelta a través de los Gateways NAT del Hub y proxies de salida para evitar la exfiltración de datos y centralizar la inspección.
- Egress Descentralizado: Los Spokes tienen sus propios Gateways NAT locales para la salida directa a internet. Esto puede reducir la latencia y los costos de transferencia de datos, pero requiere distribuir las políticas de seguridad a cada Spoke.
Subredes
Dentro de una red Spoke, el espacio IP se divide aún más en Subredes. Un patrón común y muy recomendado es separar estrictamente las subredes públicas y privadas.
Las subredes públicas contienen recursos que deben estar expuestos a internet (como balanceadores de carga o gateways NAT). Las subredes privadas contienen las cargas de trabajo reales, bases de datos y servicios internos, los cuales no tienen una ruta de entrada directa desde internet. Esto minimiza drásticamente la superficie de ataque.
Gestión de Direcciones IP (IPAM)
Un modo de fallo común para las plataformas en escalabilidad es el agotamiento de direcciones IP. Si los bloques CIDR se asignan de forma orgánica, inevitablemente terminarás con rangos de IP superpuestos, haciendo que la conexión entre redes sea matemáticamente imposible sin soluciones de NAT altamente complejas. Una Gestión de Direcciones IP (IPAM) centralizada y sin superposiciones es un requisito arquitectónico desde el primer día.
El Hub es globalmente único, mientras que los Spokes se multiplican a lo largo de regiones y entornos. El equipo de plataforma debe mapear el espacio IP privado RFC 1918 y particionarlo estratégicamente. Por ejemplo, asignando un bloque grande para el Hub, y bloques distintos para cada Spoke:
- Red Hub:
10.0.0.0/16 - Spoke
("ecommerce", "live", "us01", _):10.16.0.0/16 - Spoke
("ecommerce", "sandbox", "us01", _):10.17.0.0/16 - Spoke
("finance", "live", "eu01", _):10.32.0.0/16
Además, al dividir el bloque 10.16.0.0/16 de un Spoke en subredes más pequeñas, debes tener en cuenta los matices del proveedor de nube. Algunos proveedores tienen requisitos de delegación específicos. Por ejemplo, Azure requiere múltiples subredes privadas dedicadas para delegar direcciones IP directamente a servicios gestionados como Azure Kubernetes Service (AKS) o Azure Database for PostgreSQL. Tu estrategia de IPAM debe reservar estos sub-bloques de antemano para evitar el agotamiento dentro del Spoke.
Al definir estrictamente estas matemáticas por adelantado, expandirse a una nueva región se convierte en un simple cambio de parámetro en un módulo de IaC, eliminando por completo el riesgo de colisiones de IP.
DNS
Just como particionamos el espacio de IPs, debemos particionar el espacio de nombres del Sistema de Nombres de Dominio (DNS) de la organización. Una arquitectura DNS bien estructurada previene conflictos de enrutamiento y simplifica el descubrimiento de servicios en toda la red Hub and Spoke.
Recomiendo un enfoque de zona dual: aprovisionar dos zonas hospedadas por Spoke (o incluso por tenant) — una interna y una pública.
Al establecer la convención de nomenclatura del DNS, debes decidir cómo manejar los dominios de nivel superior. Un enfoque escalable es asignar un dominio de nivel superior único a cada sector (por ejemplo, company.com para el sector de ecommerce, y company.io para herramientas internas). Esto previene colisiones de nombres y separa elegantemente los dominios gestionados por la plataforma de los dominios gestionados por el equipo de Marketing. Alternativamente, puedes usar subdominios como ecommerce.company.com y tools.company.com.
Para la zona hospedada interna, los registros de servicios deberían seguir un patrón internal explícito:
{region}.{tier}.internal.{sector_domain}
(por ejemplo, eu01.live.internal.company.com)
Para la zona hospedada pública, el modificador public es redundante y debería omitirse:
{region}.{tier}.{sector_domain}
(por ejemplo, eu01.live.company.com)
Algunas organizaciones prefieren mezclar registros públicos e internos en una sola zona hospedada para ahorrar en la sobrecarga administrativa. Aconsejo firmemente no hacer esto. Mezclar las zonas puede exponer nombres de servicios internos sensibles y detalles de infraestructura al internet público. Mantener las zonas internas y públicas estrictamente separadas asegura que la información de enrutamiento interno permanezca completamente invisible a actores externos.
Cómputo
Con la red segmentada, necesitamos un sustrato para ejecutar las cargas de trabajo. Si bien las organizaciones usan modelos de cómputo variados, el equipo de plataforma debe proveer caminos estándar y gobernados para todos ellos.
Kubernetes: La Abstracción por Excelencia
Considero que Kubernetes es la tecnología por excelencia para construir plataformas. Kubernetes es mucho más que solo un orquestador de contenedores; es un plano de control universal. Proporciona una API estandarizada y declarativa que se sitúa por encima del proveedor de nube, abstrayendo una gran cantidad de complejidad de infraestructura subyacente. Desde la perspectiva de un desarrollador, un manifiesto de Deployment se ve exactamente igual ya sea que se aplique en Amazon EKS, Google GKE o Azure AKS.
Además, mediante el uso del patrón Operator (como Crossplane, Azure Operator o AWS Controllers para Kubernetes), la API de Kubernetes puede extenderse para gestionar casi cualquier recurso externo en la nube, permitiendo a los desarrolladores aprovisionar una base de datos usando exactamente el mismo flujo de trabajo que usan para desplegar su aplicación.
Si bien Kubernetes no es estrictamente obligatorio —puedes construir una IDP exitosa usando tecnologías de máquinas virtuales u otra orquestación de contenedores como ACA o ECS— es altamente recomendado ya que es el estándar de facto de la industria. A lo largo de este libro, utilizaré Kubernetes en mis ejemplos y diagramas como la capa de abstracción por defecto.
Multitenencia
Debemos evitar el anti-patrón del “Mega-Clúster”: ejecutar a toda la organización en un solo clúster. Sin embargo, gestionar cientos de clústeres físicos para cada equipo de desarrollo crea un trabajo tedioso operativo insoportable. En su lugar, las plataformas utilizan aislamiento lógico.
El pan de cada día del cómputo de plataformas es proveer namespaces como servicio. Los desarrolladores solicitan un namespace y lo reciben automáticamente, preconfigurado por completo con los guardarailes correctos, políticas de red y permisos RBAC. Los desarrolladores pueden solicitar este límite de aislamiento ya sea abriendo un pull request hacia un repositorio de Git que gestiona el equipo de plataforma, o invocando una API de autoservicio a través de un Portal Interno para Desarrolladores (IDP).
Para los equipos que requieren un aislamiento más profundo —como la capacidad de instalar sus propios CustomResourceDefinitions (CRDs) a nivel de clúster— la industria está adoptando los Clústeres Virtuales (vcluster). Un clúster virtual ejecuta un plano de control de Kubernetes completamente funcional dentro de un namespace de un clúster host. Para el desarrollador, esto le da acceso de administrador a su propio clúster dedicado. Para el equipo de plataforma, esto se gestiona simplemente como otro pod dentro del ciclo de vida de la infraestructura estándar.
Pools de Nodos en Kubernetes
Compartir un clúster de Kubernetes no significa necesariamente compartir exactamente los mismos recursos de cómputo. El equipo de plataforma debería proporcionar un Pool de Nodos de Propósito General como la zona de aterrizaje por defecto para la gran mayoría de las cargas de trabajo. Los tipos de instancias específicos (por ejemplo, instancias m5 estándar o t3 burstable en AWS) dentro de este pool predeterminado dependen de la naturaleza de las cargas de trabajo de la organización.
Para optimizar este pool por defecto, los equipos de plataforma dependen cada vez más de servicios de aprovisionamiento inteligente como Karpenter. En lugar de gestionar grupos de autoescalado rígidos, Karpenter aprovisiona automáticamente las instancias de cómputo más apropiadas y rentables (a menudo aprovechando instancias Spot) dinámicamente según las peticiones específicas de CPU y memoria de los pods pendientes. Esto reduce significativamente los costos de capacidad ociosa mientras mejora el rendimiento de la carga de trabajo.
Sin embargo, a veces las cargas de trabajo específicas requieren hardware especializado o un aislamiento más profundo. Un equipo de ciencia de datos podría necesitar un pool de nodos con instancias GPU para machine learning, mientras que otro equipo podría necesitar instancias de alta memoria para una base de datos en memoria. Alternativamente, un tenant específico podría requerir aislamiento físico de cómputo estricto por razones de cumplimiento.
El equipo de plataforma maneja esto aprovisionando Pools de Nodos Dedicados. Los equipos de producto solicitan estas configuraciones de cómputo específicas a través de los mismos canales de autoservicio utilizados para las cuotas. El equipo de plataforma evalúa la solicitud y, si es aprobada, aprovisiona el pool y utiliza taints y tolerations de Kubernetes para asegurar que solo las cargas de trabajo de ese equipo específico se programen en esos nodos dedicados. Esto proporciona un aislamiento físico de cómputo y, al mismo tiempo, sigue beneficiándose del plano de control compartido y la gestión centralizada de un solo clúster.
Modelos de Cómputo Alternativos
Si bien Kubernetes es nuestra abstracción principal, una estrategia de plataforma madura también debe dar cabida a modelos de cómputo alternativos. Algunas cargas de trabajo, como tareas asíncronas altamente burstable, son más adecuadas para ofertas Serverless. Otras, como el software propietario heredado, pueden demandar máquinas virtuales en crudo. Las cargas de trabajo de Computación de Alto Rendimiento podrían requerir clústeres bare-metal dedicados.
En todos estos casos, el equipo de plataforma debe proveer módulos de IaC estándar que aseguren que estos entornos de cómputo alternativos se coloquen automáticamente en la red Spoke correcta, se etiqueten con precisión y se rijan por las mismas políticas de seguridad e IAM centrales que las cargas de trabajo de Kubernetes.
Cuotas
La infraestructura de autoservicio no significa recursos infinitos. Para asegurar la estabilidad de la plataforma y unos costos predecibles, la plataforma debe gestionar estrictamente el consumo de los equipos de producto. Implementamos esto a través de un sistema de cuotas multicapa que actúa como un límite estricto en la asignación de recursos.
Aunque exploraremos los guardarailes de seguridad y cumplimiento —como el bloqueo de almacenamiento no encriptado o la restricción de despliegues regionales— en el Capítulo 9: Seguridad, esta sección se centra en los límites de recursos necesarios para la salud operativa y financiera.
Cuotas a Nivel Nube
En el nivel más alto, los proveedores de nube te permiten establecer cuotas sobre la cantidad máxima de recursos que una cuenta o suscripción puede aprovisionar (por ejemplo, vCPUs máximas, balanceadores de carga máximos). Estas cuotas a nivel nube son estrictamente un asunto del equipo de plataforma; los equipos de producto no las gestionan y, en general, ni siquiera deberían saber que existen.
El equipo de plataforma utiliza las primitivas organizativas definidas anteriormente (OUs, Carpetas, Grupos de Gestión) para centralizar la gestión de estos límites en toda la organización. Al gobernar estas cuotas a nivel de raíz, se evita que un solo experimento descontrolado en una cuenta de sandbox agote los límites de servicio del proveedor de nube de toda la organización.
Cuotas de Kubernetes
Dentro de la abstracción de Kubernetes, los equipos de producto deberían estar íntimamente conscientes de las cuotas asignadas a sus namespaces.
Los límites de recursos se aplican de forma nativa utilizando objetos de ResourceQuota. Cuando el equipo de plataforma provee un namespace a un equipo de producto, este viene preconfigurado con un ResourceQuota que actúa como un límite estricto sobre el total de CPU y memoria que el namespace puede consumir de manera colectiva.
Para simplificar la gestión de cuotas y prevenir que los equipos de producto soliciten asignaciones arbitrarias y difíciles de predecir, recomiendo implementar Plantillas de Cuotas Predefinidas:
- Extra Pequeña:
2 CPU, 4Gi Memoria - Pequeña:
4 CPU, 8Gi Memoria - Mediana:
8 CPU, 16Gi Memoria - Grande:
16 CPU, 32Gi Memoria - Personalizada: Requiere una justificación arquitectónica específica.
Los equipos de producto solicitan una plantilla específica para su namespace a través de un portal interno de autoservicio o un pull request. Luego, el equipo de plataforma lo aprueba o deniega basándose en la capacidad general del clúster y los objetivos de gestión de costos.
# Una ResourceQuota estándar 'Mediana' aplicada a un namespace provisto
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-quota-medium
namespace: tenant-checkout
spec:
hard:
requests.cpu: "8"
requests.memory: 16Gi
limits.cpu: "16"
limits.memory: 32Gi
Protección contra Inanición de Recursos
Mientras que un ResourceQuota restringe el consumo total de un namespace, no evita que una única aplicación mal configurada dentro de ese namespace acapare todos los recursos asignados. Para prevenir la inanición de recursos a un micronivel, el equipo de plataforma también debe desplegar un objeto LimitRange junto a cada namespace provisto.
Un LimitRange impone restricciones en Pods individuales. Si un desarrollador olvida especificar las peticiones de CPU o memoria en su manifiesto de despliegue, el LimitRange inyecta valores predeterminados de forma automática. Y lo que es más importante, impone límites máximos por contenedor. Al garantizar que cada pod tenga una huella definida, el programador de Kubernetes puede empaquetar de forma eficiente las cargas de trabajo en los nodos de cómputo subyacentes sin miedo a que una sola fuga de memoria tumbe a los servicios adyacentes.
Al igual que las cuotas, la configuración del LimitRange es estrictamente responsabilidad del equipo de plataforma, y los equipos de producto deben solicitar las excepciones arquitectónicas formalmente.
El Valor de la Fricción
Estas cuotas predeterminadas son intencionalmente finitas. Cuando un equipo de desarrollo alcanza su límite, no pueden simplemente desplegar más pods. Deben pedirle al equipo de plataforma que aumente su cuota o aprovisione un pool dedicado con un argumento documentado de por qué necesitan más recursos.
Esta fricción es una característica, no un fallo. Una solicitud de aumento de cuota actúa como un interruptor, provocando una conversación sobre diseño de arquitectura, planificación de capacidad y costos. Proporciona una oportunidad para preguntar: "¿Realmente necesitas 40Gi de memoria o hay una fuga de memoria que debamos investigar?"
Además, las solicitudes de cuotas personalizadas o pools de nodos dedicados costosos a menudo requieren la aprobación adicional de la gerencia para evitar el sobreaprovisionamiento y los sobrecostos. En estos casos, el equipo de plataforma actúa como el único punto de contacto, escalando la solicitud a los líderes de ingeniería o FinOps, y comunicando la decisión de vuelta al equipo de producto. Esto centraliza la gestión de capacidad y asegura que los costos de infraestructura sigan siendo predecibles y estén alineados con los objetivos de negocio.
Enrutamiento y Borde Impulsados por API
La pieza final del rompecabezas de la infraestructura es cómo entra el tráfico al sistema. La configuración de borde manual y basada en tickets es un cuello de botella enorme. Pero más allá de simplemente automatizar el trabajo tedioso, el enrutamiento de borde representa otro límite crítico de Responsabilidad Compartida.
El tráfico Norte-Sur que entra a tu plataforma es interceptado en el perímetro por servicios de borde:
- Global Server Load Balancing (GSLB) y DNS: Enruta el tráfico hacia la región más cercana o más sana.
- Web Application Firewalls (WAFs): Proporciona protección contra DDoS y filtra las cargas útiles maliciosas.
- API Gateways de Borde / Balanceadores de Carga: Terminan el SSL/TLS, imponen límites de velocidad globales y enrutan el tráfico hacia el clúster de Kubernetes específico.
Estos componentes del borde exterior son gestionados estrictamente por el equipo de plataforma. Los desarrolladores nunca deberían escribir reglas de WAF ni configurar las conexiones de Transit Gateway.
Sin embargo, una vez que el tráfico alcanza el controlador de entrada del clúster, la responsabilidad cambia. Los equipos de producto definen cómo se enruta el tráfico hacia sus pods específicos utilizando primitivas de Kubernetes estándar (como objetos Ingress o Gateway API).
Herramientas como ExternalDNS y cert-manager automatizan el puente entre estos dos guardarailes. Cuando un desarrollador despliega un manifiesto Ingress, ExternalDNS detecta el cambio de forma automática y se comunica con el DNS gestionado por la plataforma para crear los registros necesarios. Simultáneamente, cert-manager negocia, aprovisiona y adjunta un certificado SSL/TLS válido.
Al tratar el borde, la red y las capas de cómputo como servicios declarativos, fuertemente abstraídos y automatizados, el equipo de plataforma deja de ser el cuello de botella. Como aprendieron Marta y Javi, una expansión multi-región no debería ser una épica de Jira de tres semanas; debería ser un simple cambio de parámetro en un pull request.
Skills para este capítulo
design-landing-zone — Una skill de IA que mapea la Notación de Plataforma a una jerarquía organizativa cloud concreta: qué cuentas, suscripciones, proyectos, OUs y carpetas existen por coordenada, y qué guardarraíles aplican a cada nivel estructural.define-naming-convention — Una skill de IA que define los patrones de nomenclatura de recursos y el esquema de etiquetado obligatorio para tu plataforma, asegurando que la coordenada de cada recurso sea legible desde su nombre.design-networking — Una skill de IA que diseña la topología de red hub-and-spoke para tu plataforma: asignación CIDR de IPAM, definiciones de VPC/VNet por coordenada, estructura de zonas DNS y estrategia de egress.design-compute — Una skill de IA que diseña la topología de clústeres Kubernetes, la estrategia de node pools y el modelo de multi-tenancy para tu plataforma, incluyendo plantillas de cuotas de ResourceQuota que los tenants pueden elegir.manage-azure-landing-zone — Una skill de IA que aprovisiona y sincroniza la landing zone de Azure: Management Groups, Subscriptions, Resource Groups, asignaciones de Azure Policy y alertas de presupuesto.manage-aws-landing-zone — Una skill de IA que aprovisiona y sincroniza la landing zone de AWS: Organizational Units, Accounts, Service Control Policies, configuración de auditoría centralizada y alertas de presupuesto.manage-gcp-landing-zone — Una skill de IA que aprovisiona y sincroniza la landing zone de GCP: Folders, Projects, vínculos de Billing Account, Organization Policies y alertas de presupuesto.manage-azure-networking — Una skill de IA que aprovisiona y sincroniza la red de Azure: Virtual WAN Hubs, VNets de spoke, subnets, NSGs y zonas de DNS privado.manage-aws-networking — Una skill de IA que aprovisiona y sincroniza la red de AWS: Transit Gateway, tablas de rutas TGW con aislamiento por tier, VPCs de spoke, subnets y zonas hospedadas privadas de Route 53.manage-gcp-networking — Una skill de IA que aprovisiona y sincroniza la red de GCP: topología de Network Connectivity Center o Shared VPC, subnets, reglas de firewall, Cloud NAT y zonas privadas de Cloud DNS.manage-k8s-namespaces — Una skill de IA que aprovisiona y sincroniza namespaces de tenant en Kubernetes: ResourceQuota a partir de plantillas de cuotas, valores predeterminados de LimitRange y NetworkPolicy de denegación por defecto como suelo de guardarraíles de cómputo de la plataforma.Suscríbete a la Newsletter
¿Te está gustando el libro? Únete a más de 1.000 ingenieros de plataformas recibiendo artículos, reflexiones e historias de las trincheras directamente en tu bandeja de entrada.
Suscribirse gratis