> ## 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 2 Service Entries

> Guide to add external workloads to an Istio service mesh using ServiceEntry and VirtualService while ensuring Envoy sidecar intercepts outbound traffic

This guide shows how to bring an external workload into an Istio service mesh using a ServiceEntry and a VirtualService — without changing the mesh-wide outbound policy. This pattern is useful when the external workload is outside the Kubernetes cluster (for example, an internal EC2 instance or an on-premises web server) and you want Istio to capture and control traffic to it.

What you will learn:

* Confirm the cluster and Istio environment
* Create a ServiceEntry for an external host
* Create a VirtualService to instruct Istio how to route traffic to that ServiceEntry
* Ensure the client pod has an Envoy sidecar so Istio can intercept outbound traffic

Keywords: Istio ServiceEntry, VirtualService, external workload, Envoy sidecar, mesh-external, static endpoint

***

## Prerequisites

| Requirement            | Purpose / Example                                                   |
| ---------------------- | ------------------------------------------------------------------- |
| Istio installed        | `istioctl version` (example `1.26.3`)                               |
| External NGINX web app | Reachable at `myapp.com` (resolved via `/etc/hosts` to a static IP) |
| kubectl access         | To create resources and run test pods                               |

Useful quick-check commands:

```bash theme={null}
kubectl get pods -A
istioctl version
```

Example `istioctl version` output:

```text theme={null}
client version: 1.26.3
control plane version: 1.26.3
```

Verify the external app resolves and responds from the control plane host (this demo uses an `/etc/hosts` override):

```bash theme={null}
curl myapp.com
```

Example truncated HTML response:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
...
</html>
```

Example local DNS override used in this demo:

```bash theme={null}
cat /etc/hosts
# example:
# 10.0.0.6 docker-registry-mirror.kodekloud.com
# 127.0.1.1 controlplane controlplane
# 192.168.121.2 myapp.com
```

Note: replace `192.168.121.2` below with the IP address your `myapp.com` resolves to.

***

## Step 1 — Create a ServiceEntry

A ServiceEntry tells Istio about services that exist outside the mesh. For a fixed IP endpoint use `resolution: STATIC`. Create a file named `se.yaml`:

```yaml theme={null}
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: myapp-service-entry
spec:
  hosts:
  - myapp.com
  location: MESH_EXTERNAL
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: STATIC
  endpoints:
  - address: 192.168.121.2
```

Apply the ServiceEntry and confirm it was created:

```bash theme={null}
kubectl apply -f se.yaml
kubectl get serviceentries.networking.istio.io
```

Example output:

```text theme={null}
NAME                   HOSTS            LOCATION      RESOLUTION   AGE
myapp-service-entry    ["myapp.com"]    MESH_EXTERNAL STATIC       30s
```

This exposes `myapp.com:80` to the mesh and instructs Istio to resolve `myapp.com` to the static IP `192.168.121.2`.

***

## Step 2 — Run a test client pod and try curling the external host

Start a simple pod that includes curl. The official nginx image may not include curl; use an image such as `curlimages/curl` and keep the pod alive with `sleep`:

```bash theme={null}
kubectl run test --image=curlimages/curl --restart=Never --command -- sleep 3600
kubectl get pods
```

Example:

```text theme={null}
NAME   READY   STATUS    RESTARTS   AGE
test   1/1     Running   0          30s
```

From inside the test pod, attempt to curl the external host:

```bash theme={null}
kubectl exec test -- curl -sS -D - myapp.com -o /dev/null
```

If the test pod does not have an Istio sidecar injected (no Envoy), you might see an unexpected intermediate response such as an HTTP 302 from an upstream gateway or proxy:

```HTTP Response Header theme={null}
# HTTP/1.1 302 Found
# ...
```

Or you may see the small HTML 302 page:

```html theme={null}
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>stgw</center>
</body>
</html>
```

Why? Although the ServiceEntry exists, Istio only applies mesh routing (VirtualService handling) when the client pod's outbound traffic is intercepted by the Envoy sidecar. If the pod is running in a namespace without Istio automatic sidecar injection enabled, Envoy is not present and Istio cannot manage that pod’s outbound traffic.

<Callout icon="lightbulb" color="#1CB2FE">
  Make sure the client pod has an Envoy sidecar injected. If the pod is in a namespace without Istio injection enabled, Istio features (VirtualService routing and ServiceEntry handling through the proxy) will not be applied to that pod's outbound traffic.
</Callout>

***

## Step 3 — Create a VirtualService to route traffic to the ServiceEntry

A VirtualService tells Istio how to route requests for specified hosts. Create `vs.yaml`:

```yaml theme={null}
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp-vs
spec:
  hosts:
  - myapp.com
  http:
  - route:
    - destination:
        host: myapp.com
        port:
          number: 80
```

Apply the VirtualService and list virtual services:

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

Example output:

```text theme={null}
NAME       GATEWAYS   HOSTS             AGE
myapp-vs              ["myapp.com"]     4s
```

Although this VirtualService appears to route `myapp.com` to `myapp.com:80` (redundant looking), it is important: it instructs Istio how to process and forward requests for `myapp.com`. The destination host `myapp.com` is then resolved by the ServiceEntry to the external IP.

***

## Step 4 — Ensure the client pod has an Istio sidecar

If the test pod was created in a namespace without sidecar injection, Istio will analyze and warn:

```bash theme={null}
istioctl analyze
```

Example analysis result:

```text theme={null}
Info [IST0102] (Namespace default) The namespace is not enabled for Istio injection. Run 'kubectl label namespace default istio-injection=enabled' to enable it, or 'kubectl label namespace default istio-injection=disabled' to explicitly mark it as not needing injection.
```

Enable automatic injection for the namespace (example shows the `default` namespace):

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

Recreate the test pod so it will be injected with the Envoy sidecar:

```bash theme={null}
kubectl delete pod test
kubectl run test --image=curlimages/curl --restart=Never --command -- sleep 3600
kubectl get pods
```

You should see the pod transition and then report `READY 2/2`:

```text theme={null}
# NAME   READY   STATUS    RESTARTS   AGE
# test   2/2     Running   0          1m
```

`2/2` indicates the application container plus the Envoy sidecar are running.

***

## Step 5 — Retry the curl from the injected pod

With the Envoy sidecar present, Istio will capture and route outbound traffic according to the ServiceEntry and VirtualService. Run:

```bash theme={null}
kubectl exec test -- curl -sS myapp.com
```

You should receive the full NGINX page:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
...
</html>
```

This confirms Istio is intercepting the traffic and routing the request to the external endpoint specified in the ServiceEntry.

***

## Troubleshooting & Tips

* If the external service listens on a different port (e.g., `9090`), update the ServiceEntry `ports` and `endpoints` accordingly.
* For DNS-resolvable external services use `resolution: DNS` and omit `endpoints` — Istio will resolve the host dynamically.
* For fixed internal IPs (such as an on-premise box), `resolution: STATIC` with `endpoints` is appropriate.
* If you prefer manual sidecar injection, use `istioctl kube-inject` or the `sidecar.istio.io/inject` annotation on pod spec rather than enabling namespace-wide automatic injection.
* Use `istioctl analyze` to check for common configuration issues and hints.
* Remember to replace the demo IP `192.168.121.2` with the correct IP for your environment.

***

## Quick reference commands

| Action                     | Command                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------- |
| Apply ServiceEntry         | `kubectl apply -f se.yaml`                                                         |
| Apply VirtualService       | `kubectl apply -f vs.yaml`                                                         |
| Run test pod               | `kubectl run test --image=curlimages/curl --restart=Never --command -- sleep 3600` |
| Exec into pod and curl     | `kubectl exec test -- curl -sS myapp.com`                                          |
| Enable namespace injection | `kubectl label namespace default istio-injection=enabled --overwrite`              |
| Analyze Istio config       | `istioctl analyze`                                                                 |

***

## Links and References

* Istio Networking Concepts: ServiceEntry — [https://istio.io/latest/docs/reference/config/networking/service-entry/](https://istio.io/latest/docs/reference/config/networking/service-entry/)
* Istio VirtualService docs — [https://istio.io/latest/docs/reference/config/networking/virtual-service/](https://istio.io/latest/docs/reference/config/networking/virtual-service/)
* Kubernetes Documentation — [https://kubernetes.io/docs/](https://kubernetes.io/docs/)

This exercise is useful preparation for the Istio Certified Associate (ICA) exam and real-world scenarios where you need to bring external services under Istio control. Try it in a lab environment to reinforce the steps.

<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/68e4e6ad-3398-4e12-a666-5b9f96fda7d2" />

  <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/b96f41b7-7bd2-4022-b388-701bec8e39ff" />
</CardGroup>
