Guide to installing Istio, restricting egress to registered destinations, and using ServiceEntry and WorkloadEntry to expose and load balance external workloads into the mesh.
This guide demonstrates how to install Istio, lock down egress so that pods can only reach registered destinations, and use ServiceEntry + WorkloadEntry to allow pods to access an external (or external-looking) workload.Prerequisites: a running Kubernetes cluster and kubectl access.
Setting outboundTrafficPolicy.mode to REGISTRY_ONLY ensures pods can only reach destinations that are explicitly registered in Istio using ServiceEntry and/or WorkloadEntry. This is the recommended method to lock down egress in an Istio mesh.
3) Prepare a simulated external nginx service (host-based)
For this demo we simulate an external workload by installing nginx on the control-plane host (outside the cluster). In production, this would be an external VM or host.On the host (not in Kubernetes):
4) Create a test pod in Kubernetes and enable Istio injection
Start a test pod that will attempt to access the external address:
kubectl run test --image=nginxkubectl get pods# NAME READY STATUS# test 0/1 ContainerCreating (until injection enabled)
If the namespace is not enabled for Istio sidecar injection:
istioctl analyze# Info [IST0102] (Namespace default) The namespace is not enabled for Istio injection. Run 'kubectl label namespace default istio-injection=enabled' ...
Enable automatic injection, recreate the pod, and verify the sidecar is injected (pod shows 2/2 containers):
kubectl label namespace default istio-injection=enabledkubectl delete pod testkubectl run test --image=nginxkubectl get pods# test 2/2 Running
From inside the test pod, try curling the external IP. Because Istio is set to REGISTRY_ONLY, direct IP access will be blocked until the destination is registered:
kubectl exec -ti test -- curl --head http://10.50.0.1# HTTP/1.1 502 Bad Gateway# server: envoy
Envoy returns 502 Bad Gateway for unregistered destinations when REGISTRY_ONLY is enabled.
If you enable namespace injection, you must recreate pods in that namespace for the sidecar to be injected. Deleting and recreating the pod is the simplest approach.
8) Add an in-cluster pod behind the same WorkloadEntry for load balancing
You can include in-cluster pods as additional endpoints for the same logical service by sharing the label referenced by the workloadSelector. Istio will treat both the external IP and in-cluster pod(s) as endpoints for the hostname.Create a labeled nginx pod:
kubectl run nginx --image=nginx --labels="app=external"kubectl get pods# nginx 1/1 Running
Customize the pod’s index page so you can tell responses apart. Exec into the nginx pod and update the index:
kubectl exec -ti nginx -- /bin/bash# inside containercd /usr/share/nginx/htmlecho "This is an Nginx Pod" > index.htmlcurl localhost# This is an Nginx Podexit
Now query the ServiceEntry hostname from the test pod multiple times:
kubectl exec -ti test -- curl http://app.internal.com# Sometimes returns the external host content (host's nginx)# Sometimes returns "This is an Nginx Pod" (in-cluster nginx pod)
Because both endpoints have the same label used by workloadSelector, Istio will load-balance between them. Use Istio traffic-management features (VirtualService, DestinationRule) to control traffic weights and routing as needed.