--- title: "Cómo instalar la malla de servicios Istio en Kubernetes" description: "Instala Istio en Kubernetes con istioctl, inyecta sidecars, reparte tráfico entre versiones, activa mTLS estricto y autorización y visualiza la malla en Kiali." category: "Kubernetes Avanzado" language: es source: https://cubepath.com/es/docs/kubernetes-avanzado/malla-de-servicios-de-kubernetes-con-istio updated: 2026-09-25 --- # Cómo instalar la malla de servicios Istio en Kubernetes Istio es una malla de servicios (service mesh) que añade a cada pod un proxy Envoy para controlar el tráfico entre microservicios sin tocar el código: enrutado por versiones, reintentos, cifrado mTLS, autorización y métricas. En este tutorial instalarás Istio con `istioctl`, desplegarás la aplicación de ejemplo Bookinfo, repartirás el tráfico entre versiones de un servicio, forzarás mTLS en el namespace, restringirás quién puede llamar a cada servicio y verás la topología en Kiali. ## Requisitos previos Para seguir esta guía necesitas: - Un clúster de Kubernetes en una versión soportada por la release de Istio que instales (consulta la tabla de compatibilidad en istio.io), por ejemplo sobre VPS de CubePath con Ubuntu 24.04. - Al menos 4 GB de RAM y 2 vCPU libres en el clúster para el plano de control, los proxies y los complementos. - `kubectl` configurado con permisos de administrador del clúster, en un equipo Linux o macOS. - Opcional: un controlador de LoadBalancer (MetalLB, por ejemplo) si quieres exponer el ingress gateway con una IP externa. Sin él usarás `kubectl port-forward`. ## Paso 1: Instalar istioctl `istioctl` es la herramienta oficial para instalar, actualizar y diagnosticar Istio. El proyecto la distribuye mediante un script que descarga la última versión junto con los ejemplos. Descárgalo y revísalo antes de ejecutarlo: ```bash curl -fsSL https://istio.io/downloadIstio -o downloadIstio.sh less downloadIstio.sh sh downloadIstio.sh ``` El script crea un directorio `istio-` con el binario en `bin/` y los ejemplos en `samples/`. Entra en él e instala el binario en tu `PATH`: ```bash cd istio-*/ sudo install -m 0755 bin/istioctl /usr/local/bin/istioctl istioctl version ``` ```output Istio is not present in the cluster: no running Istio pods in namespace "istio-system" client version: 1.27.1 ``` El resto de comandos del tutorial se ejecutan desde este directorio, porque usan los manifiestos de `samples/`. > Consejo: para instalar una versión concreta, ejecuta el script con `ISTIO_VERSION= sh downloadIstio.sh`. ## Paso 2: Instalar el plano de control Istio incluye varios perfiles de instalación. `default` instala el plano de control (`istiod`) y un ingress gateway, y es el recomendado para producción. `demo` añade más componentes y registros, pensado solo para pruebas. Antes de instalar, comprueba que el clúster cumple los requisitos: ```bash istioctl x precheck ``` ```output ✔ No issues found when checking the cluster. Istio is safe to install or upgrade! ``` Instala Istio con el perfil `default`: ```bash istioctl install --set profile=default -y ``` ```output ✔ Istio core installed ✔ Istiod installed ✔ Ingress gateways installed ✔ Installation complete ``` Comprueba que los pods del namespace `istio-system` están en ejecución: ```bash kubectl get pods -n istio-system ``` ```output NAME READY STATUS RESTARTS AGE istio-ingressgateway-5d8c7b8f7c-k2vxn 1/1 Running 0 40s istiod-6c9f4b7d9-8qzlm 1/1 Running 0 55s ``` ## Paso 3: Desplegar una aplicación con sidecars Istio inyecta el proxy Envoy en los pods de los namespaces que tienen la etiqueta `istio-injection=enabled`. Crea un namespace para la aplicación de ejemplo y etiquétalo: ```bash kubectl create namespace bookinfo kubectl label namespace bookinfo istio-injection=enabled ``` Bookinfo es una aplicación de reseñas de libros con cuatro microservicios. El servicio `reviews` tiene tres versiones: `v1` sin estrellas, `v2` con estrellas negras y `v3` con estrellas rojas, lo que la hace ideal para probar el enrutado. Despliégala: ```bash kubectl apply -n bookinfo -f samples/bookinfo/platform/kube/bookinfo.yaml kubectl get pods -n bookinfo ``` Cada pod muestra `2/2` contenedores: la aplicación y el sidecar `istio-proxy`: ```output NAME READY STATUS RESTARTS AGE details-v1-7b8d6c5f9-xq2kd 2/2 Running 0 60s productpage-v1-6c8f7b9d4-p4mzt 2/2 Running 0 60s ratings-v1-5f9c8d7b6-9wljr 2/2 Running 0 60s reviews-v1-7d6b8c9f5-hv8kq 2/2 Running 0 60s reviews-v2-8c7d9b6f4-2mjnx 2/2 Running 0 60s reviews-v3-6f5b7c8d9-t7xzp 2/2 Running 0 60s ``` Si ves `1/1`, el namespace no tenía la etiqueta cuando se crearon los pods. Añádela y reinicia los Deployments con `kubectl rollout restart deployment -n bookinfo`. Comprueba que la aplicación responde desde dentro de la malla: ```bash kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -sS productpage:9080/productpage | grep -o ".*" ``` ```output Simple Bookstore App ``` ## Paso 4: Exponer la aplicación con el ingress gateway El ingress gateway es un Envoy en el borde del clúster. Un recurso `Gateway` le indica qué puertos escuchar, y un `VirtualService` asociado decide a qué servicio va cada petición. Bookinfo incluye ambos: ```bash kubectl apply -n bookinfo -f samples/bookinfo/networking/bookinfo-gateway.yaml ``` Si tu clúster tiene un controlador de LoadBalancer, el servicio `istio-ingressgateway` tendrá una IP externa: ```bash kubectl get svc istio-ingressgateway -n istio-system ``` Si `EXTERNAL-IP` se queda en ``, reenvía un puerto local al gateway para probar: ```bash kubectl port-forward -n istio-system svc/istio-ingressgateway 8080:80 ``` En otra terminal, pide la página: ```bash curl -s http://localhost:8080/productpage | grep -o ".*" ``` ```output Simple Bookstore App ``` Si abres `http://localhost:8080/productpage` en el navegador y recargas varias veces, verás que las estrellas cambian: sin reglas, Istio reparte las peticiones entre las tres versiones de `reviews`. ## Paso 5: Enrutar el tráfico entre versiones El enrutado usa dos recursos: - `DestinationRule`: define subconjuntos (`subsets`) de un servicio según las etiquetas de los pods, y políticas de conexión hacia él. - `VirtualService`: decide a qué subconjunto va cada petición, con pesos, coincidencias por cabecera o ruta, reintentos y timeouts. Crea la DestinationRule de `reviews` con un subconjunto por versión y un límite de conexiones con expulsión de instancias que fallan (circuit breaking): ```bash nano reviews-destinationrule.yaml ``` ```yaml apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: reviews namespace: bookinfo spec: host: reviews trafficPolicy: connectionPool: http: http1MaxPendingRequests: 100 maxRequestsPerConnection: 10 outlierDetection: consecutive5xxErrors: 5 interval: 30s baseEjectionTime: 60s maxEjectionPercent: 50 subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v3 labels: version: v3 ``` `outlierDetection` saca del balanceo durante 60 segundos cualquier pod que devuelva cinco errores 5xx seguidos, sin retirar nunca más de la mitad. Ahora crea un VirtualService que mande el 90 % del tráfico a `v1` y el 10 % a `v3`, un despliegue canario típico, con reintentos y timeout: ```bash nano reviews-virtualservice.yaml ``` ```yaml apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: reviews namespace: bookinfo spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 weight: 90 - destination: host: reviews subset: v3 weight: 10 timeout: 5s retries: attempts: 3 perTryTimeout: 2s retryOn: 5xx,connect-failure,reset ``` Aplica ambos y valida la configuración con `istioctl analyze`, que detecta errores como subconjuntos inexistentes: ```bash kubectl apply -f reviews-destinationrule.yaml -f reviews-virtualservice.yaml istioctl analyze -n bookinfo ``` ```output ✔ No validation issues found when analyzing namespace: bookinfo. ``` Para comprobar el reparto, haz 50 peticiones y cuenta cuántas páginas llevan estrellas rojas (`v3` usa el color `red`): ```bash for i in $(seq 1 50); do kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -s productpage:9080/productpage | grep -c 'color="red"' done | grep -vc '^0$' ``` ```output 5 ``` Un resultado de entre 2 y 9 sobre 50 encaja con el 10 %. Para completar el despliegue canario, cambia los pesos a `0` y `100` y vuelve a aplicar el VirtualService. ## Paso 6: Forzar mTLS entre servicios Istio cifra por defecto el tráfico entre pods con sidecar, pero en modo `PERMISSIVE`: sigue aceptando texto plano de clientes fuera de la malla. Con `STRICT`, los pods del namespace solo aceptan conexiones mTLS. Antes de activarlo, crea un cliente fuera de la malla para comprobar la diferencia: ```bash kubectl create namespace legacy kubectl run curl -n legacy --image=curlimages/curl --restart=Never --command -- sleep 3600 kubectl exec -n legacy curl -- curl -s -o /dev/null -w "%{http_code}\n" http://productpage.bookinfo:9080/productpage ``` ```output 200 ``` Crea la política `PeerAuthentication` para el namespace: ```bash nano mtls-strict.yaml ``` ```yaml apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: default namespace: bookinfo spec: mtls: mode: STRICT ``` ```bash kubectl apply -f mtls-strict.yaml kubectl exec -n legacy curl -- curl -s -o /dev/null -w "%{http_code}\n" http://productpage.bookinfo:9080/productpage ``` Ahora la conexión se corta, porque el cliente no presenta un certificado de la malla: ```output 000 command terminated with exit code 56 ``` Los servicios de Bookinfo siguen hablando entre sí sin cambios, porque sus sidecars negocian mTLS automáticamente. Si aplicas la misma política en el namespace `istio-system`, afectará a toda la malla. ## Paso 7: Restringir qué servicios pueden llamar a otros Con mTLS, cada pod tiene una identidad basada en su ServiceAccount, con la forma `cluster.local/ns//sa/`. Una `AuthorizationPolicy` usa esa identidad para permitir o denegar peticiones. En Bookinfo, solo `productpage` necesita llamar a `reviews`. Crea una política que lo permita y, como efecto, deniegue todo lo demás hacia `reviews`: ```bash nano reviews-authz.yaml ``` ```yaml apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: reviews-allow-productpage namespace: bookinfo spec: selector: matchLabels: app: reviews action: ALLOW rules: - from: - source: principals: ["cluster.local/ns/bookinfo/sa/bookinfo-productpage"] to: - operation: methods: ["GET"] ``` ```bash kubectl apply -f reviews-authz.yaml ``` Desde `ratings`, que no está autorizado, la petición a `reviews` se rechaza: ```bash kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -s -w "\n%{http_code}\n" reviews:9080/reviews/0 ``` ```output RBAC: access denied 403 ``` La página principal sigue mostrando las reseñas, porque `productpage` usa la ServiceAccount autorizada: ```bash kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -s productpage:9080/productpage | grep -c "Reviewer1" ``` ```output 1 ``` ## Paso 8: Visualizar la malla con Kiali Kiali dibuja el grafo de servicios a partir de las métricas que recoge Prometheus. Istio incluye manifiestos de ejemplo para ambos en `samples/addons`, pensados para pruebas (sin persistencia ni alta disponibilidad): ```bash kubectl apply -f samples/addons/prometheus.yaml kubectl apply -f samples/addons/kiali.yaml kubectl rollout status deployment/kiali -n istio-system ``` Genera algo de tráfico a través del gateway (con el `port-forward` del paso 4 todavía activo) y abre el panel. `istioctl dashboard` crea el reenvío de puertos hacia Kiali y abre el navegador: ```bash for i in $(seq 1 100); do curl -s -o /dev/null http://localhost:8080/productpage; done istioctl dashboard kiali ``` En **Traffic Graph**, selecciona el namespace `bookinfo`. Verás `productpage` llamando a `details` y a `reviews`, el reparto 90/10 entre `v1` y `v3`, y un candado en cada arista que indica que el tráfico va cifrado con mTLS. ## Solución de problemas **Los pods muestran `1/1` en lugar de `2/2`.** No se ha inyectado el sidecar. Comprueba la etiqueta con `kubectl get namespace bookinfo --show-labels` y reinicia los Deployments. La inyección solo ocurre al crear el pod. **Tras activar `STRICT`, un servicio deja de recibir tráfico.** Algún cliente está fuera de la malla, por ejemplo un pod sin sidecar o una sonda externa. Vuelve a `PERMISSIVE` mientras lo incorporas a la malla. **Las reglas de enrutado no se aplican.** Ejecuta `istioctl analyze -n ` y revisa la configuración que ha recibido el proxy con `istioctl proxy-config routes deploy/productpage-v1 -n bookinfo`. Recuerda que el VirtualService solo actúa sobre tráfico que sale de un pod con sidecar. **`403 RBAC: access denied` inesperados.** En cuanto un workload tiene alguna política `ALLOW`, todo lo que no coincide con ella se deniega. Revisa las políticas con `kubectl get authorizationpolicy -A`. ## Conclusión Tienes Istio funcionando con su plano de control, una aplicación con sidecars, un despliegue canario con reintentos y circuit breaking, mTLS obligatorio en el namespace, autorización basada en identidad y un panel de Kiali para ver el tráfico. Como siguientes pasos puedes publicar tus servicios con TLS en el ingress gateway usando cert-manager, sustituir los complementos de ejemplo por un Prometheus persistente o evaluar el modo ambient de Istio, que prescinde de los sidecars.