Una plataforma interna de desarrollo (IDP, Internal Developer Platform) es el conjunto de herramientas, plantillas y automatizaciones que un equipo de plataforma ofrece al resto de ingenieros para crear, desplegar y operar servicios sin abrir tickets. Esta guía explica los patrones de diseño que funcionan en la práctica, con ejemplos concretos sobre Kubernetes que puedes probar: un golden path basado en plantillas, el onboarding de equipos mediante GitOps y la forma de medir si la plataforma se usa.
Requisitos previos
Esta guía es conceptual, pero los ejemplos son ejecutables. Para probarlos necesitas:
- Un clúster de Kubernetes con permisos de administrador, por ejemplo k3s en un VPS de CubePath o kind en tu equipo.
kubectlinstalado en Ubuntu 24.04 LTS o similar.- Opcionalmente,
pipxpara instalar Cookiecutter y una instancia de Backstage si quieres probar su plantilla.
Qué es y qué no es una plataforma interna
Una IDP no es un producto que se instala: es una capa de abstracción que el equipo de plataforma construye combinando herramientas existentes. Los principios que separan una plataforma útil de un conjunto de scripts son:
- Autoservicio: un desarrollador obtiene un namespace, un repositorio o una base de datos sin esperar a otra persona.
- Golden paths: existe una forma recomendada de hacer cada cosa, y es también la más fácil. No es obligatoria, pero salirse de ella tiene un coste visible.
- Abstracciones finas: se ocultan los detalles repetitivos (etiquetas, cuotas, observabilidad), no Kubernetes entero. Los equipos deben poder mirar debajo cuando lo necesiten.
- Plataforma como producto: tiene usuarios, un roadmap, documentación y métricas de adopción, igual que cualquier producto.
Capas de una IDP y herramientas habituales
La mayoría de las plataformas se organizan en las mismas capas. No hace falta cubrirlas todas desde el principio:
| Capa | Función | Herramientas habituales |
|---|---|---|
| Portal | Catálogo de servicios, documentación, plantillas | Backstage, Port |
| Plantillas | Crear servicios nuevos con la estructura estándar | Backstage Software Templates, Cookiecutter |
| Entrega | Construir y desplegar a partir de Git | GitHub Actions, GitLab CI, Argo CD, Flux |
| Configuración | Manifiestos por entorno sin duplicar YAML | Kustomize, Helm |
| Infraestructura | Bases de datos, colas, buckets bajo demanda | Terraform/OpenTofu, Crossplane |
| Guardarraíles | Cuotas, políticas y permisos por equipo | ResourceQuota, LimitRange, RBAC, Kyverno |
| Observabilidad | Métricas, logs y trazas incluidos por defecto | Prometheus, Grafana, Loki, OpenTelemetry |
Un error frecuente es empezar por el portal. Un portal sin golden paths detrás es solo una página de enlaces. Empieza por automatizar lo que más tickets genera hoy (normalmente, crear un servicio nuevo y dar acceso a un entorno) y añade el portal cuando haya algo que mostrar.
Patrón 1: golden path con plantillas de servicio
El golden path para un servicio nuevo produce, en un solo paso, un repositorio con código base, Dockerfile, pipeline, manifiestos y la configuración de observabilidad. La herramienta más sencilla para empezar es Cookiecutter, que genera un proyecto a partir de una plantilla con variables.
En Ubuntu 24.04, instala Cookiecutter con pipx (instalar paquetes con pip fuera de un entorno virtual está bloqueado por PEP 668):
sudo apt update
sudo apt install pipx
pipx ensurepath
pipx install cookiecutter
Crea la plantilla. Las variables se definen en cookiecutter.json y el directorio del proyecto usa una de ellas como nombre:
mkdir -p ~/plantilla-servicio/'{{cookiecutter.nombre_servicio}}'/k8s
nano ~/plantilla-servicio/cookiecutter.json
{
"nombre_servicio": "mi-servicio",
"equipo": "equipo-backend",
"puerto": "8080"
}
Añade un manifiesto que ya incluye las etiquetas y anotaciones que la plataforma necesita (propietario, métricas, versión del golden path):
nano ~/plantilla-servicio/'{{cookiecutter.nombre_servicio}}'/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ cookiecutter.nombre_servicio }}
labels:
app.kubernetes.io/name: {{ cookiecutter.nombre_servicio }}
app.kubernetes.io/part-of: {{ cookiecutter.equipo }}
plataforma.example.com/golden-path: v1
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: {{ cookiecutter.nombre_servicio }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ cookiecutter.nombre_servicio }}
spec:
containers:
- name: app
image: registry.example.com/{{ cookiecutter.nombre_servicio }}:latest
ports:
- name: http
containerPort: {{ cookiecutter.puerto }}
readinessProbe:
httpGet:
path: /healthz
port: http
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
Genera un servicio a partir de la plantilla sin preguntas interactivas:
cd ~
cookiecutter --no-input ~/plantilla-servicio nombre_servicio=pagos equipo=equipo-pagos
head -n 8 pagos/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pagos
labels:
app.kubernetes.io/name: pagos
app.kubernetes.io/part-of: equipo-pagos
plataforma.example.com/golden-path: v1
La etiqueta plataforma.example.com/golden-path es importante: permite medir más adelante cuántos servicios usan la plantilla y en qué versión.
El mismo golden path en Backstage
Cuando la plataforma crece, las plantillas suelen pasar al Scaffolder de Backstage, que añade un formulario web, crea el repositorio y registra el servicio en el catálogo. Una plantilla mínima tiene esta forma:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: servicio-python
title: Servicio Python estándar
description: Crea un servicio con Dockerfile, pipeline de CI y manifiestos de Kubernetes.
spec:
owner: group:equipo-plataforma
type: service
parameters:
- title: Datos del servicio
required:
- nombre
- propietario
properties:
nombre:
title: Nombre
type: string
pattern: '^[a-z][a-z0-9-]*$'
propietario:
title: Equipo propietario
type: string
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
steps:
- id: plantilla
name: Generar código
action: fetch:template
input:
url: ./skeleton
values:
nombre: ${{ parameters.nombre }}
propietario: ${{ parameters.propietario }}
- id: publicar
name: Crear repositorio
action: publish:github
input:
repoUrl: github.com?owner=your_org&repo=${{ parameters.nombre }}
description: Servicio ${{ parameters.nombre }}
- id: registrar
name: Registrar en el catálogo
action: catalog:register
input:
repoContentsUrl: ${{ steps['publicar'].output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yaml
output:
links:
- title: Repositorio
url: ${{ steps['publicar'].output.remoteUrl }}
El directorio skeleton/ contiene los mismos archivos que la plantilla de Cookiecutter, con la sintaxis ${{ values.nombre }} y un catalog-info.yaml que declara el propietario del servicio.
Patrón 2: onboarding de equipos por GitOps
El segundo ticket más habitual es "necesito un entorno". En lugar de un script que crea namespaces a mano, define cada equipo como un directorio en un repositorio de plataforma y deja que Argo CD o Flux lo aplique. Los guardarraíles (cuotas, límites por defecto y permisos) forman parte de la definición.
Crea una base común para todos los equipos:
mkdir -p ~/plataforma/equipos/base ~/plataforma/equipos/equipo-pagos
nano ~/plataforma/equipos/base/equipo.yaml
apiVersion: v1
kind: Namespace
metadata:
name: equipo
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: cuota
namespace: equipo
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.memory: 16Gi
pods: "50"
---
apiVersion: v1
kind: LimitRange
metadata:
name: limites-por-defecto
namespace: equipo
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
memory: 256Mi
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: desarrolladores
namespace: equipo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: equipo
nano ~/plataforma/equipos/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- equipo.yaml
Cada equipo es un overlay de pocas líneas que fija el namespace, el grupo con permisos y, si lo necesita, una cuota distinta:
nano ~/plataforma/equipos/equipo-pagos/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
namespace: equipo-pagos
patches:
- target:
kind: Namespace
patch: |-
- op: replace
path: /metadata/name
value: equipo-pagos
- target:
kind: RoleBinding
patch: |-
- op: replace
path: /subjects/0/name
value: equipo-pagos
- target:
kind: ResourceQuota
patch: |-
- op: replace
path: /spec/hard/requests.cpu
value: "8"
Aplica el overlay (en producción lo haría Argo CD al detectar el commit) y comprueba el resultado:
kubectl apply -k ~/plataforma/equipos/equipo-pagos
kubectl describe resourcequota cuota -n equipo-pagos
Name: cuota
Namespace: equipo-pagos
Resource Used Hard
-------- ---- ----
limits.memory 0 16Gi
pods 0 50
requests.cpu 0 8
requests.memory 0 8Gi
Verifica que un miembro del grupo puede desplegar en su namespace pero no en otros:
kubectl auth can-i create deployments -n equipo-pagos --as=ana --as-group=equipo-pagos
kubectl auth can-i create deployments -n kube-system --as=ana --as-group=equipo-pagos
yes
no
Dar de alta un equipo nuevo pasa a ser una pull request de tres líneas que el equipo de plataforma revisa y fusiona. El historial de Git sirve además como registro de auditoría.
Patrón 3: observabilidad incluida por defecto
Si cada equipo tiene que configurar sus métricas, la mitad no lo hará. El golden path debe incluir la integración. Con Prometheus Operator (por ejemplo, instalado con el chart kube-prometheus-stack), añade a la plantilla un ServiceMonitor que recoge las métricas del puerto llamado metrics del Service de cada servicio generado (la plantilla debe incluir ese puerto en el Deployment y en el Service):
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ cookiecutter.nombre_servicio }}
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{ cookiecutter.nombre_servicio }}
endpoints:
- port: metrics
path: /metrics
interval: 30s
La etiqueta release debe coincidir con el serviceMonitorSelector de tu instalación de Prometheus. Complementa esto con un dashboard de Grafana común que filtre por la etiqueta app.kubernetes.io/name, de modo que cada servicio nuevo tenga paneles de peticiones, errores y latencia desde el primer despliegue.
Patrón 4: medir la plataforma
Tratar la plataforma como producto implica medirla. Tres indicadores sencillos:
- Adopción del golden path: proporción de workloads con la etiqueta de la plantilla.
- Tiempo hasta el primer despliegue: desde que se crea el repositorio hasta que el servicio responde en un entorno. Se obtiene de las fechas del sistema de CI/CD.
- Métricas DORA: frecuencia de despliegue, tiempo de entrega, tasa de fallos en cambios y tiempo de recuperación.
La adopción se puede consultar directamente en el clúster gracias a la etiqueta de la plantilla:
kubectl get deployments -A -l plataforma.example.com/golden-path --no-headers | wc -l
kubectl get deployments -A --no-headers | wc -l
El primer número son los Deployments creados desde la plantilla (en cualquier versión) y el segundo el total. Para ver qué versiones de la plantilla siguen en uso:
kubectl get deployments -A -L plataforma.example.com/golden-path
Si la adopción no sube, pregunta a los equipos antes de añadir funcionalidades: normalmente el golden path es más lento o más rígido de lo que parece desde el equipo de plataforma.
Por dónde empezar
No todas las organizaciones necesitan todas las capas. Una guía orientativa según el tamaño:
| Situación | Qué construir primero |
|---|---|
| Menos de 5 equipos, un clúster | Plantilla Cookiecutter, overlays de Kustomize por equipo, Argo CD |
| 5 a 20 equipos | Lo anterior más Backstage con catálogo y Software Templates, observabilidad por defecto |
| Más de 20 equipos o varios clústeres | Infraestructura bajo demanda con Crossplane o Terraform, políticas con Kyverno, métricas DORA automatizadas |
Conclusión
Una plataforma interna eficaz se construye por capas a partir de los problemas reales de los equipos: primero un golden path que crea servicios con todo lo necesario, después el onboarding de equipos por GitOps con cuotas y permisos, y la observabilidad incluida por defecto. Como siguientes pasos, puedes gestionar los overlays de equipos con Argo CD, añadir políticas de admisión con Kyverno para exigir las etiquetas de la plantilla, o desplegar Backstage para dar a todo ello un catálogo y un portal.
