Service Mesh7 min read611 lines

Learning state

Track this guide

Saved in this browser only. No account required.

Service Mesh Master Class

Engineering-grade reference manual for Istio, Linkerd, Consul, traffic management, mTLS, observability, and production service-mesh operations.

Overview

A service mesh adds a dedicated infrastructure layer for service-to-service communication. Instead of embedding retries, TLS, telemetry, and traffic splitting in every application, the mesh provides those features through sidecars, proxies, gateways, policies, or node-level agents.

Use a service mesh when you need consistent cross-service security, progressive delivery, traffic control, and deep observability across many services. Do not add one just because Kubernetes is present; a mesh adds operational complexity.

When to Use a Service Mesh

Good fit:

  • many microservices with service-to-service traffic
  • strict mTLS requirements
  • canary or blue-green releases across services
  • standardized retries, timeouts, circuit breaking, and telemetry
  • multi-cluster or multi-tenant networking controls

Poor fit:

  • a small monolith or a few services
  • teams that cannot operate Kubernetes basics yet
  • clusters without good observability foundations
  • latency-sensitive workloads where proxy overhead is unacceptable
  • organizations without ownership for mesh upgrades and policy drift

Mesh Comparison

Mesh Strengths Tradeoffs
Istio powerful traffic management, gateways, policy ecosystem larger operational surface
Linkerd simple, lightweight, strong defaults fewer advanced traffic primitives
Consul service discovery, multi-platform, VM + Kubernetes support separate HashiCorp operational model

Istio Installation

Download and install Istio CLI:

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

Add istioctl to your path:

export PATH="$PWD/istio-*/bin:$PATH"

Install the default profile:

istioctl install --set profile=default -y

Verify control plane pods:

kubectl get pods -n istio-system

Check mesh status:

istioctl proxy-status

Istio Sidecar Injection

Enable namespace injection:

kubectl label namespace production istio-injection=enabled

Restart deployments to inject sidecars:

kubectl rollout restart deployment -n production

Confirm sidecars are present:

kubectl get pods -n production -o jsonpath='{range .items[*]}{.metadata.name}{" containers="}{range .spec.containers[*]}{.name}{","}{end}{"\n"}{end}'

Disable injection for a namespace:

kubectl label namespace production istio-injection-

Istio Gateway and VirtualService

Expose HTTP traffic through an Istio gateway:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: web-gateway
  namespace: production
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - web.example.com

Route traffic to a service:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web
  namespace: production
spec:
  hosts:
    - web.example.com
  gateways:
    - web-gateway
  http:
    - route:
        - destination:
            host: web.production.svc.cluster.local
            port:
              number: 8080

Apply manifests:

kubectl apply -f gateway.yaml
kubectl apply -f virtualservice.yaml

Inspect routes:

istioctl proxy-config routes deploy/web -n production

Canary Traffic Splitting

Split traffic between stable and canary versions:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: web
  namespace: production
spec:
  host: web.production.svc.cluster.local
  subsets:
    - name: stable
      labels:
        version: stable
    - name: canary
      labels:
        version: canary
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web
  namespace: production
spec:
  hosts:
    - web.production.svc.cluster.local
  http:
    - route:
        - destination:
            host: web.production.svc.cluster.local
            subset: stable
          weight: 90
        - destination:
            host: web.production.svc.cluster.local
            subset: canary
          weight: 10

Increase canary weight after validation:

kubectl patch virtualservice web -n production --type merge -p '{"spec":{"http":[{"route":[{"destination":{"host":"web.production.svc.cluster.local","subset":"stable"},"weight":50},{"destination":{"host":"web.production.svc.cluster.local","subset":"canary"},"weight":50}]}]}}'

Timeouts and Retries

Add timeout and retry policy:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api
  namespace: production
spec:
  hosts:
    - api.production.svc.cluster.local
  http:
    - timeout: 3s
      retries:
        attempts: 2
        perTryTimeout: 1s
        retryOn: gateway-error,connect-failure,refused-stream
      route:
        - destination:
            host: api.production.svc.cluster.local

Verify Envoy cluster settings:

istioctl proxy-config clusters deploy/api -n production

Circuit Breaking

Set connection pool and outlier detection:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: api-circuit-breaker
  namespace: production
spec:
  host: api.production.svc.cluster.local
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 10
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 2m
      maxEjectionPercent: 50

Istio mTLS

Check mesh mTLS status:

istioctl authn tls-check

Enable strict mTLS for a namespace:

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

Allow only a specific service account to call a workload:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend
  namespace: production
spec:
  selector:
    matchLabels:
      app: api
  action: ALLOW
  rules:
    - from:
        - source:
            principals:
              - cluster.local/ns/production/sa/frontend

Istio Observability

Open the Kiali dashboard:

istioctl dashboard kiali

Open Grafana if the add-on is installed:

istioctl dashboard grafana

Open Jaeger tracing:

istioctl dashboard jaeger

Inspect listener configuration:

istioctl proxy-config listeners deploy/web -n production

Inspect endpoints:

istioctl proxy-config endpoints deploy/web -n production

Linkerd Installation

Check cluster readiness:

linkerd check --pre

Install Linkerd CRDs and control plane:

linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -

Validate install:

linkerd check

Install dashboard extension:

linkerd viz install | kubectl apply -f -

Open dashboard:

linkerd viz dashboard

Linkerd Injection

Inject Linkerd into manifests:

kubectl get deploy -n production -o yaml | linkerd inject - | kubectl apply -f -

Annotate a namespace for automatic injection:

kubectl annotate namespace production linkerd.io/inject=enabled

Restart workloads:

kubectl rollout restart deployment -n production

Check meshed workloads:

linkerd viz stat deploy -n production

Linkerd Traffic Split

Install SMI extension if needed:

linkerd smi install | kubectl apply -f -

Example traffic split:

apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
  name: web-split
  namespace: production
spec:
  service: web
  backends:
    - service: web-stable
      weight: 900m
    - service: web-canary
      weight: 100m

Watch live request stats:

linkerd viz stat trafficsplit -n production

Consul Service Mesh

Install Consul on Kubernetes with Helm:

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install consul hashicorp/consul --namespace consul --create-namespace --set global.name=consul

Check Consul pods:

kubectl get pods -n consul

Register a service-defaults config entry:

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
  name: api
spec:
  protocol: http

Create an intentions policy:

consul intention create -allow web api

List intentions:

consul intention list

Observability Patterns

Key mesh metrics to monitor:

  • request rate by service
  • success/error rate by route
  • p50/p95/p99 latency
  • retry volume
  • mTLS handshake failures
  • proxy CPU and memory
  • control-plane health

Prometheus query examples:

sum(rate(istio_requests_total[5m])) by (destination_service)
histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket[5m])) by (le, destination_service))
sum(rate(istio_requests_total{response_code=~"5.."}[5m])) by (destination_service)

Debugging Service Mesh Issues

Check whether sidecars are injected:

kubectl get pod web-abc123 -n production -o jsonpath='{.spec.containers[*].name}'

Run Istio analyzer:

istioctl analyze -A

Inspect Envoy bootstrap:

istioctl proxy-config bootstrap deploy/web -n production

Inspect Envoy logs:

kubectl logs deploy/web -n production -c istio-proxy --tail=100

Check Linkerd proxy logs:

kubectl logs deploy/web -n production -c linkerd-proxy --tail=100

Port-forward a service for direct testing:

kubectl port-forward svc/web 8080:80 -n production

Run a temporary curl pod:

kubectl run curl -n production --rm -it --image=curlimages/curl -- sh

Production Rollout Checklist

  1. Define the service-mesh use case and owner.
  2. Install mesh in a non-production cluster.
  3. Add observability before enforcing policy.
  4. Mesh one low-risk namespace first.
  5. Measure latency and resource overhead.
  6. Enable mTLS in permissive mode, then strict mode.
  7. Add traffic policies gradually.
  8. Document rollback commands before rollout.
  9. Add dashboards and alerts for control-plane health.
  10. Schedule regular mesh upgrades.

Rollback Commands

Remove Istio injection from a namespace:

kubectl label namespace production istio-injection-
kubectl rollout restart deployment -n production

Uninstall Istio control plane:

istioctl uninstall --purge -y
kubectl delete namespace istio-system

Remove Linkerd from manifests:

kubectl get deploy -n production -o yaml | linkerd uninject - | kubectl apply -f -

Uninstall Linkerd:

linkerd uninstall | kubectl delete -f -

Uninstall Consul Helm release:

helm uninstall consul -n consul
kubectl delete namespace consul

Common Pitfalls

  • Installing a mesh before teams understand Kubernetes networking.
  • Enforcing strict mTLS before every workload has a sidecar.
  • Adding retries without timeouts, causing retry storms.
  • Forgetting that sidecars consume CPU and memory.
  • Treating dashboards as proof that policy is correct.
  • Allowing traffic rules to drift without code review.
  • Running multiple meshes in the same namespace without a migration plan.

Quick Reference

# Istio install and status
istioctl install --set profile=default -y
istioctl proxy-status
istioctl analyze -A

# Istio dashboards
istioctl dashboard kiali
istioctl dashboard grafana
istioctl dashboard jaeger

# Linkerd install and status
linkerd check --pre
linkerd install | kubectl apply -f -
linkerd check
linkerd viz stat deploy -n production

# Consul install and intentions
helm install consul hashicorp/consul --namespace consul --create-namespace
consul intention list

# Kubernetes mesh verification
kubectl get pods -n istio-system
kubectl get pods -n linkerd
kubectl get networkpolicy -A