Técnico••30 min
Microservicios en Kubernetes con Service Mesh
Guía completa para implementar microservicios en Kubernetes usando Istio. Desde la arquitectura hasta la implementación en producción.
DF
Diego Fernández
DevOps Engineer
Microservicios: De la Teoría a la Práctica con Kubernetes e Istio
Después de migrar monolitos a microservicios en múltiples empresas, estas son las lecciones más valiosas que hemos aprendido.
¿Por Qué Microservicios?
Ventajas | Desventajas |
---|---|
✅ Escalabilidad independiente | ❌ Complejidad operacional |
✅ Despliegues independientes | ❌ Latencia de red |
✅ Tecnologías heterogéneas | ❌ Debugging distribuido |
✅ Equipos autónomos | ❌ Consistencia de datos |
Arquitectura de Referencia
1# namespace.yaml - Organización por dominios
2apiVersion: v1
3kind: Namespace
4metadata:
5 name: ecommerce
6 labels:
7 istio-injection: enabled
8---
9apiVersion: v1
10kind: Namespace
11metadata:
12 name: payments
13 labels:
14 istio-injection: enabled
15---
16apiVersion: v1
17kind: Namespace
18metadata:
19 name: inventory
20 labels:
21 istio-injection: enabled
Microservicio Base con Spring Boot
1// OrderService.java
2@RestController
3@RequestMapping("/api/v1/orders")
4@Slf4j
5public class OrderController {
6
7 private final OrderService orderService;
8 private final PaymentClient paymentClient;
9 private final InventoryClient inventoryClient;
10 private final MeterRegistry meterRegistry;
11
12 @PostMapping
13 @CircuitBreaker(name = "order-creation", fallbackMethod = "createOrderFallback")
14 @Retry(name = "order-creation")
15 @Timed(value = "order.creation.time", description = "Time taken to create order")
16 public ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody OrderRequest request) {
17
18 // Distributed tracing
19 Span span = tracer.currentSpan();
20 span.tag("order.items.count", String.valueOf(request.getItems().size()));
21
22 try {
23 // Verificar inventario
24 CompletableFuture<InventoryResponse> inventoryFuture =
25 CompletableFuture.supplyAsync(() ->
26 inventoryClient.checkAvailability(request.getItems())
27 );
28
29 // Validar pago
30 CompletableFuture<PaymentValidation> paymentFuture =
31 CompletableFuture.supplyAsync(() ->
32 paymentClient.validatePaymentMethod(request.getPaymentInfo())
33 );
34
35 // Esperar respuestas
36 CompletableFuture.allOf(inventoryFuture, paymentFuture).join();
37
38 if (!inventoryFuture.get().isAvailable()) {
39 throw new InsufficientInventoryException();
40 }
41
42 // Crear orden
43 Order order = orderService.createOrder(request);
44
45 // Publicar evento
46 eventPublisher.publishEvent(new OrderCreatedEvent(order));
47
48 // Métricas
49 meterRegistry.counter("orders.created", "status", "success").increment();
50
51 return ResponseEntity.ok(OrderResponse.from(order));
52
53 } catch (Exception e) {
54 meterRegistry.counter("orders.created", "status", "failure").increment();
55 span.error(e);
56 throw new OrderCreationException("Failed to create order", e);
57 }
58 }
59
60 // Fallback method
61 public ResponseEntity<OrderResponse> createOrderFallback(OrderRequest request, Exception ex) {
62 log.error("Order creation failed, executing fallback", ex);
63 return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
64 .body(OrderResponse.fallback());
65 }
66}
Configuración de Istio Service Mesh
1# istio-gateway.yaml
2apiVersion: networking.istio.io/v1beta1
3kind: Gateway
4metadata:
5 name: ecommerce-gateway
6spec:
7 selector:
8 istio: ingressgateway
9 servers:
10 - port:
11 number: 80
12 name: http
13 protocol: HTTP
14 hosts:
15 - "api.infraux.com"
16 - port:
17 number: 443
18 name: https
19 protocol: HTTPS
20 tls:
21 mode: SIMPLE
22 credentialName: api-tls-cert
23 hosts:
24 - "api.infraux.com"
25---
26# Virtual Service
27apiVersion: networking.istio.io/v1beta1
28kind: VirtualService
29metadata:
30 name: ecommerce-routes
31spec:
32 hosts:
33 - "api.infraux.com"
34 gateways:
35 - ecommerce-gateway
36 http:
37 - match:
38 - uri:
39 prefix: "/api/v1/orders"
40 route:
41 - destination:
42 host: order-service.ecommerce.svc.cluster.local
43 port:
44 number: 8080
45 weight: 100
46 - match:
47 - uri:
48 prefix: "/api/v1/payments"
49 route:
50 - destination:
51 host: payment-service.payments.svc.cluster.local
52 port:
53 number: 8080
Traffic Management Avanzado
1. Canary Deployments
1# canary-deployment.yaml
2apiVersion: networking.istio.io/v1beta1
3kind: VirtualService
4metadata:
5 name: order-service-canary
6spec:
7 hosts:
8 - order-service
9 http:
10 - match:
11 - headers:
12 canary:
13 exact: "true"
14 route:
15 - destination:
16 host: order-service
17 subset: v2
18 - route:
19 - destination:
20 host: order-service
21 subset: v1
22 weight: 90
23 - destination:
24 host: order-service
25 subset: v2
26 weight: 10
27---
28apiVersion: networking.istio.io/v1beta1
29kind: DestinationRule
30metadata:
31 name: order-service-destination
32spec:
33 host: order-service
34 subsets:
35 - name: v1
36 labels:
37 version: v1
38 - name: v2
39 labels:
40 version: v2
41 trafficPolicy:
42 connectionPool:
43 tcp:
44 maxConnections: 10
45 http:
46 http1MaxPendingRequests: 5
47 http2MaxRequests: 10
2. Circuit Breaker y Retry
1# resilience-config.yaml
2apiVersion: networking.istio.io/v1beta1
3kind: DestinationRule
4metadata:
5 name: payment-service-circuit-breaker
6spec:
7 host: payment-service
8 trafficPolicy:
9 connectionPool:
10 tcp:
11 maxConnections: 100
12 http:
13 http1MaxPendingRequests: 50
14 http2MaxRequests: 100
15 maxRequestsPerConnection: 2
16 outlierDetection:
17 consecutiveErrors: 5
18 interval: 30s
19 baseEjectionTime: 30s
20 maxEjectionPercent: 50
21 minHealthPercent: 30
22 splitExternalLocalOriginErrors: true
23---
24apiVersion: networking.istio.io/v1beta1
25kind: VirtualService
26metadata:
27 name: payment-service-retry
28spec:
29 hosts:
30 - payment-service
31 http:
32 - retries:
33 attempts: 3
34 perTryTimeout: 5s
35 retryOn: 5xx,reset,connect-failure,refused-stream
36 timeout: 15s
37 route:
38 - destination:
39 host: payment-service
Seguridad con mTLS
1# mtls-policy.yaml
2apiVersion: security.istio.io/v1beta1
3kind: PeerAuthentication
4metadata:
5 name: default
6 namespace: istio-system
7spec:
8 mtls:
9 mode: STRICT
10---
11# Authorization Policy
12apiVersion: security.istio.io/v1beta1
13kind: AuthorizationPolicy
14metadata:
15 name: order-service-authz
16 namespace: ecommerce
17spec:
18 selector:
19 matchLabels:
20 app: order-service
21 action: ALLOW
22 rules:
23 - from:
24 - source:
25 principals: ["cluster.local/ns/frontend/sa/webapp"]
26 to:
27 - operation:
28 methods: ["GET", "POST"]
29 paths: ["/api/v1/orders/*"]
30 - from:
31 - source:
32 namespaces: ["payments"]
33 to:
34 - operation:
35 methods: ["GET"]
36 paths: ["/api/v1/orders/*/status"]
Observabilidad Distribuida
1. Distributed Tracing
1// tracing-middleware.js
2const opentelemetry = require('@opentelemetry/api');
3const { W3CTraceContextPropagator } = require('@opentelemetry/core');
4
5class TracingMiddleware {
6 constructor() {
7 this.tracer = opentelemetry.trace.getTracer('order-service', '1.0.0');
8 this.propagator = new W3CTraceContextPropagator();
9 }
10
11 middleware() {
12 return async (req, res, next) => {
13 // Extraer contexto del header
14 const parentContext = this.propagator.extract(
15 opentelemetry.context.active(),
16 req.headers
17 );
18
19 // Crear nuevo span
20 const span = this.tracer.startSpan(
21 `${req.method} ${req.path}`,
22 {
23 kind: opentelemetry.SpanKind.SERVER,
24 attributes: {
25 'http.method': req.method,
26 'http.url': req.url,
27 'http.target': req.path,
28 'http.host': req.hostname,
29 'http.scheme': req.protocol,
30 'http.user_agent': req.get('user-agent'),
31 }
32 },
33 parentContext
34 );
35
36 // Propagar contexto
37 req.span = span;
38 req.traceId = span.spanContext().traceId;
39
40 // Agregar trace ID a logs
41 req.log = (level, message, meta = {}) => {
42 logger[level](message, {
43 ...meta,
44 traceId: req.traceId,
45 spanId: span.spanContext().spanId,
46 service: 'order-service'
47 });
48 };
49
50 // Continuar
51 res.on('finish', () => {
52 span.setAttributes({
53 'http.status_code': res.statusCode,
54 'http.response.size': res.get('content-length') || 0
55 });
56
57 if (res.statusCode >= 400) {
58 span.setStatus({
59 code: opentelemetry.SpanStatusCode.ERROR,
60 message: `HTTP ${res.statusCode}`
61 });
62 }
63
64 span.end();
65 });
66
67 next();
68 };
69 }
70}
2. Métricas Personalizadas
1// metrics.go
2package main
3
4import (
5 "github.com/prometheus/client_golang/prometheus"
6 "github.com/prometheus/client_golang/prometheus/promauto"
7)
8
9var (
10 ordersProcessed = promauto.NewCounterVec(
11 prometheus.CounterOpts{
12 Name: "orders_processed_total",
13 Help: "Total number of processed orders",
14 },
15 []string{"status", "payment_method"},
16 )
17
18 orderProcessingDuration = promauto.NewHistogramVec(
19 prometheus.HistogramOpts{
20 Name: "order_processing_duration_seconds",
21 Help: "Time taken to process orders",
22 Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
23 },
24 []string{"service"},
25 )
26
27 inventoryLevel = promauto.NewGaugeVec(
28 prometheus.GaugeOpts{
29 Name: "inventory_level",
30 Help: "Current inventory levels",
31 },
32 []string{"product_id", "warehouse"},
33 )
34)
35
36func processOrder(order Order) error {
37 timer := prometheus.NewTimer(orderProcessingDuration.WithLabelValues("order-service"))
38 defer timer.ObserveDuration()
39
40 // Procesar orden
41 err := orderService.Process(order)
42
43 if err != nil {
44 ordersProcessed.WithLabelValues("failed", order.PaymentMethod).Inc()
45 return err
46 }
47
48 ordersProcessed.WithLabelValues("success", order.PaymentMethod).Inc()
49 return nil
50}
Service Mesh Patterns
1. Sidecar Pattern
1# deployment-with-sidecar.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: order-service
6spec:
7 template:
8 metadata:
9 annotations:
10 sidecar.istio.io/inject: "true"
11 sidecar.istio.io/proxyCPU: "100m"
12 sidecar.istio.io/proxyMemory: "128Mi"
13 spec:
14 containers:
15 - name: order-service
16 image: infraux/order-service:v2.1.0
17 ports:
18 - containerPort: 8080
19 env:
20 - name: JAEGER_AGENT_HOST
21 value: "localhost"
22 - name: JAEGER_AGENT_PORT
23 value: "6831"
24 resources:
25 requests:
26 memory: "256Mi"
27 cpu: "250m"
28 limits:
29 memory: "512Mi"
30 cpu: "500m"
31 livenessProbe:
32 httpGet:
33 path: /health/live
34 port: 8080
35 initialDelaySeconds: 30
36 periodSeconds: 10
37 readinessProbe:
38 httpGet:
39 path: /health/ready
40 port: 8080
41 initialDelaySeconds: 5
42 periodSeconds: 5
Testing de Microservicios
1// integration-test.js
2const { ServiceMesh } = require('@infraux/testing');
3
4describe('Order Service Integration', () => {
5 let mesh;
6
7 beforeAll(async () => {
8 mesh = new ServiceMesh({
9 services: [
10 { name: 'order-service', port: 8080 },
11 { name: 'payment-service', port: 8081 },
12 { name: 'inventory-service', port: 8082 }
13 ]
14 });
15
16 await mesh.start();
17 });
18
19 afterAll(async () => {
20 await mesh.stop();
21 });
22
23 test('should create order with payment and inventory check', async () => {
24 // Mock responses
25 mesh.mock('payment-service', '/validate', {
26 status: 200,
27 body: { valid: true, transactionId: '12345' }
28 });
29
30 mesh.mock('inventory-service', '/check', {
31 status: 200,
32 body: { available: true, items: [{ id: 'SKU123', quantity: 10 }] }
33 });
34
35 // Test order creation
36 const response = await mesh.request('order-service', {
37 method: 'POST',
38 path: '/api/v1/orders',
39 body: {
40 items: [{ productId: 'SKU123', quantity: 2 }],
41 paymentInfo: { method: 'credit_card', token: 'tok_123' }
42 }
43 });
44
45 expect(response.status).toBe(201);
46 expect(response.body.orderId).toBeDefined();
47
48 // Verify interactions
49 expect(mesh.interactions('payment-service')).toHaveLength(1);
50 expect(mesh.interactions('inventory-service')).toHaveLength(1);
51 });
52});
Mejores Prácticas
<div class="info-box"> 💡 **Domain-Driven Design**: Organiza microservicios alrededor de dominios de negocio, no capas técnicas. </div> <div class="warning-box"> ⚠️ **Evita Distributed Monolith**: Si todos tus servicios se despliegan juntos, no tienes microservicios. </div> <div class="success-box"> ✅ **API First**: Define contratos API antes de implementar. Usa OpenAPI/AsyncAPI. </div>Métricas de Éxito
Métrica | Antes (Monolito) | Después (Microservicios) |
---|---|---|
Deploy Frequency | 1/mes | 50+/día |
Lead Time | 3 semanas | 2 horas |
MTTR | 4 horas | 15 minutos |
Change Failure Rate | 15% | 2% |
Conclusión
Los microservicios no son una bala de plata. Son una herramienta poderosa cuando se usan correctamente, pero requieren madurez organizacional y técnica. Con Kubernetes e Istio, puedes construir arquitecturas resilientes y escalables que realmente entreguen valor.
En InfraUX, simplificamos esta complejidad para que puedas enfocarte en tu lógica de negocio, no en la infraestructura.
#microservicios#kubernetes#istio#service-mesh#arquitectura