Cloud Native 30 min read

Istio Service Mesh Basics: What Is the Sidecar Pattern and Why Microservices Need It?

The article explains how traditional microservice architectures embed network concerns such as time‑outs, retries, circuit breaking, traffic monitoring and mTLS in application code, why this leads to code coupling, upgrade difficulty and duplicated effort, and how Istio’s sidecar‑based service mesh cleanly separates those concerns while providing traffic management, observability and security features.

Ops Community
Ops Community
Ops Community
Istio Service Mesh Basics: What Is the Sidecar Pattern and Why Microservices Need It?

Introduction: Why Microservices Need a Service Mesh

In traditional microservice setups, network functions (timeouts, retries, circuit breaking, traffic monitoring, mTLS encryption) are implemented in each service via code or SDKs such as Netflix Hystrix, Spring Cloud Feign, or Sentinel. This creates four main problems:

Code coupling : governance logic is scattered throughout business code.

Upgrade difficulty : changing a network policy requires code changes and redeployment.

Language inconsistency : each language (Java, Go, Python, Node.js) needs its own SDK, raising maintenance cost.

Reinventing the wheel : every team builds similar networking features without professional quality.

A service mesh moves this logic to the infrastructure layer, using a sidecar proxy so that application code is unaware of the underlying network governance, similar to how an OS handles TCP retransmission and congestion control.

Chapter 1 – What Is the Sidecar Pattern

1.1 Literal Meaning of Sidecar

A sidecar is an auxiliary container that runs alongside the main application container in the same Pod, handling network‑related functions.

# Istio‑injected Pod structure
apiVersion: v1
kind: Pod
metadata:
  name: productpage
  namespace: default
spec:
  containers:
  # Business container (no changes needed)
  - name: productpage
    image: istio/examples‑bookinfo‑productpage‑v1:1.16.2
    ports:
    - containerPort: 9080
    env:
    - name: ENV_VAR
      value: "production"
  # Sidecar proxy (automatically injected)
  - name: istio‑proxy
    image: docker.io/istio/proxyv2:1.17.2
    ports:
    - containerPort: 15090   # Prometheus metrics
    - containerPort: 15021   # Health check
    - containerPort: 15006   # Envoy inbound
    env:
    - name: ISTIO_META_DNS_AUTO_ALLOCATE
      value: "true"
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    args:
    - proxy
    - sidecar
    - --domain
    - $(POD_NAMESPACE).svc.cluster.local

1.2 How the Sidecar (Envoy) Works

Istio uses Envoy , a high‑performance L3/L4/L7 proxy written in C++ (originally from Lyft and now a CNCF graduated project), as the data‑plane proxy.

Envoy runs as a sidecar in each Pod, intercepting all inbound and outbound traffic:

Business container (productpage:9080)
        ↓ outbound traffic
Envoy Sidecar (localhost:15006)
        ↓
Service‑mesh data plane
        ↓
Target service Envoy Sidecar (productreviews:9080)
        ↓
Business container (productreviews:9080)

Inbound traffic arrives at Envoy first, which then forwards it to the business container. Outbound traffic is sent by the business container to localhost:15006; Envoy routes it according to its configuration.

1.3 Istio Control Plane (Istiod)

Istio’s architecture separates the data plane (Envoy sidecars) from the control plane (Istiod). Istiod manages configuration distribution, certificate issuance, and service discovery.

Data plane : Envoy sidecar proxies handling actual traffic.

Control plane (Istiod) : distributes xDS configurations, issues mTLS certificates, and watches Kubernetes Service/Endpoint resources.

# View Istiod pod
kubectl get pods -n istio-system
# Example output
# istiod-78d9f8c6b5-xjz4k   1/1   Running
# Istiod responsibilities:
# 1. Push xDS configs to each Envoy sidecar
# 2. Issue mTLS certificates for ServiceAccounts
# 3. Discover services from the Kubernetes API

Chapter 2 – Sidecar Traffic Interception and Forwarding

2.1 iptables Traffic Redirection

Istio installs iptables rules that redirect a Pod’s inbound and outbound traffic to the Envoy sidecar, enabling transparent injection.

# View iptables rules inside a Pod
kubectl exec -it productpage-v1-abc -c istio‑proxy -- iptables -t nat -L -n
# Simplified example output
# Chain PREROUTING (1 references)
# target     prot opt source               destination
# ISTIO_INBOUND  tcp  --  0.0.0.0/0            0.0.0.0/0
# Chain ISTIO_INBOUND (1 references)
# target     prot opt source               destination
# ISTIO_IN_REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9080   # redirect port 9080 inbound to Envoy
# Chain OUTPUT (1 references)
# target     prot opt source               destination
# ISTIO_OUTPUT tcp -- 0.0.0.0/0            0.0.0.0/0
# Chain ISTIO_OUTPUT (1 references)
# target     prot opt source               destination
# ISTIO_REDIRECT tcp -- 0.0.0.0/0 127.0.0.1
# ISTIO_REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9080   # redirect outbound to Envoy

Traffic interception flow :

Application container sends an HTTP request to http://reviews:9080/reviews/1.

iptables redirects the request to localhost:15006 (Envoy inbound port).

Envoy routes the request to the target service’s Pod IP based on its configuration.

The target Pod’s Envoy receives the request and forwards it to the local business container.

2.2 Transparent mTLS Encryption

Sidecar‑to‑sidecar communication uses mutual TLS by default. Istiod automatically issues and rotates certificates, so application code never handles TLS.

# Check mTLS configuration status
kubectl get peerauthentication -n istio-system
# NAME    MODE       AGE
# default PERMISSIVE 1d
# PERMISSIVE: allows both plaintext and mTLS (useful during migration)
# STRICT: only mTLS is allowed; non‑Istio traffic is rejected

2.3 Sidecar Scope Configuration

Although Istio intercepts all traffic by default, the Sidecar resource can limit the scope to specific Pods, reducing unnecessary forwarding.

# sidecar‑limit.yaml
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: productpage‑sidecar
  namespace: default
spec:
  workloadSelector:
    labels:
      app: productpage
  egress:
  - hosts:
    - "*/reviews.default.svc.cluster.local"   # only need reviews service
    - "*/details.default.svc.cluster.local"
    - "istio-system/*"                     # access Istio control plane

Chapter 3 – Istio Traffic Management Capabilities

3.1 VirtualService – Request Routing Rules

VirtualService defines how requests are routed to services and is the core of Istio traffic management.

# reviews‑virtualservice.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end‑user:
          exact: john
    route:
    - destination:
        host: reviews
        subset: v2
      weight: 100   # user john goes to v2
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 100   # other users go to v1
# Apply the configuration
kubectl apply -f reviews‑virtualservice.yaml
# Verify the config was pushed
kubectl exec -it productpage‑v1‑abc -c istio‑proxy -- pilot‑agent request GET config_dump | grep -A 5 "reviews.default"

3.2 DestinationRule – Subset and Circuit‑Breaking

DestinationRule defines subsets (versions) and load‑balancing policies, and can enable mTLS and outlier detection.

# reviews‑destinationrule.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL   # enable mTLS
    loadBalancer:
      simple: LEAST_REQUEST   # least‑connection LB
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: UPGRADE
        http1MaxPendingRequests: 100
        http2MaxRequest: 100
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 30s
      maxEjectionPercent: 50
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

3.3 Canary Deployment

Istio works with Kubernetes Deployments to perform canary releases without code changes.

# Deploy a new v2 replica (1 pod) while keeping 4 v1 pods
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews‑v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v2
  template:
    metadata:
      labels:
        app: reviews
        version: v2
    spec:
      containers:
      - name: reviews
        image: istio/examples‑bookinfo‑reviews‑v2:1.16.2
        ports:
        - containerPort: 9080
        env:
        - name: LOG_DIR
          value: "/test"
EOF

# Route 90% traffic to v1, 10% to v2
kubectl apply -f - <<'EOF'
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90
    - destination:
        host: reviews
        subset: v2
      weight: 10
EOF

3.4 Fault Injection

Istio can inject delays or aborts to test service resilience without modifying the application.

# fault‑injection.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        test‑user:
          exact: chaos
    fault:
      delay:
        percentage:
          value: 100
        fixedDelay: 5s   # all requests delayed 5 s
      abort:
        percentage:
          value: 50
        httpStatus: 503   # 50% of requests return 503
    route:
    - destination:
        host: reviews
        subset: v1
    - destination:
        host: reviews
        subset: v1

3.5 Timeout and Retry

# timeout‑retry.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v2
    retries:
      attempts: 3               # up to 3 retries
      perTryTimeout: 2s         # each try times out after 2 s
      retryOn: "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes"
    timeout: 10s                # total timeout

Chapter 4 – Observability – Traffic Monitoring

4.1 Kiali: Service Topology

Kiali is Istio’s visualization UI that shows service call graphs, traffic direction, success rates and latency distributions.

# List Kiali service
kubectl get svc -n istio-system | grep kiali
# Access the Kiali dashboard via Ingress or NodePort to view topology, arrows, health status, and latency.

4.2 Prometheus + Grafana: Metrics

Each sidecar exposes metrics at localhost:15090/metrics. Prometheus scrapes them and Grafana visualizes common metrics such as request count, latency, request/response sizes, and TCP connection closures.

# View metrics directly inside the pod
kubectl exec -it productpage‑v1‑abc -c istio‑proxy -- curl -s localhost:15090/metrics | head -30
# Example Prometheus queries
# Success rate per service
sum(rate(istio_requests_total{reporter="destination"}[5m])) by (destination_service) /
  sum(rate(istio_requests_total{reporter="destination"}[5m])) by (destination_service)
# P99 latency
histogram_quantile(0.99, sum(rate(istio_request_duration_milliseconds_bucket[5m])) by (le, destination_service))

4.3 Jaeger: Distributed Tracing

Istio automatically generates B3 trace IDs for each request and forwards them to Jaeger.

# Find Jaeger service
kubectl get svc -n istio-system | grep jaeger
# Send a request with a custom trace ID
kubectl exec -it productpage‑v1‑abc -c productpage -- \
  curl -s -H "x-request-id: test-123" http://reviews:9080/reviews/1
# Search for "test-123" in Jaeger UI to see the full call chain and per‑hop latency.

Chapter 5 – Security – mTLS and Authorization Policies

5.1 Enforcing mTLS (PeerAuthentication)

# strict‑mtls.yaml
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT   # force all traffic to use mTLS

5.2 AuthorizationPolicy

Fine‑grained access control can be expressed based on service accounts, namespaces, request paths, etc.

# productpage‑authz.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: productpage‑viewer
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  rules:
  # Allow traffic from the ingress gateway
  - from:
    - source:
        principals:
        - "cluster.local/ns/istio-system/sa/istio‑ingressgateway‑service‑account"
    to:
    - operation:
        paths:
        - /health
        - /ready
  # Allow productpage to call reviews service via GET
  - from:
    - source:
        principals:
        - "cluster.local/ns/default/sa/bookinfo‑productpage"
    to:
    - operation:
        host: "reviews.default.svc.cluster.local"
        methods:
        - GET

Chapter 6 – Sidecar Performance and Resource Overhead

6.1 Resource Consumption

Envoy is a high‑performance C++ proxy. A typical sidecar uses 50‑100 MiB of memory and, under low traffic, negligible CPU; under high traffic it may consume 5‑10 % of total CPU.

# View resource usage of a productpage pod
kubectl top pod -n default -l app=productpage
# Sample output
# NAME                     CPU(cores)   MEMORY(bytes)
# productpage‑v1‑abc123   50m          128Mi   ← business container
# productpage‑v1‑abc123   15m          45Mi    ← istio‑proxy

6.2 Controlling Sidecar Injection

# Disable injection at namespace level
kubectl label namespace default istio‑injection=disabled
# Enable injection at namespace level
kubectl label namespace default istio‑injection=enabled
# Disable injection for a single pod (annotation takes precedence)
apiVersion: v1
kind: Pod
metadata:
  annotations:
    sidecar.istio.io/inject: "false"
spec:
  ...

Summary: Core Value of the Sidecar Pattern

Decouples network functions from business code : developers no longer handle mTLS, timeouts, retries, or circuit breaking.

Language‑agnostic uniformity : services written in different languages share the same governance capabilities.

Centralized configuration : network policies change without code modifications or redeployment.

Zero‑cost observability : sidecars automatically expose metrics, traces and logs.

Istio’s sidecar model is a standard solution for cloud‑native service‑to‑service communication, though it has a steep learning curve. Start with traffic management (VirtualService, DestinationRule), then explore observability and security, and finally master fine‑grained authorization.

Supplement 1 – Istio Ambient Mode (Sidecar‑less Architecture)

What Is Ambient Mode?

Traditional Istio injects a per‑pod Envoy sidecar, adding a container and requiring pod restarts. Istio 1.18 introduced Ambient mode (also called “Waypoint proxy”) that replaces per‑pod sidecars with a node‑level ztunnel and per‑namespace waypoint proxies.

# Enable Ambient mode (experimental, Istio 1.18+)
istioctl install --set values.experimental.ambient=true
# Verify components
kubectl get pods -n istio-system
# Expected output includes:
# istiod‑xxx          1/1   Running
# ztunnel‑xxx        1/1   Running   ← one L4 proxy per node

Ambient vs Sidecar Comparison

Architecture : Sidecar = per‑pod Envoy; Ambient = per‑node ztunnel + per‑namespace waypoint.

Resource overhead : Sidecar adds 50‑100 MiB memory per pod; Ambient adds a single ztunnel per node (lower overall usage).

Deployment impact : Sidecar requires pod restarts for injection/removal; Ambient does not.

L7 governance : Fully supported by sidecar; Ambient needs a waypoint proxy.

Intrusiveness : Sidecar is high (modifies pod spec); Ambient is low (node‑level).

Production readiness : Sidecar is GA; Ambient is currently Alpha/experimental.

How Ambient Works

# Traffic flow in Ambient mode
# L4 traffic (unencrypted → ztunnel encrypts):
# Pod A → ztunnel (Node A) → mTLS → ztunnel (Node B) → Pod B
# L7 traffic (requires waypoint):
# Pod A → ztunnel (Node A) → waypoint proxy → ztunnel (Node B) → Pod B
# Create a waypoint proxy for a namespace
kubectl apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: productpage‑waypoint
  namespace: default
spec:
  gatewayClassName: istio‑waypoint
EOF

Supplement 2 – Istio Installation and Version Selection

Preparation

# Check Kubernetes version (Istio 1.18 requires K8s 1.25+)
kubectl version --short
# Verify node resources for Istiod and sidecars
kubectl describe node | grep -E "Allocated resources|CPU|Memory"
# Ensure sufficient storage for Istiod PVCs
kubectl get pvc -n istio-system

Install with istioctl

# Download istioctl (Linux)
curl -L https://istio.io/downloadIstio | sh -
cd istio-1.18.2
export PATH=$PWD/bin:$PATH
# Verify download
istioctl version
# Install with the default profile (production‑ready)
istioctl install --set profile=default -y
# List installed pods
kubectl get pods -n istio-system
# Expected components:
# istiod‑xxx               1/1   Running
# istio‑egressgateway‑xxx  1/1   Running   ← optional egress gateway
# istio‑ingressgateway‑xxx 1/1   Running   ← inbound gateway

Namespace‑Level Sidecar Injection

# Enable automatic injection for the default namespace
kubectl label namespace default istio‑injection=enabled
# Verify label
kubectl describe namespace default | grep istio
# Existing pods need a restart to get the sidecar
kubectl rollout restart deployment -n default
# Confirm each pod now has two containers (business + istio‑proxy)
kubectl get pod -n default -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.containers[*].name}{"
"}{end}'
# Disable injection (new pods will not get a sidecar; existing sidecars stay until pods are recreated)
kubectl label namespace default istio‑injection=disabled --overwrite

Uninstall Istio

# Remove Istio using istioctl
istioctl uninstall -p default --purge -y
# Delete CRDs and the istio-system namespace
kubectl delete namespace istio-system
kubectl delete crd $(kubectl get crd | grep istio | awk '{print $1}')

Supplement 3 – Envoy Management Ports and Troubleshooting

Envoy Admin Ports

Each sidecar exposes several management ports:

15000 – Envoy admin interface (requires port‑forward).

15020 – Health check.

15021 – Statistics.

15090 – Prometheus metrics.

# Enter the sidecar container
kubectl exec -it productpage‑v1‑abc -c istio‑proxy -- sh
# Access admin interface via port‑forward
kubectl port-forward productpage‑v1‑abc 15000:15000 -c istio‑proxy &
# View Envoy configuration
curl localhost:15000/config_dump | jq .
# List clusters
curl localhost:15000/clusters | grep -E "^localhost|^productpage" | head -20
# View listeners
curl localhost:15000/listeners | jq .

Common Troubleshooting Scenarios

Service‑to‑service communication failure

Check sidecar injection:

kubectl get pod <em>pod-name</em> -o jsonpath='{.spec.containers[*].name}'

(should include istio-proxy).

Inspect istio‑proxy logs: kubectl logs <em>pod-name</em> -c istio-proxy | tail -50.

Verify DestinationRule and VirtualService exist: kubectl get destinationrule -A, kubectl get virtualservice -A.

Test service discovery from the source pod:

kubectl exec -it productpage‑v1‑abc -c productpage -- curl -s http://reviews:9080/reviews/1

. If successful but slow, check Envoy stats:

kubectl exec -it productpage‑v1‑abc -c istio-proxy -- curl -s localhost:15000/stats | grep "cluster.outbound"

.

mTLS handshake failure

Inspect PeerAuthentication: kubectl get peerauthentication -A.

Check the target service’s certificate inside its sidecar:

kubectl exec -it reviews‑v1‑abc -c istio-proxy -- openssl s_client -connect localhost:9080 2>/dev/null | openssl x509 -noout -dates

.

Verify Istiod is issuing certificates:

kubectl logs -n istio-system istiod-xxx | grep -i "certificate\|error" | tail -20

.

Unexpected traffic drops

List AuthorizationPolicy resources: kubectl get authorizationpolicy -A.

Check Envoy access logs for DENY entries:

kubectl exec -it productpage‑v1‑abc -c istio-proxy -- tail -f /var/log/istio/istio-access.log

.

Confirm the Envoy configuration was correctly pushed:

curl localhost:15000/config_dump | jq '.configs[] | select(."@type" | contains("Listener"))'

.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ObservabilityKubernetesIstioService MeshEnvoySidecarmTLS
Ops Community
Written by

Ops Community

A leading IT operations community where professionals share and grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.