Patrones de Diseño para Plataformas Internas de Desarrollador
Una Plataforma Interna de Desarrollador (IDP, por sus siglas en inglés) proporciona infraestructura de autoservicio y caminos dorados que permiten a los equipos de desarrollo desplegar y operar servicios de forma autónoma. Esta guía cubre los patrones arquitectónicos clave, las APIs de plataforma, las plantillas de servicio y cómo mejorar la experiencia del desarrollador en entornos DevOps modernos.
Requisitos Previos
- Equipo de Platform Engineering dedicado (mínimo 2-3 personas)
- Clúster Kubernetes como base de la plataforma
- Herramientas de IaC: Terraform, Pulumi o Crossplane
- Portal de desarrollador (Backstage recomendado)
- Sistema de CI/CD establecido (GitLab CI, GitHub Actions, Argo CD)
Principios de una IDP
Una plataforma interna efectiva se basa en cuatro pilares:
- Autoservicio: los desarrolladores pueden obtener lo que necesitan sin esperar aprobaciones
- Abstracciones adecuadas: ocultar la complejidad de infraestructura sin perder control
- Caminos dorados: la forma recomendada de hacer las cosas es también la más fácil
- Producto, no proyecto: tratar la plataforma como un producto con usuarios reales
# Evaluar la madurez de tu plataforma actual
# Nivel 1: Manual - los desarrolladores abren tickets para todo
# Nivel 2: Scripts - algunos procesos automatizados pero frágiles
# Nivel 3: Self-service básico - portal o CLI para operaciones comunes
# Nivel 4: Self-service completo - desarrolladores autónomos con guardrails
# Nivel 5: Plataforma cognitiva - optimización continua basada en métricas
# Crear una CLI simple de plataforma con bash
cat << 'EOF' > /usr/local/bin/plat
#!/bin/bash
# CLI de plataforma - punto de entrada unificado para desarrolladores
COMANDO=${1}
case $COMANDO in
"nuevo-servicio")
curl -s https://portal.empresa.com/api/crear-servicio \
-H "Authorization: Bearer ${PLAT_TOKEN}" \
-d "{\"nombre\": \"${2}\", \"tipo\": \"${3:-microservicio}\"}"
;;
"desplegar")
kubectl apply -k "apps/${2}/overlays/${3:-staging}"
;;
"logs")
stern --namespace "${3:-default}" "${2}" --tail 100
;;
*)
echo "Uso: plat [nuevo-servicio|desplegar|logs] [argumentos]"
;;
esac
EOF
chmod +x /usr/local/bin/plat
El Camino Dorado
El "Golden Path" es la ruta predefinida y validada para crear, desplegar y operar servicios:
# Estructura del camino dorado para un nuevo microservicio
# Paso 1: Crear desde plantilla (vía Backstage o CLI)
plat nuevo-servicio mi-nuevo-servicio api-rest
# Esto genera automáticamente:
# ├── Repositorio Git con estructura estándar
# ├── Pipeline de CI/CD configurado
# ├── Manifiestos Kubernetes base
# ├── Configuración de observabilidad
# ├── Documentación inicial (TechDocs)
# └── Entrada en el catálogo de Backstage
Definir el camino dorado como código con Cookiecutter:
# Instalar cookiecutter
pip3 install cookiecutter
# Crear una plantilla de proyecto estándar
mkdir -p plantilla-microservicio/\{\{cookiecutter.nombre_servicio\}\}
cat << 'EOF' > plantilla-microservicio/cookiecutter.json
{
"nombre_servicio": "mi-servicio",
"lenguaje": ["go", "python", "nodejs", "java"],
"tipo": ["api-rest", "worker", "grpc"],
"equipo": "equipo-backend",
"repo_base": "github.com/mi-org"
}
EOF
# Crear la estructura de la plantilla
mkdir -p "plantilla-microservicio/{{cookiecutter.nombre_servicio}}/k8s"
mkdir -p "plantilla-microservicio/{{cookiecutter.nombre_servicio}}/.github/workflows"
cat << 'EOF' > "plantilla-microservicio/{{cookiecutter.nombre_servicio}}/k8s/deployment.yaml"
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ cookiecutter.nombre_servicio }}
labels:
app: {{ cookiecutter.nombre_servicio }}
equipo: {{ cookiecutter.equipo }}
spec:
replicas: 2
selector:
matchLabels:
app: {{ cookiecutter.nombre_servicio }}
template:
metadata:
labels:
app: {{ cookiecutter.nombre_servicio }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
containers:
- name: {{ cookiecutter.nombre_servicio }}
image: registry.empresa.com/{{ cookiecutter.nombre_servicio }}:latest
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: metrics
EOF
# Usar la plantilla para crear un nuevo servicio
cookiecutter plantilla-microservicio/
Plantillas de Servicio
Las plantillas estandarizan las decisiones de arquitectura:
# Plantilla Backstage para microservicio Go
# templates/microservicio-go/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: microservicio-go
title: Microservicio Go (Estándar Empresa)
description: |
Crea un microservicio Go con:
- API REST con Gin
- Métricas Prometheus
- Health checks
- Logging estructurado (zap)
- Tests unitarios
- Pipeline CI/CD configurado
spec:
owner: equipo-plataforma
type: service
parameters:
- title: Configuración del servicio
properties:
nombre:
type: string
pattern: '^[a-z][a-z0-9-]*$'
descripcion:
type: string
equipo:
type: string
ui:field: OwnerPicker
sla:
type: string
enum: ['99.9', '99.5', '99.0']
default: '99.5'
description: SLA objetivo del servicio
steps:
- id: crear-repo
action: publish:github
input:
repoUrl: github.com?repo=${{ parameters.nombre }}&owner=mi-org
- id: registrar-catalogo
action: catalog:register
input:
repoContentsUrl: ${{ steps['crear-repo'].output.repoContentsUrl }}
APIs de Plataforma
Las APIs de plataforma abstraen la complejidad de infraestructura:
# API REST de plataforma para autoservicio
# Implementación básica con FastAPI
cat << 'EOF' > platform_api.py
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
import subprocess
import yaml
app = FastAPI(title="Platform API", version="1.0.0")
class SolicitudServicio(BaseModel):
nombre: str
tipo: str = "api-rest"
equipo: str
namespace: str = "default"
@app.post("/servicios")
async def crear_servicio(solicitud: SolicitudServicio):
"""Crear un nuevo servicio con la configuración estándar"""
# Verificar que el nombre es válido
if not solicitud.nombre.replace('-', '').isalnum():
raise HTTPException(status_code=400, detail="Nombre de servicio inválido")
# Crear namespace si no existe
subprocess.run(
['kubectl', 'create', 'namespace', solicitud.namespace, '--dry-run=client', '-o', 'yaml'],
check=True
)
# Generar y aplicar los manifiestos estándar
manifiestos = generar_manifiestos(solicitud)
return {
"estado": "creado",
"servicio": solicitud.nombre,
"namespace": solicitud.namespace,
"url_catalogo": f"https://portal.empresa.com/catalog/{solicitud.nombre}"
}
def generar_manifiestos(solicitud: SolicitudServicio) -> dict:
"""Generar manifiestos Kubernetes estándar para el servicio"""
return {
"deployment": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": solicitud.nombre, "namespace": solicitud.namespace},
"spec": {"replicas": 2}
}
}
EOF
# Ejecutar la API de plataforma
uvicorn platform_api:app --host 0.0.0.0 --port 8000
Autoservicio de Infraestructura
# Namespace Request - los equipos piden namespaces via GitOps
# teams/equipo-backend/namespace-request.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: namespace-request-equipo-backend
namespace: platform-system
labels:
platform.empresa.com/tipo: namespace-request
data:
equipo: equipo-backend
proyecto: sistema-pagos
entorno: produccion
cuota-cpu: "10"
cuota-memoria: "20Gi"
cuota-pods: "50"
# Operador de plataforma que procesa las solicitudes
# (simplificado con un script, en producción usaría un operador Kubernetes real)
cat << 'EOF' > /usr/local/bin/platform-operator.sh
#!/bin/bash
# Operador de plataforma - procesa solicitudes de namespace
# Obtener todas las solicitudes pendientes
kubectl get configmaps -n platform-system \
-l platform.empresa.com/tipo=namespace-request \
-o json | jq -r '.items[].metadata.name' | while read solicitud; do
# Extraer datos de la solicitud
EQUIPO=$(kubectl get configmap $solicitud -n platform-system -o jsonpath='{.data.equipo}')
CUOTA_CPU=$(kubectl get configmap $solicitud -n platform-system -o jsonpath='{.data.cuota-cpu}')
# Crear el namespace con las políticas correctas
kubectl create namespace "$EQUIPO" --dry-run=client -o yaml | kubectl apply -f -
# Aplicar cuotas de recursos
kubectl apply -f - << YAML
apiVersion: v1
kind: ResourceQuota
metadata:
name: cuota-${EQUIPO}
namespace: ${EQUIPO}
spec:
hard:
requests.cpu: "${CUOTA_CPU}"
pods: "50"
YAML
echo "Namespace ${EQUIPO} configurado correctamente"
done
EOF
chmod +x /usr/local/bin/platform-operator.sh
Observabilidad Integrada
Toda la observabilidad debe estar incluida por defecto en el camino dorado:
# Plantilla de ServiceMonitor para Prometheus (parte del camino dorado)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ nombre_servicio }}
namespace: monitoring
labels:
release: prometheus-stack
spec:
selector:
matchLabels:
app: {{ nombre_servicio }}
endpoints:
- port: metrics
interval: 30s
path: /metrics
# Dashboard de Grafana estándar para todos los servicios
# Incluido automáticamente vía plantilla
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: servicio-estandar
spec:
folder: "Servicios"
json: |
{
"title": "Servicio - Métricas Estándar",
"panels": [
{"title": "Tasa de Peticiones", "type": "graph"},
{"title": "Latencia P95", "type": "graph"},
{"title": "Tasa de Errores", "type": "graph"},
{"title": "Uso de CPU", "type": "graph"}
]
}
Adopción y Cultura de Plataforma
# Medir la adopción de la plataforma
cat << 'EOF' > medir_adopcion.sh
#!/bin/bash
# Script para medir el éxito de la plataforma
echo "=== Métricas de Adopción de Plataforma ==="
# Servicios usando la plantilla estándar
SERVICIOS_ESTANDAR=$(kubectl get deployments --all-namespaces \
-l gestionado-por=plataforma --no-headers | wc -l)
TOTAL_SERVICIOS=$(kubectl get deployments --all-namespaces --no-headers | wc -l)
echo "Servicios usando plantilla estándar: ${SERVICIOS_ESTANDAR}/${TOTAL_SERVICIOS}"
echo "Tasa de adopción: $(echo "scale=1; ${SERVICIOS_ESTANDAR}*100/${TOTAL_SERVICIOS}" | bc)%"
# Tiempo medio de despliegue (DORA metrics)
# Requiere integración con el sistema de CI/CD
echo ""
echo "=== Métricas DORA ==="
echo "Para calcular métricas DORA completas, integrar con el sistema CI/CD"
EOF
chmod +x medir_adopcion.sh
Solución de Problemas
# Diagnóstico de problemas comunes en IDPs
# Los desarrolladores no pueden desplegar (problemas de permisos)
kubectl auth can-i create deployments --as=system:serviceaccount:equipo-backend:default -n equipo-backend
# Verificar que las cuotas no están agotadas
kubectl describe resourcequota -n equipo-backend
# Ver eventos del namespace del equipo
kubectl get events -n equipo-backend --sort-by='.lastTimestamp' | tail -20
# Depurar pipeline de CI/CD
# Ver los últimos runs de GitHub Actions
gh run list --limit 5
# Verificar la sincronización de Argo CD
argocd app sync mi-servicio --dry-run
argocd app diff mi-servicio
Conclusión
Una Plataforma Interna de Desarrollador bien diseñada multiplica la productividad de los equipos de ingeniería al eliminar la fricción entre escribir código y verlo en producción. El éxito depende de tratar la plataforma como un producto real con sus propios usuarios, SLAs y ciclo de retroalimentación constante. Los caminos dorados y las plantillas de servicio son la inversión más rentable: hacen que hacer las cosas bien sea también la opción más sencilla.


