Overview
For a long time, Ingress was the primary way to receive external traffic in Kubernetes, but Gateway API is now becoming the de facto standard. Istio can act as a Gateway API controller, so Gateway/HTTPRoute alone can define the "domain → routing → service" flow.
This post provides a reproducible checklist for verifying that applications communicate correctly through Gateway API in an Istio-based environment. (The same structure can be used for validation in both Sidecar and Ambient modes.)
Goals (Success Criteria)
HTTPRoutehas the statusAccepted=True,ResolvedRefs=True(in Ambient mode,ResolvedWaypoints=Truemay also appear)- Accessing the LoadBalancer address externally returns a response header containing
server: istio-envoy(= passed through the Istio Gateway) - A response is received from the final backend (such as the sample nginx)
Overall Structure (Concept)
Gateway API describes the "routing intent (specification)," while Istio handles the actual implementation (controller). It is therefore normal to see istio-envoy in the response header when successful.
Read the diagram as text
- Gateway(gateway, istio-system)가 gatewayClassName: istio로 GatewayClass istio를 참조한다.
- Namespace demo의 라벨 shared-gateway-access=true가 Gateway allowedRoutes selector와 맞아야 route attach가 허용된다.
- HTTPRoute demo가 parentRefs(gateway, istio-system, sectionName: http)로 Gateway에 연결된다.
- HTTPRoute가 hostnames app.example.com, PathPrefix /, backendRefs demo:80을 정한다.
- Gateway API는 라우팅 의도이고, Istio가 이 설정을 Istio Gateway Envoy로 구현한다.
- 요청: Client가 Host: app.example.com으로 LoadBalancer에 보낸다.
- LoadBalancer에서 Istio Gateway Envoy로 간다.
- Envoy가 설정된 backend인 Service demo:80으로 보낸다.
- Service가 app: demo 라벨로 Pod(nginx)를 선택한다.
1) Prerequisites
- A Kubernetes cluster
- Istio installed (Sidecar or Ambient)
- Gateway API CRDs installed
- An environment that can create a
type=LoadBalancer(a cloud LB, on-premises MetalLB, etc.) - DNS configuration permissions for domain testing (Route 53 or any other DNS service)
The examples below include common issues in AWS environments (NLB). In other environments, adjust only the LoadBalancer/firewall sections as needed.
2) Install Gateway API CRDs
Gateway API requires CRDs first. (Pinning the version is recommended in production.)
- Follow the
standard-install.yamlinstallation flow in the official Gateway API documentation, or - Skip this step if they are already installed (for example, through a managed add-on or the platform's default setup).
3) Install Istio (Sidecar / Ambient)
Sidecar Mode (Typical Setup)
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update
helm upgrade --install istio-base istio/base -n istio-system --create-namespace
helm upgrade --install istiod istio/istiod -n istio-system
Ambient Mode (Additional Components Required)
Ambient centers on ztunnel/waypoint rather than sidecars, so it requires more components.
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update
helm upgrade --install istio-base istio/base -n istio-system --create-namespace
helm upgrade --install istiod istio/istiod -n istio-system --set profile=ambient
helm upgrade --install istio-cni istio/cni -n istio-system --set profile=ambient
helm upgrade --install ztunnel istio/ztunnel -n istio-system
4) Create a (Shared) Gateway
In production, it is usually easier to manage one shared Gateway for the team, with each service adding only an HTTPRoute.
Here is an example.
- For
*.example.com, open a listener (http) - Restrict
allowedRouteswith a namespace label selector.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-system
annotations:
# (AWS 예시) internet-facing
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
After creation, first check that Istio is actually controlling Gateway API, as shown below. This makes debugging faster.
kubectl get gatewayclass
kubectl -n istio-system get gateway gateway -o wide
5) Prepare the Application Namespace and Workload
Common Setup (Label Allowing Route Attachment)
Because the Gateway restricts allowedRoutes with a selector, label the namespace accordingly.
kubectl create ns demo
kubectl label ns demo shared-gateway-access=true --overwrite
Additional Setup for Ambient (Ambient Opt-In)
Ambient requires namespace opt-in.
kubectl label ns demo istio.io/dataplane-mode=ambient --overwrite
6) Deploy the Sample Application (nginx)
The goal is to verify "Gateway → HTTPRoute → Service," so keep the backend as simple as possible.
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: demo
namespace: demo
spec:
selector:
app: demo
ports:
- name: http
port: 80
targetPort: 80
kubectl apply -f demo.yaml
kubectl -n demo rollout status deploy/demo
7) Connect the Service with HTTPRoute
An HTTPRoute defines which service receives requests for a given Host/Path.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: demo
namespace: demo
spec:
parentRefs:
- name: gateway
namespace: istio-system
sectionName: http
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: demo
port: 80
8) DNS and Access Tests
8-1) Check HTTPRoute Status
kubectl -n demo get httproute demo -o wide
kubectl -n demo describe httproute demo
If Accepted=True and ResolvedRefs=True (and ResolvedWaypoints=True in Ambient mode) are confirmed here, the routing configuration can be considered successfully attached.
8-2) Check the Gateway Address (LoadBalancer DNS)
Usually, you can check it with one of the following two methods.
kubectl -n istio-system get gateway gateway -o jsonpath='{.status.addresses[0].value}{"\n"}'
Or from the LoadBalancer Service created by Istio:
kubectl -n istio-system get svc gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}'
Immediately after an NLB is created, incomplete DNS propagation may briefly cause
Could not resolve host. Waiting a few minutes and retrying often resolves it.
8-3) Verify Host-Based Routing with curl
export LB_DNS="<위에서 확인한 값>"
curl -i -H "Host: app.example.com" "http://${LB_DNS}/"
If working correctly:
HTTP/1.1 200 OKserver: istio-envoy- A welcome page (for the sample nginx)
8-4) (Optional) Access Through a Real Domain
The curl -H "Host: ..." approach above is useful for checking whether Host routing works without DNS.
To use a real domain such as app.example.com in production, configure DNS as follows.
- If the LoadBalancer provides a DNS name:
CNAMEis usually the most straightforward choice - If the LoadBalancer provides an IP: An
Arecord
Example (CNAME):
app.example.com. CNAME <LB_DNS>
9) Troubleshooting Checklist
A. HTTPRoute Accepted=False
The most common cause is a mismatch in the allowedRoutes conditions.
- Check the namespace label:
shared-gateway-access=true - In
kubectl -n demo get httproute demo -o yaml, checkreason/message
B. curl Times Out
This is common with AWS (NLB) setups.
type=LoadBalancermay forward traffic to the node's NodePort (30000-32767)- NLB often preserves the client's Source IP. If the worker node firewall/security group blocks inbound NodePort traffic, a timeout occurs.
- In production, restrict access to the actual allowed CIDRs rather than 0.0.0.0/0.
C. Could not resolve host
- DNS propagation delay immediately after NLB provisioning
- Local/corporate DNS environment issues
Closing Notes
- Gateway API lets you configure ingress routing with a clearer structure than Ingress.
- The Gateway(API) → HTTPRoute → Service → Pod flow itself is largely the same in Sidecar and Ambient modes.
- Ambient also requires
istio.io/dataplane-mode=ambientopt-in, and additional status entries such asResolvedWaypointsmay appear.
Next steps:
- Configure an HTTPS(443) listener and verify the certificate (SNI)
- Apply AuthorizationPolicy / L7 policies (including a waypoint strategy in ambient mode)
- Organize observability (metrics/logs/tracing)