When deploying a proxy outside of Kubernetes, users typically reach for solutions like nginx or Traefik; it's pretty rare to see Kubernetes-native load balancers, like Istio, used outside of the Kubernetes context. While it is not documented anywhere on istio.io, it is completely possible to set up an Istio Gateway outside of Kubernetes - not just running out of the cluster, but not connected to any Kubernetes api-server whatsoever.
In this post, we will set up Istiod, and Istio ingress gateway, and a test application using docker-compose
. The full configuration can be found in howardjohn/local-istio-gateway. Setting up a full mesh outside of Kubernetes is possible, but outside of the scope of this post.
Setup Istiod
First, we will run the control plane. Because we don't have an api-server to act as a config store, we will use local files as configuration. First, we need to create a folder to store our configurations and secrets (Istio will fail if it cannot write some certificates to disk; this is not really needed for our setup since everything is over localhost, but its not easy to disable).
mkdir configs/ secrets/
We also need a mesh config, stored in this example as mesh
, specified:
enableAutoMtls: false # We are not handling mTLS here.
accessLogFile: /dev/stdout # Setup access logs so we can verify traffic.
defaultConfig:
discoveryAddress: istiod:15010 # Address proxies can reach us at.
controlPlaneAuthPolicy: NONE # We are not using TLS for control plane communication in this example.
configSources:
# Tell Istiod to read configurations from files only (and not Kubernetes)
- address: fs:///var/lib/istio/config/data
Next, we can set up the container. Create a docker-compose.yml
:
version: "3.3"
services:
istiod:
image: gcr.io/istio-testing/pilot:latest
command: [discovery, --registries=] # --registries= tells Istiod to not look at Kubernetes for Endpoints, Services, etc
hostname: istiod # Setup a stable hostname for the proxies to connect to
volumes:
- ./mesh:/etc/istio/config/mesh
- ./configs:/var/lib/istio/config/data
- ./secrets:/var/run/secrets
With that setup, you should be able to run docker-compose up
and see Istiod running successfully.
Setup the gateway
Next, we can add a new service to our docker-compose.yml
for the Gateway. We will also add a simple test app so we can verify our setup works:
ingress:
image: gcr.io/istio-testing/proxyv2:latest
command: [proxy, router]
volumes:
- ./mesh:/etc/istio/config/mesh
environment:
FILE_MOUNTED_CERTS: "true" # We don't have the full certificate flow setup here, so just use local files.
ISTIO_META_NAMESPACE: istio-system
ISTIO_METAJSON_LABELS: '{"istio": "ingressgateway", "app": "istio-ingressgateway"}'
GRPC_XDS_BOOTSTRAP: ""
ports:
- 80:80
echo:
hostname: echo.local
image: gcr.io/istio-testing/app:latest
command: [--port=80]
Running docker-compose up
now should have all three applications running. However, if you try to curl localhost
, you will not get any response. This is because we haven't yet configured the Gateway at all.
Setup the config
In the configs/
folder we created previously, add the following configuration:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: echo
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: echo
namespace: default
spec:
hosts:
- "*"
gateways:
- echo
http:
- route:
- destination:
host: echo.local
port:
number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: echo
spec:
hosts:
- "echo.local"
ports:
- number: 80
name: http
protocol: HTTP
resolution: DNS
When the file is added, Istiod will automatically pick up the changes and send them to the gateway, without needing to restart.
Now a curl localhost
should succeed.