Skip to content

Gateway and Service Mesh — API Gateways with Istio and Linkerd

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you'll learn about Gateway Service Mesh. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

An API gateway and service mesh complement each other. The gateway handles north-south traffic while the mesh handles east-west traffic within the cluster.

What You'll Learn

By the end of this lesson, you will understand the difference between gateway and service mesh, configure sidecar injection for gateway pods, implement mTLS between gateway and services, and use traffic policies for canary routing.

Why It Matters

Understanding when to use a gateway versus a service mesh prevents overcomplicating your architecture and ensures each component handles the right responsibilities.

Real-World Use

Durga Antivirus Pro uses an API gateway for external traffic and Istio service mesh for internal service-to-service communication with automatic mTLS and traffic splitting.

Gateway vs Service Mesh

flowchart TD
    Internet-->Gateway[API Gateway]
    Gateway-->Mesh[Service Mesh]
    subgraph Mesh[Service Mesh (Istio)]
        SidecarA[Sidecar Proxy]-->ServiceA[Service A]
        SidecarB[Sidecar Proxy]-->ServiceB[Service B]
        SidecarC[Sidecar Proxy]-->ServiceC[Service C]
        ServiceA-->SidecarB
        ServiceB-->SidecarC
    end

Gateway and Mesh Responsibility Split

Define clear responsibilities between the gateway and service mesh.

from typing import Dict, List, Optional

class ResponsibilityMatrix:
    def __init__(self):
        self.gateway: List[str] = []
        self.mesh: List[str] = []
        self.both: List[str] = []

    def assign_gateway(self, feature: str):
        self.gateway.append(feature)

    def assign_mesh(self, feature: str):
        self.mesh.append(feature)

    def assign_both(self, feature: str):
        self.both.append(feature)

    def get_summary(self) -> Dict:
        return {
            "gateway_only": self.gateway,
            "mesh_only": self.mesh,
            "both": self.both,
        }

matrix = ResponsibilityMatrix()
matrix.assign_gateway("TLS termination")
matrix.assign_gateway("Authentication")
matrix.assign_gateway("Rate limiting")
matrix.assign_gateway("API key validation")
matrix.assign_mesh("Service-to-service mTLS")
matrix.assign_mesh("Traffic splitting within mesh")
matrix.assign_mesh("Retry and timeout between services")
matrix.assign_both("Observability and metrics")
matrix.assign_both("Access logging")
print(f"Gateway: {matrix.gateway}")
print(f"Mesh: {matrix.mesh}")
print(f"Both: {matrix.both}")

Istio Sidecar Injection for Gateway

Configure Istio sidecar injection for gateway pods.

from typing import Dict, Optional

class IstioGatewayConfig:
    def __init__(self, gateway_name: str,
                 namespace: str = "default"):
        self.gateway_name = gateway_name
        self.namespace = namespace
        self.sidecar_injection = True
        self.mtls_mode = "STRICT"
        self.traffic_policies: Dict = {}

    def enable_sidecar(self, enabled: bool = True):
        self.sidecar_injection = enabled

    def set_mtls_mode(self, mode: str = "STRICT"):
        self.mtls_mode = mode

    def add_retry_policy(self, attempts: int = 3,
                         per_try_timeout: str = "2s"):
        self.traffic_policies["retries"] = {
            "attempts": attempts,
            "perTryTimeout": per_try_timeout
        }

    def add_timeout(self, timeout: str = "10s"):
        self.traffic_policies["timeout"] = timeout

    def add_circuit_breaker(self, max_connections: int = 100,
                            max_pending: int = 30,
                            max_retries: int = 3):
        self.traffic_policies["circuitBreaker"] = {
            "maxConnections": max_connections,
            "maxPendingRequests": max_pending,
            "maxRetries": max_retries,
        }

    def generate_vs(self) -> str:
        vs = {
            "apiVersion": "networking.istio.io/v1beta1",
            "kind": "VirtualService",
            "metadata": {
                "name": self.gateway_name,
                "namespace": self.namespace
            },
            "spec": {
                "hosts": [self.gateway_name],
                "gateways": [self.gateway_name],
                "http": [{"route": [{
                    "destination": {
                        "host": self.gateway_name,
                        "port": {"number": 8080}
                    }
                }]}]
            }
        }
        if self.traffic_policies:
            vs["spec"]["http"][0].update(
                self.traffic_policies
            )
        import yaml
        return yaml.dump(vs, default_flow_style=False)

    def generate_peer_auth(self) -> str:
        pa = {
            "apiVersion": "security.istio.io/v1beta1",
            "kind": "PeerAuthentication",
            "metadata": {
                "name": f"{self.gateway_name}-mtls",
                "namespace": self.namespace
            },
            "spec": {
                "mtls": {
                    "mode": self.mtls_mode
                }
            }
        }
        import yaml
        return yaml.dump(pa, default_flow_style=False)

istio = IstioGatewayConfig("api-gateway")
istio.add_retry_policy(attempts=3, per_try_timeout="2s")
istio.add_timeout("10s")
print(istio.generate_vs())
print(istio.generate_peer_auth())

mTLS Between Gateway and Services

Configure mutual TLS between the gateway and backend services.

from typing import Dict, Optional

class MeshMTLSConfig:
    def __init__(self):
        self.services: Dict[str, str] = {}

    def add_service(self, name: str,
                    mtls_mode: str = "STRICT"):
        self.services[name] = mtls_mode

    def get_service_config(self, name: str
                           ) -> Optional[str]:
        return self.services.get(name)

    def validate_gateway_cert(self,
                              cert_pem: str) -> bool:
        return True

    def generate_destination_rule(self,
                                  service: str,
                                  namespace: str = "default"
                                  ) -> str:
        dr = {
            "apiVersion": "networking.istio.io/v1beta1",
            "kind": "DestinationRule",
            "metadata": {
                "name": f"{service}-mtls",
                "namespace": namespace
            },
            "spec": {
                "host": f"{service}.{namespace}.svc.cluster.local",
                "trafficPolicy": {
                    "tls": {
                        "mode": self.services.get(
                            service, "ISTIO_MUTUAL"
                        )
                    }
                }
            }
        }
        import yaml
        return yaml.dump(dr, default_flow_style=False)

mtls = MeshMTLSConfig()
mtls.add_service("scan-service", "ISTIO_MUTUAL")
mtls.add_service("report-service", "ISTIO_MUTUAL")
print(mtls.generate_destination_rule("scan-service"))

Traffic Policies for Canary Routing

Use service mesh traffic policies for canary routing within the cluster.

from typing import Dict, List, Optional

class MeshTrafficSplit:
    def __init__(self, service_name: str,
                 namespace: str = "default"):
        self.service = service_name
        self.namespace = namespace
        self.subsets: List[Dict] = []

    def add_subset(self, version: str,
                   labels: Dict,
                   weight: int):
        self.subsets.append({
            "version": version,
            "labels": labels,
            "weight": weight,
        })

    def generate_vs(self) -> str:
        routes = []
        for subset in self.subsets:
            routes.append({
                "weight": subset["weight"],
                "destination": {
                    "host": self.service,
                    "subset": subset["version"],
                    "port": {"number": 8080}
                }
            })
        vs = {
            "apiVersion": "networking.istio.io/v1beta1",
            "kind": "VirtualService",
            "metadata": {
                "name": self.service,
                "namespace": self.namespace
            },
            "spec": {
                "hosts": [self.service],
                "http": [{
                    "route": routes
                }]
            }
        }
        import yaml
        return yaml.dump(vs, default_flow_style=False)

    def generate_dr(self) -> str:
        subsets = []
        for subset in self.subsets:
            subsets.append({
                "name": subset["version"],
                "labels": subset["labels"]
            })
        dr = {
            "apiVersion": "networking.istio.io/v1beta1",
            "kind": "DestinationRule",
            "metadata": {
                "name": self.service,
                "namespace": self.namespace
            },
            "spec": {
                "host": self.service,
                "subsets": subsets
            }
        }
        import yaml
        return yaml.dump(dr, default_flow_style=False)

split = MeshTrafficSplit("scan-service")
split.add_subset("v1", {"version": "v1"}, 90)
split.add_subset("v2", {"version": "v2"}, 10)
print(split.generate_vs())

Common Mistakes

Mistake 1: Duplicating Gateway and Mesh Features

Both can do retries, timeouts, and auth. Choose one to avoid conflicts and confusion.

Mistake 2: Forgetting Sidecar Injection

Gateway pods without sidecar cannot participate in the mesh. Enable injection for mTLS and Observability.

Mistake 3: mTLS Misconfiguration

If the gateway and mesh have different mTLS settings, requests fail. Ensure consistent TLS configuration.

Mistake 4: Overlapping Rules

Gateway and mesh both rewriting headers or enforcing timeouts causes unpredictable behavior.

Mistake 5: Not Using the Mesh for Internal Traffic

Without the mesh, internal services lose mTLS, retries, and observability benefits.

Practice Questions

  1. What is the difference between north-south and east-west traffic?
  2. How does the API gateway complement the service mesh?
  3. What is sidecar injection and why is it needed?
  4. How does mTLS work between the gateway and mesh services?
  5. What traffic policies should be in the gateway versus the mesh?

Challenge

Build an architecture where an API gateway handles external authentication and Rate Limiting, while an Istio service mesh handles mTLS, retries, and canary traffic splitting between internal services, with clear responsibility boundaries.

FAQ

Do I need both a gateway and a service mesh?

Not always. For simple architectures, a gateway alone is sufficient. Add a service mesh when you need advanced traffic management, mTLS, and observability for internal traffic.

How does the gateway connect to the mesh?

The gateway runs as a pod in the mesh with sidecar injection enabled. Traffic from the gateway to internal services flows through the mesh.

What happens if the gateway does not have a sidecar?

Without a sidecar, the gateway bypasses mesh features like mTLS and traffic policies when communicating with mesh services.

Can the gateway be outside the mesh?

Yes, the gateway can be outside the mesh and communicate with mesh services through the ingress gateway. This is a common pattern for multi-cluster setups.

What is the performance impact of adding a sidecar to the gateway?

Sidecar proxies add 2-5ms of latency per request but enable mTLS, retries, and observability. The benefits typically outweigh the overhead.

Mini Project

Build a combined gateway and service mesh architecture where the gateway handles TLS termination and rate limiting, Istio handles mTLS and retries between services, and the gateway has sidecar injection enabled for mesh participation.

What's Next

Learn about Gateway Kubernetes for container Orchestration, or explore Gateway Clustering for high availability.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro