- Go 66.4%
- Go Template 30%
- Makefile 2%
- Dockerfile 1.6%
| build | ||
| deploy | ||
| templates | ||
| .containerignore | ||
| .gitignore | ||
| .golangci.yml | ||
| Containerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| README.md | ||
scaling-replicas
A small Go demo app for Kubernetes that shows which pod and node served the current request, and lists the other replicas of its own Deployment by querying the Kubernetes API. Useful for demonstrating replication, load balancing, and pod scheduling.
Features
- Web UI (Tailwind CSS via CDN) showing:
- This pod's name, node, and IP
- A live table of all sibling replicas (name, node, IP, phase, ready status, start time)
- Event-driven live updates via Turbo Streams
over a WebSocket (Turbo loaded from a CDN, no build step): the server
watches the Kubernetes API for changes to sibling pods and pushes a
freshly rendered
<turbo-stream>update to every connected browser the moment something changes /healthzendpoint for liveness/readiness probes/metricsendpoint exposing Prometheus metrics:demo_app_replica_count/demo_app_ready_replica_countdemo_app_http_requests_total/demo_app_http_request_duration_secondsdemo_app_pod_info
How it works
The app discovers its own identity without relying on the Downward API:
- Pod name —
os.Hostname()(Kubernetes sets the pod's hostname to its name) - Namespace — read from the projected service account token at
/var/run/secrets/kubernetes.io/serviceaccount/namespace - Node name / Pod IP — looked up via the Kubernetes API by fetching its own Pod object
It then builds a label selector from its own labels (excluding
pod-template-hash) and lists matching pods in the namespace to find its
sibling replicas.
Live updates (/ws)
/ws is a WebSocket endpoint. At startup the server opens a Kubernetes
watch on pods matching its replica label selector. Whenever that watch
sees a pod added, updated, or deleted, the server re-lists the replicas,
renders templates/_stream.html.tmpl (which wraps the rows partial,
templates/_rows.html.tmpl, plus the count and selector/error areas in
<turbo-stream> elements), and broadcasts the result to every connected
client. New connections immediately receive the most recent render.
On the frontend, templates/index.html.tmpl declares a Turbo
<turbo-stream-source> element whose src is set server-side to the
correct ws:///wss:// URL for the request (/ws). Turbo applies the
incoming <turbo-stream> updates to the page directly — no custom
DOM-patching code needed.
Requirements
- RBAC: the pod's ServiceAccount needs
get/list/watchonpodsin its namespace (seedeploy/role.yamlanddeploy/rolebinding.yaml)
Building
podman build -f Containerfile -t scaling-replicas:latest .
Deploying (OpenShift / Kubernetes)
Manifests live in deploy/ and are managed with Kustomize:
oc apply -k deploy/
# or
kubectl apply -k deploy/
This creates:
ServiceAccount,Role,RoleBinding— RBAC for listing podsDeployment— 3 replicas, restricted SCC-compatible security contextService— exposes HTTP and metrics portsRoute— external access (edge TLS)ServiceMonitor— Prometheus scrape config (requires the cluster/user-workload monitoring stack)
Configuration
| Env var | Default | Description |
|---|---|---|
LISTEN_ADDR |
:8080 |
Address the HTTP server listens on |
License
MIT — see LICENSE.