Service Mesh with Istio: A Practical Guide

November 26, 2018

Service mesh has become the answer to many microservices challenges: mTLS, traffic management, observability, and resilience. Istio is the most popular implementation, but it’s also complex and easy to misconfigure.

Here’s a practical guide to implementing Istio in production.

What Service Mesh Provides

Traffic Management

Route traffic based on rules:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: beta-tester
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v1

Capabilities:

Security

Automatic mTLS between services:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT

Capabilities:

Observability

Automatic metrics, tracing, and logging:

Architecture

Components

┌─────────────────────────────────────────────────────────┐
│                        Control Plane                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │   Istiod    │  │   Config    │  │   Certificate   │  │
│  │ (Pilot+     │  │   Server    │  │   Authority     │  │
│  │  Citadel+   │  │             │  │                 │  │
│  │  Galley)    │  │             │  │                 │  │
│  └─────────────┘  └─────────────┘  └─────────────────┘  │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                        Data Plane                        │
│  ┌─────────────────┐    ┌─────────────────┐             │
│  │   App Pod       │    │   App Pod       │             │
│  │ ┌───────────┐   │    │ ┌───────────┐   │             │
│  │ │ App       │   │    │ │ App       │   │             │
│  │ └───────────┘   │    │ └───────────┘   │             │
│  │ ┌───────────┐   │    │ ┌───────────┐   │             │
│  │ │ Envoy     │◄──┼────┼─│ Envoy     │   │             │
│  │ │ (sidecar) │   │    │ │ (sidecar) │   │             │
│  │ └───────────┘   │    │ └───────────┘   │             │
│  └─────────────────┘    └─────────────────┘             │
└─────────────────────────────────────────────────────────┘

Istiod: Unified control plane (config, certificates, service discovery) Envoy sidecars: Proxy all traffic to/from application

Sidecar Injection

Automatic injection via namespace label:

kubectl label namespace production istio-injection=enabled

Or manual sidecar addition:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    sidecar.istio.io/inject: "true"

Getting Started

Installation

# Download Istio
curl -L https://istio.io/downloadIstio | sh -

# Install with demo profile (for learning)
istioctl install --set profile=demo

# Install with production profile (minimal, secure)
istioctl install --set profile=minimal

Profiles

ProfileUse Case
demoLearning, has everything enabled
minimalProduction starting point
defaultProduction with sensible defaults
emptyCustom configuration only

Start minimal and add what you need.

Verify Installation

istioctl analyze
kubectl get pods -n istio-system

Traffic Management

VirtualService

Define how requests are routed:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: api
spec:
  hosts:
  - api
  http:
  - route:
    - destination:
        host: api
        subset: v1
      weight: 90
    - destination:
        host: api
        subset: v2
      weight: 10

DestinationRule

Define subsets and load balancing:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: api
spec:
  host: api
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 100
        http2MaxRequests: 1000
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

Canary Deployments

# Start with 0% traffic to new version
http:
- route:
  - destination:
      host: api
      subset: v1
    weight: 100
  - destination:
      host: api
      subset: v2
    weight: 0

# Gradually increase
# weight: 10, 25, 50, 100

Fault Injection

Test resilience:

http:
- fault:
    delay:
      percentage:
        value: 10
      fixedDelay: 5s
    abort:
      percentage:
        value: 5
      httpStatus: 500
  route:
  - destination:
      host: api

Security

mTLS Configuration

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT  # Or PERMISSIVE during migration

Authorization Policies

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: api-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: api
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/production/sa/frontend"]
    to:
    - operation:
        methods: ["GET", "POST"]
        paths: ["/api/*"]

JWT Authentication

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-auth
spec:
  selector:
    matchLabels:
      app: api
  jwtRules:
  - issuer: "https://auth.example.com"
    jwksUri: "https://auth.example.com/.well-known/jwks.json"
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
spec:
  selector:
    matchLabels:
      app: api
  rules:
  - from:
    - source:
        requestPrincipals: ["*"]

Observability

Metrics

Istio automatically generates metrics:

# Request rate
rate(istio_requests_total{destination_service="api"}[5m])

# Error rate
rate(istio_requests_total{destination_service="api", response_code=~"5.."}[5m])
/ rate(istio_requests_total{destination_service="api"}[5m])

# Latency
histogram_quantile(0.99, rate(istio_request_duration_milliseconds_bucket{destination_service="api"}[5m]))

Distributed Tracing

Enable with Jaeger:

kubectl apply -f samples/addons/jaeger.yaml
istioctl dashboard jaeger

Traces propagate automatically through Envoy sidecars.

Kiali Dashboard

Service mesh visualization:

kubectl apply -f samples/addons/kiali.yaml
istioctl dashboard kiali

Shows:

Common Pitfalls

Resource Overhead

Sidecars consume resources:

Mitigation:

Configuration Complexity

Easy to create invalid or conflicting config:

Mitigation:

mTLS Migration

Enabling strict mTLS breaks traffic from non-mesh services:

# Start permissive
mtls:
  mode: PERMISSIVE

# Verify all traffic is mTLS
# Then switch to STRICT
mtls:
  mode: STRICT

Sidecar Resource Limits

Default limits may not fit your workload:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    sidecar.istio.io/proxyCPU: "100m"
    sidecar.istio.io/proxyMemory: "128Mi"
    sidecar.istio.io/proxyCPULimit: "500m"
    sidecar.istio.io/proxyMemoryLimit: "256Mi"

Production Readiness

Checklist

Debugging

# Check proxy status
istioctl proxy-status

# View Envoy config
istioctl proxy-config routes <pod-name>
istioctl proxy-config clusters <pod-name>

# Check for config issues
istioctl analyze

# Debug traffic
istioctl x describe pod <pod-name>

Key Takeaways

Service mesh is powerful but complex. Start small, learn the concepts, and expand gradually.