Deploy on ankka¶
satisfactory deploys as two ankka services in one project: api, exposed,
and solver, which serves nothing. The service descriptors are in the repository under deploy/, and
everything a deployment must supply is an environment variable read from a secret.
Build the images¶
sbt api/Docker/publishLocal solver/Docker/publishLocal deployDescriptors
The first two publish satisfactory-api and satisfactory-solver to the local Docker daemon. The
third writes the descriptors to target/deploy/ with the image names and the ankka runtime version
filled in; set DOCKER_REPOSITORY to prefix the image names with a registry. Push the images wherever
the cluster pulls from, or load them into a local kind cluster:
kind load docker-image satisfactory-api:<version> satisfactory-solver:<version> --name ankka
Create the secrets¶
Both descriptors read from one Kubernetes secret, satisfactory-secrets, in the project's namespace:
| Key | Used by | What it is |
|---|---|---|
runner-token |
both | the shared token workers present on /internal; any long random string |
secret-key |
api | encrypts webhook signing secrets at rest; any long random string, and changing it makes existing subscriptions unreadable |
auth-issuer |
api | the identity provider's issuer URL, which every platform token must carry |
auth-jwks-url |
api | where the identity provider publishes its signing keys |
ankka projects create satisfactory -O <organization>
kubectl -n ankka-satisfactory create secret generic satisfactory-secrets \
--from-literal=runner-token="$(openssl rand -base64 32)" \
--from-literal=secret-key="$(openssl rand -base64 32)" \
--from-literal=auth-issuer="https://<keycloak>/realms/<realm>" \
--from-literal=auth-jwks-url="https://<keycloak>/realms/<realm>/protocol/openid-connect/certs"
The identity provider is the one ankka's control plane already uses, so a person who can log in to
ankka can administer a tenant here with the same token. The operator role,
satisfactory-operator by default, is a realm role on that provider.
Deploy the services¶
ankka services apply -f target/deploy/api.json -p satisfactory
ankka services apply -f target/deploy/solver.json -p satisfactory
ankka services expose api -p satisfactory
The api descriptor sets SAT_LOCAL_RUNNER to false, so the API hosts no worker of its own, and
runs three small instances. The solver descriptor sets http to false and runs three large
instances, each with one solving slot, so the pool's capacity is its instance count; it names no api
address, because in a cluster the solver calls the api service as itself over mutual TLS. Both are
odd-counted because ankka's cluster overlay keeps the majority side of a partition.
Two settings the descriptor leaves at their defaults are worth setting on api:
SAT_PUBLIC_URL, the address the API is exposed at. It is how the links in webhook events are rooted; left unset they point athttp://localhost:9000.SAT_METRICS_TOKEN, the bearer token Prometheus presents to/metrics. Left unset,/metricsrefuses every caller.
expose prints the hostname, https://api-satisfactory.<base domain>. The runner endpoint under
/internal is reachable there too, and its token is the only thing in front of it: keep the token long
and random. Configuration lists every variable.
Create the first tenant and key¶
With a token from the identity provider for a user holding the operator role:
export SAT=https://api-satisfactory.<base domain>
curl -s -X POST $SAT/api/platform/v1/tenants -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"acme","firstAdminSubject":"<the subject of the tenant admin>"}'
curl -s -X POST $SAT/api/platform/v1/tenants/<tenantId>/keys -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"label":"planner","role":"read-write"}'
The second reply is the only time the key's plaintext is shown. From here the model API answers that
key, and Your first solve applies with $SAT/api/models/… as the base.
Check it¶
ankka services logs solver -p satisfactory # claims and reports as datasets are solved
curl -s -H "X-API-KEY: $KEY" $SAT/api/aboutme
Deleting a solver pod mid-solve is a good first test of the pool: the dataset's stream stays monotonic, another worker continues from the best solution within the lease's time to live, and the dataset completes. Workers and leases says why.
Roll out a new version¶
ankka services apply with a new image tag rolls each service. A solver instance that receives the
stop signal releases every dataset it holds, with its best solution reported, before it exits, and
another instance continues them. An API roll does not interrupt a solve at all: the workers retry an
unavailable API and the event stream resumes from its sequence number.