A small Go demo app for Kubernetes/OpenShift that shows which pod and node served the current request, and lists the other replicas of its own Deployment via the Kubernetes API — useful for demonstrating replication, load balancing, and pod scheduling.
  • Go 66.4%
  • Go Template 30%
  • Makefile 2%
  • Dockerfile 1.6%
Find a file
2026-06-15 22:06:57 +02:00
build Things on building and deploying 2026-06-13 17:21:22 +02:00
deploy Things on building and deploying 2026-06-13 17:21:22 +02:00
templates Remove the PodIP from the top cards 2026-06-13 17:04:02 +02:00
.containerignore Inital commit 2026-06-13 16:00:23 +02:00
.gitignore Inital commit 2026-06-13 16:00:23 +02:00
.golangci.yml Inital commit 2026-06-13 16:00:23 +02:00
Containerfile Inital commit 2026-06-13 16:00:23 +02:00
go.mod Update a couple of dependencies 2026-06-15 22:06:57 +02:00
go.sum Update a couple of dependencies 2026-06-15 22:06:57 +02:00
LICENSE Inital commit 2026-06-13 16:00:23 +02:00
main.go Remove the PodIP from the top cards 2026-06-13 17:04:02 +02:00
Makefile Inital commit 2026-06-13 16:00:23 +02:00
README.md Things on building and deploying 2026-06-13 17:21:22 +02:00

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
  • /healthz endpoint for liveness/readiness probes
  • /metrics endpoint exposing Prometheus metrics:
    • demo_app_replica_count / demo_app_ready_replica_count
    • demo_app_http_requests_total / demo_app_http_request_duration_seconds
    • demo_app_pod_info

How it works

The app discovers its own identity without relying on the Downward API:

  • Pod nameos.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/watch on pods in its namespace (see deploy/role.yaml and deploy/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 pods
  • Deployment — 3 replicas, restricted SCC-compatible security context
  • Service — exposes HTTP and metrics ports
  • Route — 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.