Skip to content

Install a local platform

A complete ankka platform runs on a laptop in a kind cluster. One script, kustomization/deploy-local.sh in the ankka repository, builds the platform's images, loads them into the cluster, installs everything the platform depends on and starts it. The result answers at https://api.127.0.0.1.sslip.io:8443, over TLS, with no port-forward.

The local platform is the same platform a cloud cluster runs, with three local choices: images are loaded into the node rather than pulled from a registry, certificates come from a certificate authority created inside the cluster, and the base domain resolves to 127.0.0.1.

Inside the cluster, every name under the base domain resolves to the gateway instead, on the same port as on your machine. A service can therefore call another at its public hostname, such as https://cart-checkout.127.0.0.1.sslip.io:8443, and the call goes through the gateway with the same routes and certificates a browser meets. The deploy script adds this to the cluster's DNS, CoreDNS, with a stable ankka-gateway Service in envoy-gateway-system for the names to resolve to. A cloud installation's domain resolves publicly to its load balancer and needs neither.

Prerequisites

  • Docker, with enough memory for several JVMs; 8 GB for Docker is comfortable.
  • kind 0.24 or later, and kubectl. Earlier versions of kind do not enforce network policy, which the platform relies on.
  • A JDK 21 and sbt, because the script builds the platform's images from source.
  • A checkout of the ankka repository. Every command on this page runs from its root.
  • Optionally just, for the short forms of the commands.

Nothing more is needed for services built to WebAssembly modules. A wasm service's pod copies its module into a shared volume by running the service's own image as an init container, so it works on any container runtime kind ships; the runtime that loads the module is the platform's own image, which the script already builds.

Create the cluster

kind create cluster --name ankka --config kustomization/kind.yaml

The configuration file matters. It publishes the gateway's node ports on your machine, container port 30080 on host port 8080 and 30443 on 8443, and kind decides port mappings only when it creates a cluster: they cannot be added later. The deploy script checks for them and refuses a cluster created without them, naming the fix, because such a cluster would deploy cleanly and then fail every request by hostname.

Before it deploys anything, the script also proves the cluster enforces network policy: it starts two throwaway pods, puts a deny-all policy on one, and requires a connection to it to fail. On a cluster that stores the policy and ignores it, the script stops with:

refusing to deploy: this cluster accepted a NetworkPolicy and did not enforce it; ankka needs a
network that does (kind 0.24 or later does; see docs/platform/install-local.md).

On kind, a cluster that passed this probe before can fail it later: kind's network, kindnet, stops enforcing new policies once its informers have died of API timeouts, which the load of a first install can cause. The script restarts kindnet once and probes again before it refuses; by hand, that is kubectl -n kube-system rollout restart daemonset/kindnet.

A first run also pulls every image the platform's databases, broker and identity provider need, so its waits are long, up to ten minutes for a database. If a wait still runs out, run the script again once the cluster has caught up: every step is idempotent.

Deploy the platform

./kustomization/deploy-local.sh

Or, with just, create the cluster if it is not there and deploy in one step:

just up

The script takes several minutes on its first run. In order, it:

  1. Checks where it is pointed. It refuses to run unless the current kubectl context is kind-ankka, and unless that cluster publishes the gateway's ports. It never deploys to whatever context happens to be current.
  2. Installs the controllers the platform builds on, each with server-side apply and waiting for each to be ready: CloudNativePG for Postgres, cert-manager for certificates, Envoy Gateway for the gateway, and the Keycloak operator for the identity provider. Their custom resource definitions must exist before anything that uses them is applied.
  3. Builds the platform's images with sbt docker:publishLocal: the operator, the control plane, the sidecar that hosts services in other languages, and the shopping cart sample.
  4. Loads them into the cluster's node with kind load docker-image. No registry is involved.
  5. Applies the AnkkaService custom resource definition, then the control plane's namespace.
  6. Applies everything else with one kubectl apply -k of the local overlay: the operator, the control plane with its own Postgres cluster and the schema that cluster is created with, the installation's gateway with a local certificate authority and a wildcard certificate, Keycloak with its database and the ankka realm's import, and the telemetry store every service exports its traces and metrics to, with an agent on the node that gathers what every pod printed. The store's image is about a gigabyte, pulled the first time. The overlay is the whole platform, so applying it any other way — by hand, or from a GitOps tool — installs the same thing.
  7. Restarts the operator and the control plane so they run the images just loaded, even when nothing in their manifests changed.
  8. Waits for the control plane's database, the operator, the control plane's three instances, the gateway and its certificate, and Keycloak.
  9. Waits for the ankka realm to be imported into Keycloak, then creates a development user dev with password dev and the platform-admin role, and a client ankka-local-smoke for scripts on this machine. The user and the client are the only things the script adds beyond the overlay, and they exist only on a local cluster.
  10. Exports the local certificate authority's root to ~/.ankka/local-ca.crt.
  11. Checks the platform end to end: it obtains a token from the identity provider and lists organizations through the gateway, which exercises DNS, TLS, both routes, token verification and the control plane's database. Then it asks the telemetry store, through Grafana, for a trace from the control plane. A problem is printed as a warning naming the likely cause.

It ends by printing the control plane's address and the commands to use it.

Add the model server

The local installation runs no model server unless asked. To add one, deploy with the flag set:

ANKKA_LOCAL_MODELS=on ./kustomization/deploy-local.sh
just deploy-models               # the same

The script then applies the local-models overlay, which is the local overlay with the models component added, loads the keeper's image into the node, and waits for the model server to be ready. The first run fetches every declared model, so it is as slow as the download. The component declares one small model that runs on a laptop's processor; see Models served by the installation for changing the declaration and naming a model from a service. Adding the model server restarts every running service once, because each is given the server's address.

Deploying again without the flag applies the local overlay, which has no model server. The model server's namespace and the models it fetched stay until the cluster is deleted.

Point the CLI at it

ankka config set url https://api.127.0.0.1.sslip.io:8443
ankka config set ca ~/.ankka/local-ca.crt
ankka login                      # user dev, password dev, in a browser

Nothing on your machine is asked to trust the local certificate authority. The CLI is told about it with config set ca, and curl with --cacert. There is no option anywhere to skip certificate verification.

Open the console

The console is at https://console.127.0.0.1.sslip.io:8443; sign in as dev with password dev. A browser shows it only once it trusts the local certificate authority, which the deploy script exported to ~/.ankka/local-ca.crt. On macOS, open that file in Keychain Access and mark it trusted for SSL; remove it from the keychain when you tear the platform down.

Open the telemetry store

Every service's traces, metrics and logs are at https://grafana.127.0.0.1.sslip.io:8443, as admin with password admin, in the same browser that trusts the local certificate authority. The control plane's own traces are there before anything is deployed. The store keeps what it holds only until it restarts. See Telemetry.

The identity provider

The identity provider's console is at https://auth.127.0.0.1.sslip.io:8443/admin/, as admin with password admin. Those credentials are public and belong to the local platform only; a cloud installation removes them. Add users there. See Identity and machine accounts.

From here, Deploy to a local platform deploys the sample.

Use another base domain

The base domain 127.0.0.1.sslip.io relies on a public DNS service that resolves any name ending in an address to that address. If your resolver blocks it, add hosts-file entries and deploy with a matching base domain:

echo '127.0.0.1  api.ankka.local auth.ankka.local cart-checkout.ankka.local' | sudo tee -a /etc/hosts
ANKKA_BASE_DOMAIN=ankka.local ./kustomization/deploy-local.sh

A hosts file has no wildcards, so every hostname you use, including auth.<base domain> for logins and each exposed service, needs a line.

Run it again

The script is safe to run again, and running it again is how the platform picks up a change to its source: it rebuilds and reloads the images and restarts the operator and the control plane onto them. Without that restart a re-run would change nothing running, because the manifests and the latest tags are the same as before and Kubernetes sees no difference.

Some things are created once and not updated by a re-run. The Keycloak realm is imported only if it does not exist, so a change to the realm file on an existing cluster is made in Keycloak's console.

Tear it down

just down                        # delete the kind cluster and stop the local compose services
kind delete cluster --name ankka # the same, without just

Deleting the cluster deletes every service and every database in it.

The shortcuts

Command Does
just up create the cluster if it does not exist, then deploy
just deploy run deploy-local.sh
just deploy-models run deploy-local.sh with the model server added
just cluster-create create the cluster from kustomization/kind.yaml
just cluster-status whether the cluster exists and what platform pods are running
just down delete the cluster and stop the local compose services
just render print the local overlay's manifests without applying them

Each is one command, or a call to the script, which holds the guards. Everything works without just installed.