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.
kubectlconfigurado 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:
curl -fsSL https://istio.io/downloadIstio -o downloadIstio.sh
less downloadIstio.sh
sh downloadIstio.sh
El script crea un directorio istio-<versión> con el binario en bin/ y los ejemplos en samples/. Entra en él e instala el binario en tu PATH:
cd istio-*/
sudo install -m 0755 bin/istioctl /usr/local/bin/istioctl
istioctl version
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/.
Consejopara instalar una versión concreta, ejecuta el script con
ISTIO_VERSION=<versión> 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:
istioctl x precheck
✔ No issues found when checking the cluster. Istio is safe to install or upgrade!
Instala Istio con el perfil default:
istioctl install --set profile=default -y
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ Installation complete
Comprueba que los pods del namespace istio-system están en ejecución:
kubectl get pods -n istio-system
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:
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:
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:
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:
kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
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:
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:
kubectl get svc istio-ingressgateway -n istio-system
Si EXTERNAL-IP se queda en <pending>, reenvía un puerto local al gateway para probar:
kubectl port-forward -n istio-system svc/istio-ingressgateway 8080:80
En otra terminal, pide la página:
curl -s http://localhost:8080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
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):
nano reviews-destinationrule.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:
nano reviews-virtualservice.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:
kubectl apply -f reviews-destinationrule.yaml -f reviews-virtualservice.yaml
istioctl analyze -n bookinfo
✔ 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):
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$'
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:
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
200
Crea la política PeerAuthentication para el namespace:
nano mtls-strict.yaml
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: bookinfo
spec:
mtls:
mode: STRICT
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:
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/<namespace>/sa/<serviceaccount>. 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:
nano reviews-authz.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"]
kubectl apply -f reviews-authz.yaml
Desde ratings, que no está autorizado, la petición a reviews se rechaza:
kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -s -w "\n%{http_code}\n" reviews:9080/reviews/0
RBAC: access denied
403
La página principal sigue mostrando las reseñas, porque productpage usa la ServiceAccount autorizada:
kubectl exec -n bookinfo deploy/ratings-v1 -c ratings -- curl -s productpage:9080/productpage | grep -c "Reviewer1"
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):
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:
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 <namespace> 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.
