Desplegar microservicios requiere una infraestructura preparada para cambios frecuentes, escalado horizontal y monitoreo continuo. Los contenedores simplifican la portabilidad, los orquestadores coordinan el ciclo de vida de las instancias y las tuberías de CI/CD automatizan la entrega. Este tema cubre los elementos esenciales desde la construcción de imágenes hasta las estrategias de despliegue que minimizan el riesgo al liberar nuevas versiones.
La contenedorización empaqueta el servicio y sus dependencias en una imagen inmutable. Docker se convirtió en estándar de facto gracias a su formato portable y al ecosistema de herramientas que incluye registro de imágenes, composición y volúmenes persistentes. Cada microservicio puede construir su propia imagen y publicarla en un registro central, garantizando que el binario ejecutado en producción sea idéntico al probado.
Para maximizar eficiencia se recomienda usar imágenes base ligeras, copiar únicamente artefactos necesarios y aplicar escaneo de vulnerabilidades como parte del pipeline. También es importante etiquetar las imágenes con la versión del servicio y el hash del commit para facilitar auditorías.
El siguiente archivo define una imagen multietapa donde se compila la aplicación y luego se empaqueta sobre una base mínima.
# Etapa de compilación
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
COPY pom.xml .
COPY src ./src
RUN mvn -B clean package -DskipTests
# Etapa de ejecución
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /workspace/target/orders-service.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
La imagen resultante contiene únicamente el JRE y el artefacto compilado, lo que reduce el tamaño y acorta el tiempo de arranque. Se expone el puerto 8080 para recibir peticiones.
Kubernetes gestiona el despliegue, escalado y actualización de contenedores. Define objetos declarativos como Deployments, Services y ConfigMaps que describen el estado deseado. El plano de control programa pods en los nodos, reinicia contenedores fallidos y expone puntos de salud.
Los manifiestos permiten versionar la infraestructura como código y aplicar políticas homogéneas entre servicios. Además, la integración con Helm y con operadores facilita empaquetar configuraciones repetibles.
El ejemplo muestra cómo describir un servicio de pedidos con sondas de salud y políticas de actualización.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-service
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: orders
template:
metadata:
labels:
app: orders
spec:
containers:
- name: orders
image: registry.local/orders-service:1.4.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: orders-service
spec:
selector:
app: orders
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Las probes permiten retirar la instancia del balanceo si deja de responder adecuadamente. El Service expone el microservicio dentro del clúster y puede complementarse con un Ingress para tráfico externo.
Las tuberías de integración y entrega continua automatizan compilaciones, pruebas, análisis estático, empaquetado y despliegue. Plataformas como GitHub Actions, GitLab CI o Jenkins habilitan pipelines declarativos que se ejecutan ante cada cambio en el repositorio.
En la práctica se adoptan plantillas reutilizables para estandarizar etapas. Los pipelines suelen incluir verificación de vulnerabilidades, publicación en registro de imágenes y despliegues progresivos hacia ambientes de prueba y producción.
El siguiente flujo en GitHub Actions compila, prueba, crea la imagen y la publica. Una vez aprobada, despliega en Kubernetes utilizando credenciales almacenadas como secretos.
name: orders-service-ci
on:
push:
branches: ["main"]
pull_request:
paths: ["orders-service/**"]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- name: Build with Maven
run: mvn -B -pl orders-service -am clean verify
- name: Build Docker image
run: docker build -t registry.local/orders-service:${{ github.sha }} orders-service
- name: Push Docker image
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login registry.local -u "${{ secrets.REGISTRY_USER }}" --password-stdin
docker push registry.local/orders-service:${{ github.sha }}
- name: Deploy to Kubernetes
env:
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG }}
run: |
echo "$KUBE_CONFIG_DATA" | base64 -d > kubeconfig
kubectl --kubeconfig kubeconfig set image deployment/orders-service orders=registry.local/orders-service:${{ github.sha }}
kubectl --kubeconfig kubeconfig rollout status deployment/orders-service
Los secretos se almacenan cifrados y el despliegue se ejecuta solamente si las pruebas se completan con éxito. Es buena práctica agregar validaciones de política e integración con herramientas de escaneo de contenedores.
Gestionar versiones permite mantener múltiples variantes de un servicio, aplicar parches críticos y retroceder en caso de fallas. Se recomienda etiquetar cada liberación con un número semántico y asociarla a una imagen inmutable. El control de versiones también abarca los manifiestos de infraestructura mediante repositorios Git dedicados (GitOps).
Los rollbacks deben ser procesos automatizados. Kubernetes ofrece comandos de rollout undo, mientras que los pipelines pueden revertir la imagen a la etiqueta anterior. Las métricas y alertas determinan cuándo iniciar la reversión.
El despliegue blue-green mantiene dos entornos idénticos: blue (actual) y green (nuevo). Una vez que se valida el entorno green, el tráfico se redirige de forma atómica. Si aparece un problema, basta con retornar al entorno blue. El patrón canary libera la versión a un subconjunto de usuarios y amplía el porcentaje a medida que se verifica la estabilidad.
Implementar estas estrategias requiere balanceadores capaces de dirigir tráfico por etiquetas o encabezados, registros de métricas en tiempo real y automatización para escalar progresivamente. También conviene un plan de comunicación para informar a los equipos de soporte sobre la ventana de despliegue.
El siguiente fragmento ilustra un servicio que consulta el estado del rollout y dispara una alerta si la nueva versión no alcanza el estado listo en el tiempo esperado.
@Service
public class RolloutWatcher {
private final KubernetesClient client;
private final AlertingService alertingService;
public RolloutWatcher(KubernetesClient client, AlertingService alertingService) {
this.client = client;
this.alertingService = alertingService;
}
public void waitForRevision(String deploymentName, Duration timeout) {
Boolean ready = client.apps().deployments()
.withName(deploymentName)
.waitUntilCondition(deployment -> {
if (deployment == null) {
return false;
}
return deployment.getStatus().getUnavailableReplicas() == null;
}, timeout.toMillis(), TimeUnit.MILLISECONDS);
if (Boolean.FALSE.equals(ready)) {
alertingService.send("Rollout de " + deploymentName + " no alcanzó estado READY", Severity.HIGH);
throw new IllegalStateException("El despliegue no finalizó a tiempo");
}
}
}
Este tipo de supervisión automatizada ayuda a detectar fallos antes de que impacten de forma masiva y permite iniciar un rollback sin intervención manual.