Smarter on Kubernetes
Smarter runs in two kinds of environment, and the same Docker image serves both:
Docker, with
docker-compose.yml, on a developer’s computer. Every service runs as one container on one machine, the web console is served onhttp://localhost:9357, and nothing is reachable from the internet.Kubernetes, with the Smarter Helm chart, in production. The platform runs as several Deployments that scale horizontally, behind a load balancer, and every deployed LLMClient gets its own hostname, DNS record, Kubernetes Ingress and TLS certificate.
The Python code is the same in both. What differs is the infrastructure around it, and that difference matters most when an LLMClient is deployed: in Docker, deploying an LLMClient changes a row in the database; in Kubernetes, it also creates cloud and cluster resources that someone has to support.
This section is for the people who install, operate and support Smarter in production: platform engineers, SREs, and the administrators who answer “why isn’t my LLMClient reachable?”. ADR-002 and ADR-003 record why Smarter chose Docker and Kubernetes (AWS EKS) in the first place.
The Services
Both environments run the same five processes. Only how they are started, scaled and connected differs.
Process |
Docker ( |
Kubernetes (Helm chart) |
|---|---|---|
Web application and REST API |
|
Deployment |
Operational Celery worker |
|
Deployment |
Infrastructure Celery worker |
|
Deployment |
Celery Beat |
|
Deployment |
Database and cache |
|
MySQL or MariaDB and Redis, inside the cluster or managed by the cloud
( |
The two Celery queues are separate on purpose. The infrastructure queue carries the slow tasks that deploy, verify and destroy DNS records, ingresses, certificates, node groups and databases, so that they can never delay the operational tasks that record prompt history, charges and budgets. See ADR-030.
The Helm chart also runs a Job, smarter-job-init, on every install and upgrade. It migrates the
database, initializes the platform, loads the example resources and deploys the built-in LLMClients.
On a new cluster, that one Job queues a deployment for every built-in LLMClient at once.
Docker and Kubernetes Compared
Concern |
Docker |
Kubernetes |
|---|---|---|
Configuration |
A |
The |
Kubernetes namespace |
None is used. |
|
Hostnames |
|
|
TLS |
None: plain HTTP on localhost. |
cert-manager issues a certificate for every hostname, including one per deployed LLMClient. Custom Domains also get an ACM certificate. |
Routing a request to an LLMClient |
Django resolves the LLMClient from the URL path (sandbox URLs). |
Traefik routes the hostname to the |
Deploying an LLMClient |
Sets |
Creates a DNS A record, an Ingress, a cert-manager Certificate and a TLS Secret, then verifies them. See LLMClient Deployment. |
Scaling |
One container per service. |
HorizontalPodAutoscalers for the app and the operational worker. Pods are stateless; all state is in the database, Redis, and the cloud. |
Health checks |
None. |
Liveness, readiness and startup probes on |
Upgrades |
Rebuild the image and restart the containers. |
|
Logs |
|
|
Cloud resources |
None, normally. |
Route53 records, ACM certificates, EKS node groups (LLMHost) and StatefulSets with volumes (self-hosted vector stores), recorded in the infrastructure resource ledger. |
Warning
The deployment tasks act on whatever cloud credentials and kubeconfig their container has. If you
give a Docker environment real AWS credentials and a kubeconfig for a real cluster, deploying an
LLMClient locally creates real Route53 records and applies a real Ingress to the namespace
smarter-local of that cluster. To keep a Docker environment self-contained, leave the cloud
credentials out, or set SMARTER_LLM_CLIENT_TASKS_CREATE_DNS_RECORD=false and
SMARTER_LLM_CLIENT_TASKS_CREATE_INGRESS_MANIFEST=false.
How Smarter Talks to the Cluster
Smarter never calls the Kubernetes API from a client library. The
infrastructure services run
kubectl as a subprocess, with a kubeconfig that
the cloud provider writes (aws eks update-kubeconfig for EKS), and with the cloud identity of the
pod. Every pod that runs the image has kubectl and the AWS CLI installed, and KUBECONFIG
points at /home/smarter_user/data/.kube/config.
In practice, the infrastructure worker is the pod that changes the cluster: it applies and deletes Ingresses, and reads Certificates and Secrets. The permissions that this requires, and how to keep them small, are described in Operating Smarter at Scale.
Kubernetes, DNS and certificate calls all go through one facade,
smarter.apps.infrastructure.services.infrastructure, so the cloud is a configuration choice
(SMARTER_CLOUD_PROVIDER), and tests replace it with an in-memory provider.
Cluster Prerequisites
The Helm chart installs Smarter itself. It does not install the cluster add-ons that LLMClient deployment depends on. Before the first LLMClient is deployed, the cluster needs:
Traefik as an ingress controller, with the
traefikIngressClass and thewebsecureentry point, exposed by a cloud load balancer.A Traefik Middleware named
https-redirectin the environment’s namespace. Every LLMClient Ingress refers to it as<namespace>-https-redirect@kubernetescrd.cert-manager, with a ClusterIssuer named after the environment’s API domain, e.g.
api.platform.example.comin production oralpha.api.platform.example.comin alpha. Every LLMClient Ingress names it in itscert-manager.io/cluster-issuerannotation.A DNS zone for the API domain, whose A record points at the load balancer. Every LLMClient’s A record is a copy of it.
An identity for the pods that can manage that DNS zone and the namespace’s Ingresses. See Operating Smarter at Scale.
The Smarter Terraform modules build this infrastructure on AWS. See Production Deployment.