| name | service-mesh |
| description | Implement Istio and Linkerd service meshes. Configure mTLS, traffic management, and observability. Use when managing microservices communication. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
Service Mesh
Implement service-to-service communication management with mTLS, traffic shaping, observability, and policy enforcement using Istio or Linkerd.
When to Use
- Securing microservice communication with automatic mTLS.
- Implementing canary deployments, traffic splitting, or A/B testing.
- Adding circuit breakers, retries, and timeouts without changing application code.
- Gaining service-level observability (latency, error rates, request volume).
- Enforcing authorization policies between services.
Prerequisites
- Kubernetes cluster (1.26+) with kubectl configured.
- Helm 3 installed (for some installation methods).
- Sufficient cluster resources (Istio control plane needs ~2 GB RAM).
- For Istio:
istioctl CLI installed.
- For Linkerd:
linkerd CLI installed.
Istio Installation
Install with istioctl
curl -L https://istio.io/downloadIstio | sh -
cd istio-*
export PATH=$PWD/bin:$PATH
istioctl install --set profile=default -y
istioctl install --set profile=demo -y
istioctl verify-install
kubectl get pods -n istio-system
Enable Sidecar Injection
kubectl label namespace default istio-injection=enabled
kubectl get namespace default --show-labels
kubectl rollout restart deployment -n default
kubectl get pods -n default -o jsonpath='{range .items[*]}{.metadata.name}{" containers: "}{range .spec.containers[*]}{.name}{" "}{end}{"\n"}{end}'
Install Observability Addons
kubectl apply -f samples/addons/prometheus.yaml
kubectl apply -f samples/addons/grafana.yaml
kubectl apply -f samples/addons/jaeger.yaml
kubectl apply -f samples/addons/kiali.yaml
kubectl rollout status deployment/kiali -n istio-system
istioctl dashboard kiali
istioctl dashboard grafana
istioctl dashboard jaeger
Traffic Management
VirtualService (Routing Rules)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-app
namespace: default
spec:
hosts:
- my-app
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: my-app
subset: canary
- route:
- destination:
host: my-app
subset: stable
weight: 90
- destination:
host: my-app
subset: canary
weight: 10
timeout: 30s
retries:
attempts:
DestinationRule (Subsets and Connection Policy)
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-app
namespace: default
spec:
host: my-app
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: DEFAULT
http1MaxPendingRequests: 100
http2MaxRequests: 1000
maxRequestsPerConnection: 10
outlierDetection:
consecutive5xxErrors: 5
interval: 10s
baseEjectionTime: 30s
maxEjectionPercent: 50
subsets:
- name: stable
labels:
version: v1
- name: canary
labels:
version: v2
Gateway (Ingress Traffic)
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: app-gateway
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: app-tls-cert
hosts:
- app.example.com
- port:
number: 80
name: http
protocol: HTTP
hosts:
- app.example.com
tls:
httpsRedirect: true
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-external
mTLS Configuration
Strict mTLS (Cluster-Wide)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
Permissive mTLS (Per Namespace)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: legacy-apps
spec:
mtls:
mode: PERMISSIVE
Verify mTLS Status
istioctl x describe pod <pod-name> -n default
istioctl proxy-config cluster <pod-name>.default --fqdn my-app.default.svc.cluster.local -o json | grep -A5 "tlsContext"
istioctl authn tls-check <pod-name>.default my-app.default.svc.cluster.local
Authorization Policies
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: api-access
namespace: default
spec:
selector:
matchLabels:
app: my-api
action: ALLOW
rules:
- from:
- source:
principals:
- "cluster.local/ns/default/sa/frontend"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: default
spec:
selector:
matchLabels:
app: my-api
action: DENY
rules:
- from:
Circuit Breaking
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-api-circuit-breaker
spec:
host: my-api
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
http:
http1MaxPendingRequests: 50
http2MaxRequests: 100
maxRetries: 3
outlierDetection:
consecutive5xxErrors: 3
interval: 15s
baseEjectionTime: 60s
maxEjectionPercent: 100
Linkerd Installation
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
export PATH=$HOME/.linkerd2/bin:$PATH
linkerd check --pre
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
linkerd check
kubectl get deploy -n my-app -o yaml | linkerd inject - | kubectl apply -f -
kubectl annotate namespace my-app linkerd.io/inject=enabled
linkerd viz install | kubectl apply -f -
linkerd viz dashboard
Linkerd Traffic Split (SMI)
apiVersion: split.smi-spec.io/v1alpha4
kind: TrafficSplit
metadata:
name: my-app-split
namespace: default
spec:
service: my-app
backends:
- service: my-app-stable
weight: 900
- service: my-app-canary
weight: 100
Debugging
istioctl proxy-config routes <pod-name>.default
istioctl proxy-config clusters <pod-name>.default
istioctl proxy-config listeners <pod-name>.default
istioctl analyze -n default
istioctl proxy-config log <pod-name>.default --level debug
linkerd viz stat deploy -n default
linkerd viz top deploy/my-app -n default
linkerd viz edges deploy -n default
Troubleshooting
| Symptom | Cause | Fix |
|---|
| Sidecar not injected | Missing namespace label | Add istio-injection=enabled label; restart pods |
| 503 errors between services | mTLS mismatch (one side plaintext) | Set PeerAuthentication to PERMISSIVE during migration |
| High latency after mesh install | Sidecar resource limits too low | Increase sidecar CPU/memory limits in mesh config |
| VirtualService not routing | Missing DestinationRule subsets | Create matching DestinationRule with subset labels |
upstream connect error | Circuit breaker tripped | Check outlier detection settings; increase thresholds |
| Authorization policy blocks everything | Default deny without matching allow rule | Add explicit ALLOW rule before DENY-all |
| Kiali shows "Unknown" traffic | Missing sidecar on calling service | Inject sidecar into all communicating services |
Related Skills