Istio Service Mesh#
Istio adds a proxy sidecar (Envoy) to every pod in the mesh. These proxies handle traffic routing, mutual TLS, retries, circuit breaking, and telemetry without changing application code. The control plane (istiod) pushes configuration to all sidecars.
When You Actually Need a Service Mesh#
You need Istio when you have multiple services requiring mTLS, fine-grained traffic control (canary releases, fault injection), or consistent observability across service-to-service communication. If you have fewer than five services, standard Kubernetes Services and NetworkPolicies are sufficient. A service mesh adds operational complexity – more moving parts, higher memory usage per sidecar, and a learning curve for proxy-level debugging.
Installation#
istioctl (Recommended for Getting Started)#
istioctl install --set profile=demo -yProfiles: minimal (just istiod), default (istiod + ingress gateway), demo (everything). Use default for production.
Helm (Better for GitOps)#
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm upgrade --install istio-base istio/base \
--namespace istio-system --create-namespace
helm upgrade --install istiod istio/istiod \
--namespace istio-system --wait
helm upgrade --install istio-ingress istio/gateway \
--namespace istio-ingress --create-namespaceIstio Operator#
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-control-plane
namespace: istio-system
spec:
profile: default
meshConfig:
accessLogFile: /dev/stdoutSidecar Injection#
Label the namespace for automatic injection. Existing pods must be restarted afterward.
kubectl label namespace payments istio-injection=enabled
kubectl rollout restart deployment -n paymentsFor selective injection, use manual mode:
kubectl apply -f <(istioctl kube-inject -f deployment.yaml)Traffic Management#
VirtualService defines routing rules. DestinationRule defines policies applied after routing (load balancing, connection pool, outlier detection).
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: payments-api
namespace: payments
spec:
host: payments-api
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: payments-api
namespace: payments
spec:
hosts:
- payments-api
http:
- route:
- destination:
host: payments-api
subset: v1
weight: 90
- destination:
host: payments-api
subset: v2
weight: 10
retries:
attempts: 3
perTryTimeout: 2s
retryOn: "5xx,reset,connect-failure"Both subsets point to the same Kubernetes Service but select different pod labels. Your v2 Deployment must have version: v2 in its pod labels.
Canary Release Workflow#
- Deploy the new version alongside the old one with a different
versionlabel. - Create a DestinationRule with subsets for both versions.
- Shift VirtualService weights gradually: 95/5, 80/20, 50/50, then 100 to the new version.
- Monitor error rates at each step. Roll back by shifting weight to v1.
kubectl patch virtualservice payments-api -n payments --type merge -p '
spec:
http:
- route:
- destination:
host: payments-api
subset: v1
weight: 50
- destination:
host: payments-api
subset: v2
weight: 50'Mutual TLS (mTLS)#
PeerAuthentication controls mTLS. With STRICT, only mTLS connections are accepted – pods outside the mesh cannot reach pods inside. Use PERMISSIVE during migration.
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: payments
spec:
mtls:
mode: STRICTApply in istio-system namespace to enforce mesh-wide.
Authorization Policies#
L7 access control – the service mesh equivalent of NetworkPolicies but with HTTP method and path awareness.
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payments-api-policy
namespace: payments
spec:
selector:
matchLabels:
app: payments-api
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/frontend/sa/frontend-sa"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/payments/*"]
- from:
- source:
principals: ["cluster.local/ns/monitoring/sa/prometheus"]
to:
- operation:
methods: ["GET"]
paths: ["/metrics"]The principals field uses SPIFFE identities assigned based on service accounts. Everything not explicitly allowed is denied.
Observability#
Istio sidecars emit metrics, traces, and access logs automatically.
# Kiali - service mesh dashboard with traffic graph
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml
# Jaeger - distributed tracing
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/jaeger.yaml
# Prometheus + Grafana with pre-built Istio dashboards
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/prometheus.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/grafana.yamlFor tracing to work across services, your application must propagate trace headers (x-request-id, x-b3-traceid, etc.). Istio only creates spans at the proxy level.
Debugging Istio Issues#
# Verify proxy configuration across the mesh
istioctl proxy-status
# Check for configuration conflicts
istioctl analyze -n payments
# View Envoy access logs
kubectl logs <pod-name> -c istio-proxy -n paymentsThe most common issue is sidecar not being injected – verify the namespace label and that pods were restarted. The second most common is VirtualService host not matching the actual Kubernetes Service name.