Istio Gateway API
This post analyzes how Istio implements and operates the Kubernetes Gateway API. The analyzed Istio Version is 1.31, and the Gateway API Version is v1.6.
1. Istio Gateway API
![[Figure 1] Istio Gateway API Architecture](/blog-software/docs/theory-analysis/istio-gateway-api/images/istio-gateway-api.png)
[Figure 1] Istio Gateway API Architecture
Istio provides its own Traffic management APIs, the Gateway and VirtualService Resources, but it also serves as an implementation of the Gateway API, the Kubernetes standard API. Istio plans to transition to the Gateway API as its default Traffic management API in the future, and the Waypoint of the new Ambient Mode also operates based on the Gateway API. Since the CRDs of the Gateway API are not included in Istio, they must be installed separately, and once the CRDs are installed, istiod Watches and processes Gateway API Resources, so no separate Controller installation is required.
[Figure 1] shows the architecture of the Istio Gateway API. istiod’s Gateway API Controller Watches Gateway API Resources such as Gateways and HTTPRoutes created in each Namespace and converts them into internal Istio Gateway and VirtualService configuration, and the converted configuration is gathered into the PushContext together with the Istio CR configuration Watched by the crdclient and the Kubernetes Service and Endpoints information Watched by the Service Registry.
The ConfigGenerator then converts the Gateway configuration gathered in the PushContext into Envoy Listener configuration and the VirtualService configuration into Envoy Route configuration, and the DiscoveryServer delivers the converted configuration via xDS to every Envoy, including the Ingress/Egress Gateways and the Sidecars. The Envoys that receive the configuration handle Traffic as shown in the Traffic flow of [Figure 1]: Traffic coming from outside the Cluster is received by a Gateway and forwarded to the App’s Sidecar, and Traffic between Apps is forwarded directly between Sidecars. Therefore, even when using the Gateway API, the actual Traffic processing behavior is identical to the case of using the Istio API.
istiod’s Gateway Deployment Controller Watches Gateway Resources and is responsible for directly creating the Deployment where Envoy runs and the Service that receives Traffic. As in the gateway Namespace of [Figure 1], creating the gateway-external and gateway-internal Gateways creates the external-gateway and internal-gateway Services and Deployments respectively, which allows multiple Gateways to be placed and used in the required Namespaces.
| GatewayClass | Purpose |
|---|---|
istio | General Gateway that receives Traffic from outside the Cluster |
istio-remote | Gateway that exists in a remote Cluster and is not deployed directly by istiod |
istio-waypoint | Waypoint that handles L7 Traffic in Ambient Mode |
istio-east-west | Gateway that receives inter-Cluster Traffic in the Multi-Cluster setup of Ambient Mode |
[Table 1] shows the types of GatewayClasses provided by Istio. The istio GatewayClass is used for the typical Ingress Gateway purpose, and the remaining GatewayClasses are used in Multi-Cluster setups or Ambient Mode. When installing in Sidecar Mode, only the istio and istio-remote GatewayClasses are created, and the istio-waypoint and istio-east-west GatewayClasses are created when installing in Ambient Mode. The istio-east-west GatewayClass is still an experimental feature.
1.1. Test Environment Setup
![[Figure 2] Test Environment](/blog-software/docs/theory-analysis/istio-gateway-api/images/test-environment.png)
[Figure 2] Test Environment
# Create kind cluster
$ kind create cluster --name istio-gateway-api
# Install gateway api CRDs (v1.6.0 standard channel)
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/standard-install.yaml
# Install istio (minimal profile, sidecar mode)
$ istioctl install --set profile=minimal -y$ kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
istio istio.io/gateway-controller True 5s
istio-remote istio.io/unmanaged-gateway True 5sThe behavior checks in the rest of this post are performed by installing the Gateway API v1.6.0 Standard Channel CRDs and Istio 1.31.0 with the minimal Profile in Sidecar Mode on a kind Cluster, as shown in [Shell 1]. Since the GatewayClasses are created together when istiod is installed, the GatewayClass list in [Shell 2] confirms that Istio has been installed correctly as a Gateway API implementation. The istio and istio-remote GatewayClasses have been created, and since Ambient Mode was not installed, the istio-waypoint GatewayClass does not exist.
| |
$ kubectl -n server-namespace get pods,services
NAME READY STATUS RESTARTS AGE
pod/client 2/2 Running 0 3h20m
pod/server-v1-7cf5688dfc-rcw7t 2/2 Running 0 3h20m
pod/server-v2-69bf76f867-htddz 2/2 Running 0 3h20m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/server ClusterIP 10.96.4.246 <none> 8080/TCP 3h20m
service/server-v1 ClusterIP 10.96.255.26 <none> 8080/TCP 3h20m
service/server-v2 ClusterIP 10.96.153.106 <none> 8080/TCP 3h20mThe Test Workloads consist of the server-v1 and server-v2 Deployments and Services that respond with their own names, the server Service that selects the Pods of both Deployments, and the client Pod that sends requests, as shown in [File 1]. Applying [File 1] creates the Test Workloads in the server-namespace Namespace as shown in [Shell 3]. Since the istio-injection Label is set on the server-namespace Namespace, the READY of every Pod is 2/2, confirming that the Sidecar has been injected. The Gateway is created in the gateway-namespace Namespace.
[Figure 2] shows the constructed Test environment. The gateway-namespace Namespace is still empty as the place where the Gateway will be deployed, and the Ingress Gateway in the istio-system Namespace is drawn with a dashed line because it is deployed directly later in the process of checking the approach of using an existing Ingress Gateway. The curl Client outside the Cluster sends requests to the Test Workloads through the Gateway.
1.2. Ingress Gateway Setup
1.2.1. Deployment via Gateway CR
![[Figure 3] Deployment via Gateway CR](/blog-software/docs/theory-analysis/istio-gateway-api/images/gateway-cr.png)
[Figure 3] Deployment via Gateway CR
| |
The Gateway Resource of the Istio API defines only the configuration of an already deployed Ingress Gateway, so the Ingress Gateway must be deployed separately through a Helm Chart or IstioOperator, whereas the Gateway Resource of the Gateway API is responsible not only for configuration but also for deployment. When a Gateway using the istio GatewayClass is created as shown in [File 2], istiod automatically creates a Deployment and Service whose names take the form [Gateway name]-[GatewayClass name], as shown in [Figure 3]. Envoy runs in the Pods of the created Deployment and receives its configuration from istiod via xDS. The Type of the Service is set to LoadBalancer by default and can be changed through the networking.istio.io/service-type Annotation.
| |
The automatically deployed Deployment and Service can be Customized through the infrastructure setting of the Gateway. Values specified in labels and annotations of infrastructure are propagated as-is to the created Resources, and a ConfigMap like [File 3] can be specified in parametersRef. The ConfigMap can define the deployment, service, serviceAccount, horizontalPodAutoscaler, and podDisruptionBudget Keys, and the contents of each Key are reflected in the created Resources in the Strategic Merge Patch manner. Cluster-wide defaults can be set per GatewayClass through a ConfigMap with the gateway.istio.io/defaults-for-class Label in the istio-system Namespace.
$ kubectl -n gateway-namespace get gateway,deployment,service
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/gateway istio gateway-istio.gateway-namespace.svc.cluster.local True 25s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/gateway-istio 3/3 3 3 25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/gateway-istio ClusterIP 10.96.214.92 <none> 15021/TCP,80/TCP 25s
$ kubectl -n gateway-namespace get deployment gateway-istio -o jsonpath='{.spec.template.spec.containers[0].resources.requests}'
{"cpu":"500m","memory":"512Mi"}Applying the Gateway of [File 2] and the ConfigMap of [File 3] shows that the gateway-istio Deployment and Service are automatically created, as can be seen in [Shell 4]. According to the contents of the ConfigMap, the Deployment has 3 Replicas, the resources of the istio-proxy Container are set to the specified values, and the Service Type is created as ClusterIP, confirming that the Customization through parametersRef has been reflected. Since no LoadBalancer exists in a kind Cluster, the Service Type was changed to ClusterIP, and the ADDRESS of the Gateway is set to the Domain address of the created Service.
1.2.2. Using an Existing Ingress Gateway
![[Figure 4] Using an Existing Ingress Gateway](/blog-software/docs/theory-analysis/istio-gateway-api/images/gateway-ingress.png)
[Figure 4] Using an Existing Ingress Gateway
| |
Instead of using automated deployment, it is also possible to apply only the Gateway API configuration to an already deployed Ingress Gateway. [File 4] shows an example of deploying an Ingress Gateway directly in the Gateway Injection manner, where istiod’s Injection Webhook creates the istio-proxy Container as a Gateway-purpose Proxy according to the sidecar.istio.io/inject Label and the inject.istio.io/templates Annotation set on the Pod. The gateway.networking.k8s.io/gateway-name Label specifies the name of the Gateway to be attached, and the Label must be set for the Routes and Policies attached to the Gateway to be applied correctly.
| |
$ kubectl -n istio-system get gateway,deployment,service
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/gateway-ingress istio istio-ingressgateway.istio-system.svc.cluster.local True 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/istio-ingressgateway 1/1 1 1 16m
deployment.apps/istiod 1/1 1 1 16d
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-ingressgateway ClusterIP 10.96.230.0 <none> 80/TCP 16m
service/istiod ClusterIP 10.96.229.61 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 16d
service/istiod-revision-tag-default ClusterIP 10.96.145.230 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 16dWhen the name of the Ingress Gateway Service is specified in the Gateway’s addresses with the Hostname Type as shown in [File 5], istiod does not create a Deployment and Service and only delivers the Listener configuration to the Ingress Gateway of the specified Service, as shown in [Figure 4]. Applying [File 4] and [File 5] shows in [Shell 5] that no Deployment and Service in the form of gateway-istio are created and that the ADDRESS of the Gateway is set to the address of the existing Ingress Gateway Service specified in addresses. The approach of using an existing Ingress Gateway is used when the deployment of the Ingress Gateway must be controlled directly, and the deployment approach via the Gateway CR is generally recommended.
1.3. Gateway Traffic Control
istiod’s Gateway API Controller converts Gateway API Resources into internal configuration of the same form as the Istio API. The Gateway Resource is converted into Istio’s Gateway configuration, and the HTTPRoute, GRPCRoute, TLSRoute, and TCPRoute Resources are converted into VirtualService configuration. The converted configuration exists only in istiod’s Memory and is not stored in Kubernetes, so it cannot be queried through kubectl, and the final configuration delivered to Envoy can be checked through the istioctl proxy-config command.
Among the Routes of the Gateway API, Istio supports HTTPRoute, GRPCRoute, TLSRoute, and TCPRoute, and since Envoy-based Istio does not provide a UDP Proxy feature, UDPRoute is not supported. Also, since the Gateway API does not yet provide all of Istio’s features as standards, the Istio API must be used together when features like Fault Injection or Circuit Breaking are needed. DestinationRule is applied based on the Host, so it can be used together with the Routes of the Gateway API.
1.3.1. Attaching to the Gateway CR
![[Figure 5] Traffic Distribution of the HTTPRoute Attached to the Gateway CR](/blog-software/docs/theory-analysis/istio-gateway-api/images/config-cr.png)
[Figure 5] Traffic Distribution of the HTTPRoute Attached to the Gateway CR
| |
# Port-forward to the gateway service
$ kubectl -n gateway-namespace port-forward svc/gateway-istio 8080:80 &
# Send 100 requests
$ for i in $(seq 1 100); do curl -s -H "Host: server.ssup2.com" http://127.0.0.1:8080/; done | sort | uniq -c
93 server-v1
7 server-v2[File 6] shows an example of an HTTPRoute that specifies the Gateway of [File 2] in parentRefs and distributes the Traffic received by the Gateway to the server-v1 Service at a 90% ratio and the server-v2 Service at a 10% ratio, as shown in [Figure 5]. After applying the HTTPRoute, sending 100 requests as shown in [Shell 6] results in 93 responses from server-v1 and 7 responses from server-v2, confirming that the distribution follows a ratio close to the weight setting. Since no LoadBalancer exists in a kind Cluster, the requests were sent through port-forward.
# No VirtualService is stored in kubernetes
$ kubectl get virtualservice -A
No resources found
# Check the converted route config of the gateway envoy
$ istioctl proxy-config routes gateway-istio-6cf9dd97dd-8lrn4 -n gateway-namespace --name http.80 -o json
[
{
"name": "http.80",
"virtualHosts": [
{
"name": "server.ssup2.com:80",
"domains": [
"server.ssup2.com"
],
"routes": [
{
"name": "server-namespace.server.0",
...
"route": {
"weightedClusters": {
"clusters": [
{
"name": "outbound|8080||server-v1.server-namespace.svc.cluster.local",
"weight": 90
},
{
"name": "outbound|8080||server-v2.server-namespace.svc.cluster.local",
"weight": 10
}
]
},
...
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1/namespaces/server-namespace/virtual-service/gateway-namespace~gateway~istio-autogenerated-k8s-gateway~http~server.ssup2.com"
}
}
},
...As shown in [Shell 7], even after applying the HTTPRoute, no VirtualService is stored in Kubernetes, but the contents of the HTTPRoute have been converted into weightedClusters and reflected in the Route configuration of the Gateway Envoy. The metadata of the Route specifies a VirtualService path containing the name istio-autogenerated-k8s-gateway, which confirms that istiod converts the HTTPRoute into an in-Memory VirtualService for processing.
1.3.2. Attaching to an Existing Ingress Gateway
![[Figure 6] Traffic Distribution of the HTTPRoute Attached to an Existing Ingress Gateway](/blog-software/docs/theory-analysis/istio-gateway-api/images/config-ingress.png)
[Figure 6] Traffic Distribution of the HTTPRoute Attached to an Existing Ingress Gateway
| |
Even when using an existing Ingress Gateway, the conversion process of the HTTPRoute is the same, and only the target to which the converted configuration is delivered differs. [File 7] shows an example of the server-ingress HTTPRoute that specifies the Gateway of [File 5] in parentRefs to apply the same distribution rules as [File 6] to the existing Ingress Gateway. As shown in [Figure 6], istiod delivers the converted configuration not to an automatically deployed Gateway but to the Envoy of the existing Ingress Gateway with the gateway.networking.k8s.io/gateway-name Label set.
# Check the converted route config of the existing ingress gateway envoy
$ istioctl proxy-config routes istio-ingressgateway-585694fd7c-kf6qj -n istio-system --name http.80 -o json
[
{
"name": "http.80",
"virtualHosts": [
{
"name": "server.ssup2.com:80",
"domains": [
"server.ssup2.com"
],
"routes": [
{
"name": "server-namespace.server-ingress.0",
...
"route": {
"weightedClusters": {
"clusters": [
{
"name": "outbound|8080||server-v1.server-namespace.svc.cluster.local",
"weight": 90
},
{
"name": "outbound|8080||server-v2.server-namespace.svc.cluster.local",
"weight": 10
}
]
},
...
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1/namespaces/server-namespace/virtual-service/istio-system~gateway-ingress~istio-autogenerated-k8s-gateway~http~server.ssup2.com"
}
}
},
...As shown in [Shell 8], after applying the HTTPRoute, the contents of the HTTPRoute have also been converted into weightedClusters and reflected in the Route configuration of the Envoy of the existing Ingress Gateway. Unlike [Shell 7], the VirtualService path specified in the metadata of the Route contains the istio-system Namespace where the Gateway of [File 5] exists and the Gateway name, which shows that the HTTPRoute has been converted into the configuration of the Gateway using the existing Ingress Gateway.
1.4. Mesh Traffic Control
![[Figure 7] Mesh Traffic Distribution of the HTTPRoute Attached to a Service](/blog-software/docs/theory-analysis/istio-gateway-api/images/config-gamma.png)
[Figure 7] Mesh Traffic Distribution of the HTTPRoute Attached to a Service
| |
Through GAMMA (Gateway API for Mesh Management and Administration), the Gateway API can be used not only for controlling North-South Traffic coming from outside the Cluster but also for controlling East-West Traffic inside the Mesh. [File 8] shows an example of an HTTPRoute that specifies a Service instead of a Gateway in parentRefs, distributing the Traffic sent to the server Service inside the Mesh to the server-v1 and server-v2 Services. As shown in [Figure 7], in Sidecar Mode the Routing rules are applied in the Sidecar of the Client sending the request, which performs the same role as applying a VirtualService to the mesh Gateway.
# Before applying the mesh httproute
$ kubectl -n server-namespace exec client -c curl -- sh -c 'for i in $(seq 1 100); do curl -s http://server:8080/; done' | sort | uniq -c
55 server-v1
45 server-v2
# After applying the mesh httproute
$ kubectl -n server-namespace exec client -c curl -- sh -c 'for i in $(seq 1 100); do curl -s http://server:8080/; done' | sort | uniq -c
90 server-v1
10 server-v2[Shell 9] shows the results of sending 100 requests from the client Pod to the server Service before and after applying the HTTPRoute of [File 8]. Before applying, since there are no Routing rules, the requests are distributed at roughly a 50:50 ratio to the server-v1 and server-v2 Pods, which are the Endpoints of the server Service, but after applying, it can be confirmed that the requests are distributed at a 90:10 ratio to the server-v1 and server-v2 Services according to the weight setting of the HTTPRoute.
$ istioctl proxy-config routes client -n server-namespace --name 8080 -o json
...
{
"name": "server.server-namespace.svc.cluster.local:8080",
"domains": [
"server.server-namespace.svc.cluster.local",
...
],
"routes": [
{
"name": "server-namespace.server-mesh.0",
...
"route": {
"weightedClusters": {
"clusters": [
{
"name": "outbound|8080||server-v1.server-namespace.svc.cluster.local",
"weight": 90
},
{
"name": "outbound|8080||server-v2.server-namespace.svc.cluster.local",
"weight": 10
}
]
},
...As shown in [Shell 10], it can be confirmed that the Routing rules are reflected in the Sidecar Route configuration of the client Pod that sends the requests, which shows that the rules for Mesh Traffic are applied not at the Gateway but at the Client’s Sidecar.
1.5. Comparison with the Istio API
| Category | Istio API | Gateway API |
|---|---|---|
| Resource Composition | Gateway, VirtualService, DestinationRule | GatewayClass, Gateway, Route |
| Gateway Role | Defines only the configuration of a deployed Ingress Gateway | Handles both Gateway configuration and deployment |
| Protocol Handling | Defines HTTP, TLS, TCP in a single VirtualService | Separate Route Resources per Protocol |
| Role Separation | Limited | Separation of Cluster Operators and App developers |
| Feature Scope | All Istio features | Focused on standard features |
[Table 2] shows the main differences between the Istio API and the Gateway API. The Istio API allows the use of all of Istio’s features, but since it is an Istio-specific API, it cannot be ported to other implementations, whereas the Gateway API is a standard API with high portability but cannot use all of Istio’s features. Since Istio supports both APIs, they can be mixed in a single Cluster, but from a management perspective it is recommended to use only one API per Ingress Gateway.
2. References
- Istio Kubernetes Gateway API : https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/
- Istio Gateway Deployment : https://istio.io/latest/docs/setup/additional-setup/gateway/
- Gateway API : https://gateway-api.sigs.k8s.io/
- Gateway API GAMMA : https://gateway-api.sigs.k8s.io/docs/mesh/mesh-overview/
- Istio Gateway API Conversion : https://deepwiki.com/istio/istio/3.5.1-gateway-api-integration-and-conversion