1-ServiceMesh
istio
Istiogateway Nlb

Setting up Istio Gateway with AWS NLB (TLS terminated at NLB)

This guide demonstrates how to deploy a simple Nginx application in Kubernetes, enable Istio service mesh, and expose it externally using an Istio Gateway behind an AWS NLB with ACM TLS certificate.


1️⃣ Prerequisites

  • Kubernetes cluster running in AWS (EKS recommended).
  • Istio CLI installed (istioctl).
  • AWS CLI configured with appropriate IAM permissions for creating NLB and attaching ACM certificate.
  • ACM certificate already issued in the region for your domain.

2️⃣ Step 1: Create Namespaces

kubectl create namespace istio-system
kubectl create namespace demo

Enable Istio sidecar injection for demo namespace:

kubectl label namespace demo istio-injection=enabled --overwrite

3️⃣ Step 2: Install Istio

istioctl install --set profile=demo -y

Check that Istio ingressgateway pods are running:

kubectl get pods -n istio-system

4️⃣ Step 3: Deploy Nginx Application

nginx-istio.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: demo
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: nginx

Apply:

kubectl apply -f nginx-istio.yaml
kubectl get pods -n demo
kubectl get svc -n demo

5️⃣ Step 4: Create Istio Gateway

istioG-https.yaml:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: demo-gateway
  namespace: demo
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"

Apply:

kubectl apply -f istioG-https.yaml

6️⃣ Step 5: Create VirtualService

istio-VS.yaml:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: nginx-vs
  namespace: demo
spec:
  hosts:
    - "*"
  gateways:
    - demo-gateway
  http:
    - route:
        - destination:
            host: nginx.demo.svc.cluster.local
            port:
              number: 80

Apply:

kubectl apply -f istio-VS.yaml

7️⃣ Step 6: Update Istio IngressGateway Service with NLB

istioG-nlb.yaml:

apiVersion: v1
kind: Service
metadata:
  name: istio-ingressgateway
  namespace: istio-system
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:ap-south-1:certificate/xxx"
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
spec:
  type: LoadBalancer
  selector:
    istio: ingressgateway
  ports:
    - name: http2
      port: 80
      targetPort: 8080
    - name: https
      port: 443
      targetPort: 8080

Apply:

kubectl apply -f istioG-nlb.yaml
kubectl get svc istio-ingressgateway -n istio-system
  • Note: NLB terminates TLS on 443, forwards HTTP to Istio ingressgateway port 8080.
  • Target group health check should be port 8080, path /healthz/ready.

8️⃣ Step 7: Test Locally

Port-forward to test HTTP routing:

kubectl port-forward svc/istio-ingressgateway -n istio-system 8080:80
curl http://localhost:8080
  • Should return Nginx welcome page.
  • Do NOT use HTTPS locally — TLS is terminated at NLB only.

9️⃣ Step 8: Test via NLB

  • Copy the NLB DNS from kubectl get svc istio-ingressgateway -n istio-system.
  • Open browser:
https://<NLB-DNS>
  • Should show the Nginx page via HTTPS.

10️⃣ Common Issues and Fixes

SymptomCauseFix
HTTP ERROR 503Istio Gateway cannot routeCheck pods running in demo namespace, VirtualService hosts, and sidecar injection
ERR_SSL_PROTOCOL_ERROR on localhostTesting HTTPS locallyUse HTTP on localhost (8080) — NLB does TLS termination
NLB targets unhealthyIncorrect target port or pathTarget group should check port 8080, path /healthz/ready

11️⃣ Summary of Files

FilePurpose
nginx-istio.yamlNginx deployment + service
istioG-https.yamlIstio Gateway listening on HTTP
istio-VS.yamlVirtualService routing traffic to Nginx
istioG-nlb.yamlIstio ingressgateway Service with NLB + ACM TLS

✅ Traffic Flow Diagram

Client (HTTPS) 
     |
     v
AWS NLB (TLS terminated, port 443)
     |
     v
Istio IngressGateway (HTTP 8080 inside cluster)
     |
     v
Istio VirtualService → Nginx Service (port 80)
     |
     v
Nginx Pod
  • NLB handles HTTPS.
  • Istio Gateway handles routing, retries, etc.
  • Nginx serves the application.

This setup is production-ready for TLS termination at NLB only while letting Istio manage all routing/traffic features internally.



💬 Need a Quick Summary?

Hey! Don't have time to read everything? I get it. 😊
Click below and I'll give you the main points and what matters most on this page.
Takes about 5 seconds • Uses Perplexity AI