Istio isn’t just a proxy; it’s a distributed system that fundamentally changes how your applications communicate, offering observability and control without touching your application code.

Let’s get Istio installed on EKS. We’ll use the istioctl installer for simplicity, but keep in mind this is for demonstration and production might warrant more advanced configurations.

First, ensure you have kubectl and istioctl installed and configured for your EKS cluster.

# Install istioctl (example for macOS)
curl -L https://istio.io/downloadIstio | sh -
export PATH=$PWD/bin:$PATH
istioctl version

Now, let’s install Istio. We’ll use the demo profile, which is good for getting started and includes most features.

istioctl install --set profile=demo -y

This command deploys Istio’s core components into the istio-system namespace. You’ll see Deployments, Services, and CRDs being created.

To verify the installation, check the pods in the istio-system namespace.

kubectl get pods -n istio-system

You should see pods like istiod, ingressgateway, and egressgateway running. istiod is the control plane, managing the proxies. The gateways are the entry and exit points for traffic into and out of the mesh.

Now, let’s enable Istio injection for a namespace. This is crucial because it tells Istio to automatically inject its sidecar proxy (envoy) into your application pods. We’ll create a new namespace called my-app.

kubectl create namespace my-app
kubectl label namespace my-app istio-injection=enabled

The istio-injection=enabled label is the magic. When a pod is created in this namespace, Istio’s admission controller intercepts it and adds the Envoy sidecar container.

Let’s deploy a simple application, like httpbin, into this namespace.

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.19/samples/httpbin/httpbin.yaml -n my-app

After deploying, check the pods in the my-app namespace.

kubectl get pods -n my-app

You’ll notice that each httpbin pod now has two containers: the httpbin application and the istio-proxy (Envoy). This is the sidecar injection in action.

To access httpbin, we need an Istio Ingress Gateway. We’ll create a simple Gateway and VirtualService to expose it.

First, apply the default Istio ingress gateway configuration:

kubectl apply -f samples/addons/prometheus.yaml -n istio-system # Prometheus is useful for observability
kubectl apply -f samples/addons/grafana.yaml -n istio-system # Grafana for dashboards
kubectl apply -f samples/addons/jaeger.yaml -n istio-system # Jaeger for distributed tracing
kubectl apply -f samples/addons/kiali.yaml -n istio-system # Kiali for mesh visualization

Then, create a Gateway resource. This tells Istio to listen on a specific port for incoming traffic and where to send it.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
  namespace: my-app
spec:
  selector:
    istio: ingressgateway # Use istio default ingress gateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*" # Or specify your domain

Apply this:

kubectl apply -f gateway.yaml -n my-app

Now, create a VirtualService. This defines how to route traffic to your httpbin service.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
  namespace: my-app
spec:
  hosts:
  - "*" # Matches the host in the Gateway
  gateways:
  - httpbin-gateway # Link to the Gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: httpbin
        port:
          number: 8000 # httpbin service port

Apply this:

kubectl apply -f virtualservice.yaml -n my-app

To get the external IP of the ingress gateway:

kubectl get svc istio-ingressgateway -n istio-system

Find the EXTERNAL-IP. Once it’s available, you can access httpbin using curl with that IP.

curl http://<EXTERNAL-IP>/status/200

The most surprising thing about Istio is that its core functionality—traffic routing, load balancing, health checks, and even secure mTLS communication—is handled by the Envoy sidecar, not by your application code. Your application just talks to localhost and Istio takes care of the rest.

The istiod control plane’s primary role is to configure all these Envoy sidecars. It watches Kubernetes resources (like VirtualService, DestinationRule, Gateway) and pushes the relevant configuration to each Envoy instance. When you change a VirtualService, istiod tells the relevant Envoys to update their routing rules.

The one aspect that trips most people up initially is understanding the relationship between Gateway and VirtualService. The Gateway acts like a traditional load balancer or ingress controller, defining how traffic enters the mesh (which ports, hosts, TLS settings). The VirtualService then defines where that traffic goes within the mesh (which services, paths, versions). They are separate but tightly coupled for ingress traffic.

Next, you’ll likely want to explore traffic management features like fault injection or traffic splitting for canary deployments.

Want structured learning?

Take the full Eks course →