You have an existing Kubernetes cluster running a microservices application — a frontend, an API gateway, several backend services, and a database. Services communicate over plain HTTP with no encryption, no retries, and no traffic control. This walkthrough adds Istio to give you mTLS everywhere, traffic management, and observability.
Step 1: Local Development Setup with Docker Compose
Before deploying to Kubernetes with Istio, verify the microservices work together locally.
# docker-compose.yml — Local development stack for the microservices
version: "3.8"
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- API_URL=http://api-gateway:8080
api-gateway:
build: ./api-gateway
ports:
- "8080:8080"
environment:
- USERS_SERVICE=http://users:8081
- ORDERS_SERVICE=http://orders:8082
- PAYMENTS_SERVICE=http://payments:8083
users:
build: ./users
environment:
- DATABASE_URL=postgresql://app:secret@postgres:5432/users
orders:
build: ./orders
environment:
- DATABASE_URL=postgresql://app:secret@postgres:5432/orders
- PAYMENTS_SERVICE=http://payments:8083
payments:
build: ./payments
environment:
- DATABASE_URL=postgresql://app:secret@postgres:5432/payments
postgres:
image: postgres:15
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
# local-test.sh — Verify services work locally
docker compose up -d
curl http://localhost:8080/health
docker compose down
Step 2: Install Istio on the Cluster
Install Istio with the default profile and enable sidecar injection for your application namespace.
# install-istio.sh — Install Istio and enable injection
istioctl install --set profile=default -y
kubectl create namespace microservices
kubectl label namespace microservices istio-injection=enabled
istioctl verify-install
Step 3: Deploy the Application with Helm
Use Helm to deploy the microservices into the mesh-enabled namespace. With sidecar injection enabled, each pod automatically gets an Envoy proxy.
# deploy-app.sh — Deploy microservices via Helm
helm install microservices ./helm/microservices \
--namespace microservices \
--set global.image.tag=v1.0.0 \
--wait
Verify all pods have 2 containers (app + istio-proxy sidecar):
# verify-injection.sh — Confirm sidecar injection
kubectl get pods -n microservices
# Each pod should show 2/2 READY
istioctl analyze -n microservices
Step 4: Enable Strict mTLS
Enforce mutual TLS across the entire mesh so all service-to-service communication is encrypted and authenticated.
# security/peer-authentication.yaml — Enforce strict mTLS mesh-wide
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
# apply-mtls.sh — Enable strict mTLS
kubectl apply -f security/peer-authentication.yaml
# Verify mTLS is active between services
istioctl proxy-config secret deploy/api-gateway -n microservices | head
Step 5: Configure Traffic Management
Set up destination rules for circuit breaking and virtual services for retries and timeouts.
# networking/destination-rules.yaml — Circuit breaking for backend services
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: users-service
namespace: microservices
spec:
host: users
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
http:
http1MaxPendingRequests: 50
http2MaxRequests: 100
outlierDetection:
consecutive5xxErrors: 3
interval: 10s
baseEjectionTime: 30s
maxEjectionPercent: 50
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: payments-service
namespace: microservices
spec:
host: payments
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 25
http2MaxRequests: 50
outlierDetection:
consecutive5xxErrors: 2
interval: 10s
baseEjectionTime: 60s
# networking/virtual-services.yaml — Retries and timeouts for API gateway routes
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-gateway
namespace: microservices
spec:
hosts:
- api-gateway
http:
- route:
- destination:
host: api-gateway
timeout: 15s
retries:
attempts: 3
perTryTimeout: 5s
retryOn: 5xx,reset,connect-failure
# apply-traffic.sh — Apply traffic management rules
kubectl apply -f networking/
Step 6: Set Up Ingress Gateway
Expose the application through Istio's ingress gateway with TLS.
# networking/gateway.yaml — Istio Gateway for external HTTPS access
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: app-gateway
namespace: microservices
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: app-tls-cert
hosts:
- "app.example.com"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-ingress
namespace: microservices
spec:
hosts:
- "app.example.com"
gateways:
- app-gateway
http:
- match:
- uri:
prefix: /api
route:
- destination:
host: api-gateway
port:
number: 8080
- route:
- destination:
host: frontend
port:
number: 3000
Step 7: Canary Deployment
Deploy a new version of the users service with traffic splitting to gradually roll out changes.
# canary/users-canary.yaml — Canary deployment with 10% traffic to v2
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: users-canary
namespace: microservices
spec:
hosts:
- users
http:
- route:
- destination:
host: users
subset: stable
weight: 90
- destination:
host: users
subset: canary
weight: 10
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: users-versions
namespace: microservices
spec:
host: users
subsets:
- name: stable
labels:
version: v1
- name: canary
labels:
version: v2
Step 8: Observability
Install Kiali, Grafana, and Jaeger to visualize the mesh traffic and trace requests across services.
# install-observability.sh — Deploy Istio observability addons
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/grafana.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/jaeger.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/kiali.yaml
# Access dashboards
istioctl dashboard kiali
istioctl dashboard grafana
istioctl dashboard jaeger
Verification
# verify-mesh.sh — Confirm the service mesh is fully operational
istioctl proxy-status
istioctl analyze -n microservices
kubectl get virtualservices,destinationrules,gateways -n microservices
curl -k https://app.example.com/api/health
The microservices now communicate over encrypted mTLS connections, have automatic retries and circuit breaking, support canary deployments through traffic splitting, and provide full observability through Kiali, Grafana, and Jaeger dashboards.