> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Gateways

> Configuring an Istio Ingress Gateway to expose the Bookinfo app including VirtualService and Gateway setup, sidecar injection verification, testing, and troubleshooting

In this lesson you'll configure an Istio Ingress Gateway to expose the Bookinfo application. The steps covered:

* Verify Istio is installed and sidecar injection is enabled on the namespace.
* Deploy Bookinfo and confirm sidecar injection on pods.
* Create an internal-only VirtualService.
* Create a Gateway that selects the Istio ingress dataplane.
* Update the VirtualService to bind to the Gateway and test via curl.
* Explain differences between cloud clusters (LoadBalancer) and kubeadm-style labs (NodePort).

Relevant resources

* [Istio Service Mesh course — KodeKloud](https://learn.kodekloud.com/user/courses/istio-service-mesh)
* [Kubernetes Documentation](https://kubernetes.io/docs/)

***

## 1. Verify Istio installation and namespace injection

1. Check Istio control-plane / system pods:

```bash theme={null}
kubectl get pods -n istio-system
```

Example output:

```text theme={null}
NAME                           READY   STATUS    RESTARTS   AGE
istio-egress-cfcd9bc96-9yncl   1/1     Running   0          4m32s
istio-ingress-6cf77d4858-g2cvz 1/1     Running   0          4m31s
istiod-5fcb7d9676-j9vr9        1/1     Running   0          4m42s
```

2. Confirm sidecar injection is enabled for the `default` namespace:

```bash theme={null}
kubectl get ns --show-labels
```

Example output:

```text theme={null}
NAME               STATUS   AGE     LABELS
default            Active   5m29s   istio-injection=enabled,kubernetes.io/metadata.name=default
istio-system       Active   4m51s   kubernetes.io/metadata.name=istio-system
kube-node-lease    Active   5m29s   kubernetes.io/metadata.name=kube-node-lease
kube-public        Active   5m29s   kubernetes.io/metadata.name=kube-public
kube-system        Active   5m29s   kubernetes.io/metadata.name=kube-system
```

Tip: If `istio-injection=enabled` is not present, enable injection with:

```bash theme={null}
kubectl label namespace default istio-injection=enabled --overwrite
```

***

## 2. Deploy Bookinfo and confirm sidecar injection

Deploy Bookinfo using your usual method (the YAMLs are omitted here). After deploying, verify the pods show `2/2` (application + Envoy sidecar) where sidecar injection applies:

```bash theme={null}
kubectl get pods
```

Example output:

```text theme={null}
NAME                               READY   STATUS    RESTARTS   AGE
details-v1-65599dcf88-m7qpm        2/2     Running   0          4s
productpage-v1-9487c9c5b-qk955     1/2     Running   0          4s
ratings-v1-59b99c644-2dgfs         2/2     Running   0          4s
reviews-v1-5985998544-ntzxh        2/2     Running   0          4s
reviews-v2-86d6cc668-qgqmj         2/2     Running   0          4s
reviews-v3-dbb5fb5dd-p78hv         1/2     Running   0          4s
```

If some pods show `1/2`, check pod events and sidecar injection policies for the namespace.

***

## 3. Create an internal-only VirtualService

Start by creating a VirtualService that routes traffic for the `productpage` service internally. Save this manifest as `vs.yaml`:

```yaml theme={null}
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: book-info-vs
spec:
  hosts:
  - productpage
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: productpage.default.svc.cluster.local
        port:
          number: 9080
```

Apply it:

```bash theme={null}
kubectl apply -f vs.yaml
```

Expected feedback:

```text theme={null}
virtualservice.networking.istio.io/book-info-vs created
```

At this stage the VirtualService is internal-only: it matches the `productpage` service host but is not yet exposed through a Gateway.

***

## 4. Locate the ingress dataplane label for Gateway selector

Istio Gateways select the ingress dataplane (ingress pod) using pod labels. Inspect the ingress pod(s) in `istio-system` to find the label to use as the Gateway selector:

```bash theme={null}
kubectl get pods -n istio-system
kubectl describe pod -n istio-system istio-ingress-6cf77d4858-g2cvz
```

Look for the `Labels:` block. Example:

```text theme={null}
Labels:
  app=istio-ingress
  istio=ingress
  sidecar.istio.io/inject=true
  ...
```

Note the label (for example `istio=ingress` or `app=istio-ingress`) — you will use this value under `spec.selector` in the Gateway manifest.

***

## 5. Create an Istio Gateway

Create a Gateway manifest that selects the ingress dataplane label and listens on port 80. Save as `gw.yaml` in the same namespace as your VirtualService (here we use `default`):

```yaml theme={null}
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-gateway
spec:
  selector:
    istio: ingress
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "book.info.com"
```

Apply it:

```bash theme={null}
kubectl apply -f gw.yaml
```

Verify Gateway and VirtualService presence:

```bash theme={null}
kubectl get gateways.networking.istio.io
kubectl get virtualservices.networking.istio.io
```

Example outputs:

```text theme={null}
# Gateways
NAME           AGE
istio-gateway  10s

# VirtualServices
NAME           GATEWAYS   HOSTS             AGE
book-info-vs               ["productpage"]   30s
```

At this point the Gateway resource exists, but the VirtualService is still not bound to the Gateway and still only matches the internal `productpage` host.

***

## 6. Check Istio ingress Service type (Cluster behavior)

Istio's ingress Service type varies by environment:

```bash theme={null}
kubectl get svc -n istio-system
```

Example (kubeadm lab, NodePort):

```text theme={null}
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)
istio-ingress   NodePort    10.97.88.127     <none>        15021:31817/TCP,80:30992/TCP,443:30171/TCP
```

Example (cloud-managed cluster, LoadBalancer):

```text theme={null}
NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP
istio-ingressgateway LoadBalancer   10.100.188.237   abb6be537f8134cc084b21378582f75-477874693.us-east-1.elb.amazonaws.com
```

Table: Service types and how to reach the ingress

| Service Type   | How to target for curl / external tests                                                                                                                      |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `LoadBalancer` | Use the external IP/hostname in requests (no Host header required if DNS points to it).                                                                      |
| `NodePort`     | Use `nodeIP:nodePort` (or node IP + NodePort). When targeting a NodePort for a Gateway with port 80 mapped to a NodePort, use the node IP and that NodePort. |
| `ClusterIP`    | Must curl from inside the cluster (e.g., using a temporary `curl` pod) and target the ClusterIP.                                                             |

***

## 7. Test the Gateway (initial behavior)

If you test now without updating the VirtualService, you may get a 404. Example (curl from inside the cluster to the ClusterIP):

```bash theme={null}
curl --head --header "Host: book.info.com" http://10.97.88.127
```

Possible response:

```text theme={null}
HTTP/1.1 404 Not Found
date: Fri, 11 Apr 2025 21:33:08 GMT
server: istio-envoy
transfer-encoding: chunked
```

Why 404? The VirtualService still matches only the internal host `productpage`. The Gateway receives traffic for `book.info.com`, but no VirtualService is bound to that host + gateway combination yet.

***

## 8. Update the VirtualService to bind to the Gateway

Edit `vs.yaml` to include the external host and reference the Gateway. The updated manifest:

```yaml theme={null}
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: book-info-vs
spec:
  hosts:
  - "book.info.com"
  - productpage
  gateways:
  - istio-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: productpage.default.svc.cluster.local
        port:
          number: 9080
```

Apply the change:

```bash theme={null}
kubectl apply -f vs.yaml
```

Expected feedback:

```text theme={null}
virtualservice.networking.istio.io/book-info-vs configured
```

***

## 9. Re-test and validate a 200 response

Target the ingress address that is reachable from where you run curl. If you don't have DNS for `book.info.com`, continue using the Host header:

Example (from inside the cluster using the ClusterIP):

```bash theme={null}
curl --head --header "Host: book.info.com" http://10.97.88.127
```

You should now receive a 200:

```text theme={null}
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 1683
server: istio-envoy
date: Fri, 11 Apr 2025 21:35:57 GMT
x-envoy-upstream-service-time: 21
```

If you have DNS pointing `book.info.com` to the external LoadBalancer hostname/IP, you can omit the Host header and call the DNS name directly.

<Callout icon="lightbulb" color="#1CB2FE">
  Testing without a DNS record: pass the Host header to the ingress IP to emulate requests for the hostname (for example, `Host: book.info.com`). On cloud clusters the ingress Service typically becomes a LoadBalancer with an external hostname—update DNS to point your host (e.g., `book.info.com`) to that external IP/hostname.
</Callout>

***

## 10. Quick troubleshooting checklist

* Confirm the Gateway `spec.selector` matches an ingress pod label (e.g., `istio=ingress`).
* Ensure the VirtualService lists the external host (e.g., `book.info.com`) under `spec.hosts`.
* Bind the VirtualService to the Gateway by adding `spec.gateways: - istio-gateway`.
* Verify the ingress Service type (`LoadBalancer` vs `NodePort` vs `ClusterIP`) and use an accessible address for curl tests.
* Inspect Envoy logs on the ingress pod for routing errors:
  ```bash theme={null}
  kubectl logs -n istio-system <ingress-pod> -c istio-proxy
  ```

***

## Summary

* Create a Gateway whose `selector` matches the ingress dataplane label (e.g., `istio=ingress`).
* Create or update a VirtualService to include the external host(s) and a reference to the Gateway.
* Test using curl with a `Host` header when DNS is not configured, making sure you target an ingress address reachable from your client.
* On cloud-managed clusters, Istio commonly provisions a `LoadBalancer` for the ingress Service; on kubeadm lab environments you may see `NodePort` instead.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/f3f5ca4b-b8d6-4788-9553-9ed765709933/lesson/0489a47d-305a-4e60-8a03-5742192bc357" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/istio-certified-associate/module/f3f5ca4b-b8d6-4788-9553-9ed765709933/lesson/8a471175-53e2-4d14-95ce-aa3721c7ef20" />
</CardGroup>
