Deploy OpenZiti in Kubernetes with Ease Using k3d
Fast and Easy Deployment for Kubernetes in Docker
k3d is a Kubernetes distribution for Docker that makes it easy to replicate a production-like environment in a local container environment. It's great for local development and familiarizing yourself with OpenZiti's deployment options.
The Plan
Create a k3d cluster.
Install the OpenZiti controller.
Install the OpenZiti router.
Create a Cluster
Prerequisite: k3d
CLI
We need a cluster with forwarded ports for the controller and a router. Run k3d
to create a cluster.
k3d cluster create ziti-local \
--port 1280:1280@loadbalancer \
--port 6262:6262@loadbalancer \
--port 3022:3022@loadbalancer \
--port 10080:10080@loadbalancer
Ports Breakdown
controller
1280
- client API web listener6262
- control plane for routers
router
3022
- edge listener for identities10080
- link listener for other routers
A note about why everything's port-separated in this tutorial: It's simpler. OpenZiti uses mTLS extensively, so the cluster must pass through TLS to the controller and router pods. This tutorial uses LoadBalancer services because they provide a raw TCP proxy and are convenient with Traefik in k3d. OpenZiti can also separate services sharing a port by ALPN identifier, and Ingress/Gateway controllers can separate them by SNI.
Find the Node Address
Prerequisite: kubectl
CLI
Run kubectl
to set a shell environment variable NODE_IP
to the LoadBalancer address. This gives us an IP address that's routable inside and outside our k3d cluster. We'll use it to invent a DNS name in a magic wildcard zone (e.g., sslip.io
, nip.io
).
Windows
For Docker Desktop on Windows, use the WSL2 VM interface address and run the rest of the commands with BASH in WSL2.
NODE_IP=$(getent hosts host.docker.internal | awk '{ print $1 }')
macOS
For Docker Desktop on macOS, use the default host interface address.
NODE_IP=$(ipconfig getifaddr $(route get default | awk '/interface: / { print $2 }'))
Linux
For Docker daemon on Linux, use the IP of the load balancer container.
NODE_IP=$(docker inspect k3d-ziti-local-serverlb|jq -r '.[].NetworkSettings.Networks[].IPAddress')
Install the Controller Chart
Prerequisite: helm
CLI
Run helm
to add the OpenZiti charts repo.
helm repo add "openziti" https://openziti.io/helm-charts
helm repo update "openziti"
Install the Cert Manager and Trust Manager Custom Resource Definitions so we can install them as subcharts.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.crds.yaml
kubectl apply -f https://raw.githubusercontent.com/cert-manager/trust-manager/v0.7.0/deploy/crds/trust.cert-manager.io_bundles.yaml
Install and configure the controller to advertise the node address (i.e., the LoadBalancer address).
helm upgrade --install "ziti-controller" openziti/ziti-controller \
--namespace "ziti" --create-namespace \
--set clientApi.advertisedHost="client.ziti.${NODE_IP}.sslip.io" \
--set clientApi.advertisedPort=1280 \
--set clientApi.service.type=LoadBalancer \
--set ctrlPlane.advertisedHost="ctrl.ziti.${NODE_IP}.sslip.io" \
--set ctrlPlane.advertisedPort=6262 \
--set ctrlPlane.service.type=LoadBalancer \
--set trust-manager.app.trust.namespace=ziti \
--set trust-manager.enabled=true \
--set cert-manager.enabled=true
Input Values Breakdown
clientApi
advertisedHost
: FQDN that identities will connect to the control planeadvertisedPort
: port on the FQDN identities will connect toservice.type
: K8s Service type
ctrlPlane
advertisedHost
: FQDN that routers will connect to the control planeadvertisedPort
: port on the FQDN routers will connect toservice.type
: K8s service type
-
app.trust.namespace
: K8s Namespace where Trust Manager is allowed to read K8s Secrets to serve bundles of trusted root CA certificatesenabled
: install Trust Manager sub chart in the same namespace
cert-manager
enabled
: install Cert Manager sub chart in the same namespace
Log in to the OpenZiti Controller
Wait for the controller deployment to become ready.
kubectl wait deployments "ziti-controller" \
--namespace ziti \
--for condition=Available=True \
--timeout 240s
Prerequisite: ziti
CLI
Get the admin's password and log in
kubectl get secrets "ziti-controller-admin-secret" \
--namespace "ziti" \
--output go-template='{{index .data "admin-password" | base64decode }}' \
| xargs -rl ziti edge login client.ziti.${NODE_IP}.sslip.io:1280 \
--yes --username "admin" \
--password
What does this gnarly one-liner do? It's three commands. The first, kubectl
, gets the admin password from K8s. The second command, xargs
, tacks the password onto the end of the third, ziti edge login
.
Create an OpenZiti Router
You must administratively create a Ziti router. The -t
option is short for --tunneler-enabled
and enables the built-in tunneler to be used as a reverse proxy for cluster-internal services. This will be convenient when you create your first OpenZiti service.
ziti edge create edge-router "router1" -t -o ./router1.jwt
Expected Output
New edge router router1 created with id: OdiqYOi9RW
Enrollment expires at 2024-05-17T20:50:59.142Z
Install the Router Chart
The token allows the router to bootstrap itself with a renewable identity from the controller.
helm upgrade --install "ziti-router" openziti/ziti-router \
--namespace "ziti" \
--set-file enrollmentJwt=./router1.jwt \
--set edge.advertisedHost="router1.edge.ziti.${NODE_IP}.sslip.io" \
--set edge.advertisedPort=3022 \
--set edge.service.type=LoadBalancer \
--set linkListeners.transport.advertisedHost="router1.link.ziti.${NODE_IP}.sslip.io" \
--set linkListeners.transport.advertisedPort=10080 \
--set linkListeners.transport.service.type=LoadBalancer \
--set tunnel.mode=host \
--set ctrl.endpoint="ctrl.ziti.${NODE_IP}.sslip.io:6262"
Input Values Breakdown
enrollmentJwt
: This is the path to the token file you got from administratively creating the router.edge
advertisedHost
: FQDN identities will connect to the data planeadvertisedPort
: Port on the FQDN identities will connect toservice.type
: K8s service type
linkListeners
transport
advertisedHost
: FQDN routers will connect to the data planeadvertisedPort
: port on the FQDN where routers will connectservice.type
: K8s service type
ctrl
endpoint
: control plane address for this router to connect to
Ensure router1 is Online.
ziti edge list edge-routers
Expected Output
╭────────────┬─────────┬────────┬───────────────┬──────┬────────────╮
│ ID │ NAME │ ONLINE │ ALLOW TRANSIT │ COST │ ATTRIBUTES │
├────────────┼─────────┼────────┼───────────────┼──────┼────────────┤
│ OdiqYOi9RW │ router1 │ true │ true │ 0 │ │
╰────────────┴─────────┴────────┴───────────────┴──────┴────────────╯
results: 1-1 of 1
That's It!
Now, you can add identities and connect them with services and policies.
Recommended: Kubernetes service tutorial.
Pop into the forum if you have any ideas or issues: https://openziti.discourse.group/.
Related
- This other Kubernetes Quickstart uses Minikube instead of k3d