TL;DR

Istio injects Envoy sidecars or uses ambient dataplane to control service-to-service traffic. Debug by checking injection, sidecar readiness, istiod config push, VirtualService/DestinationRule, mTLS policy, and Envoy config.

Sidecar Traffic Flow

Source PodAppEnvoyDestination PodEnvoyAppistiodconfig pushAll traffic passes through Envoy sidecars; VirtualService/DestinationRule control routing and mTLS.

Istio sidecar mode: outbound Envoy → inbound Envoy → app container.

Components

ComponentRoleCheck
istiodControl plane; sends config to proxies.kubectl get pods -n istio-system
Envoy sidecarPer-Pod proxy for traffic and telemetry.Pod has istio-proxy container.
GatewayIngress/egress proxy entry point.Gateway Service and routes.
VirtualServiceHTTP/TCP routing rules.Host, gateway, destination subset.
DestinationRuleSubsets, load balancing, TLS policy.Subset labels match Pods.

Injection And Health

bashistio-health.sh
kubectl get pods -n istio-system -o wide # istiod and gateways should be Ready.
kubectl get namespace app --show-labels # Look for istio-injection=enabled or revision label.
kubectl get pod -n app web-api-abc123 -o jsonpath='{.spec.containers[*].name}' # Should include istio-proxy in sidecar mode.
kubectl describe pod -n app web-api-abc123 # Sidecar injection, readiness, and init container events.
kubectl logs -n istio-system deploy/istiod --tail=100 # Config push, validation, cert, or discovery issues.

Canary Traffic Split

yamlvirtualservice-destinationrule.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: web-api
  namespace: app
spec:
  host: web-api.app.svc.cluster.local
  subsets:
    - name: stable
      labels:
        version: v1 # Must match labels on stable Pods.
    - name: canary
      labels:
        version: v2 # Must match labels on canary Pods.
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-api
  namespace: app
spec:
  hosts:
    - web-api.app.svc.cluster.local
  http:
    - route:
        - destination:
            host: web-api.app.svc.cluster.local
            subset: stable
          weight: 90
        - destination:
            host: web-api.app.svc.cluster.local
            subset: canary
          weight: 10

mTLS Policy

yamlpeer-authentication.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: app
spec:
  mtls:
    mode: STRICT # Workloads must use Istio mTLS; non-mesh clients may fail.
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend
  namespace: app
spec:
  selector:
    matchLabels:
      app: web-api # Destination workload.
  rules:
    - from:
        - source:
            principals:
              - cluster.local/ns/app/sa/frontend # Source workload identity.
      to:
        - operation:
            methods: ["GET"]
            paths: ["/health"]

Debugging Commands

bashistio-debug.sh
istioctl analyze -A # Static analysis for broken Istio config.
istioctl proxy-status # Shows whether Envoy proxies are synced with istiod.
istioctl proxy-config routes web-api-abc123.app # Envoy route config for the Pod.
istioctl proxy-config clusters web-api-abc123.app | grep web-api
istioctl proxy-config listeners web-api-abc123.app

kubectl logs -n app web-api-abc123 -c istio-proxy --tail=100 # Envoy access/errors.
kubectl get virtualservice,destinationrule,gateway,peerauthentication,authorizationpolicy -A

Gotchas

  • !Namespace labels affect sidecar injection only for newly created Pods; restart workloads after enabling injection.
  • !DestinationRule subset labels must match Pod labels exactly or traffic gets no healthy upstream.
  • !STRICT mTLS breaks calls from non-mesh clients unless you design exceptions.
  • !VirtualService host names must match the caller's destination host, not just the Service you intended.