Submission Updates — Budget Coach UI (Dev / Judges / Prod)
What changed (and why)
Single image placeholder in base
ui/k8s/base/deployment.yamlusesimage: REPLACE_ME_UI_IMAGE. Overlays swap this to a concrete image (tag for Dev/Judges, digest for Prod). Why: avoids “InvalidImageName” races and makes overlays deterministic.Environment variables standardized UI reads in-cluster endpoints via env:
USERSVC=http://userservice.default.svc.cluster.local:8080MCPSVC=http://mcp-server.default.svc.cluster.local:80INSIGHT=http://insight-agent.default.svc.cluster.local:80/apiACCOUNT=1011226111WINDOW_DAYS=30Why: names match the Streamlit app, kills 404s from wrong base paths.
Overlays by purpose
- Development: tag-based image for quick iteration.
- Judges: same tag + a
LoadBalancerservice for external access. - Production: digest-pinned image for immutability & rollbacks.
Tag vs Digest (quick guide)
Tag (
:v0.1.0) – mutable pointer; great for Dev/Judges. Pros: fast swaps, simple mental model. Cons: can drift; rollouts may pick unexpected bits if tag is moved.Digest (
@sha256:…) – immutable; the exact bytes you tested. Pros: reproducible, safe, audit-friendly, ideal for Prod. Cons: one more step to resolve the digest.
How to deploy
0) Set shared env once (terminal)
export PROJECT=<PROJECT-ID>
export REGION=us-central1
export REG="${REGION}-docker.pkg.dev/${PROJECT}/bank-of-anthos-repo"
1) Development (tag-based)
# Pin the dev overlay to a tag (already in repo, but repeatable)
(cd ui/k8s/overlays/development && \
kustomize edit set image REPLACE_ME_UI_IMAGE=${REG}/budget-coach-ui:v0.1.0)
# Apply + verify
make ui-dev-apply
make ui-dev-status
make ui-dev-smoke # quick POST to /api/budget/coach (expects 200/400 JSON)
2) Judges (public LB + tag-based)
# Apply + get the external IP + tail logs
make ui-judges-apply
make ui-judges-ip
make ui-judges-logs
# Open: http://$(make ui-judges-ip)
3) Production (digest-pinned)
A. Resolve digest for a tag (or skip if you already have it):
UI_TAG=v0.1.0
UI_IMAGE_DIGEST=$(gcloud artifacts docker images describe \
"$REG/budget-coach-ui:${UI_TAG}" \
--project "$PROJECT" --format='value(image_summary.digest)')
echo "$UI_IMAGE_DIGEST"
B. Bake the digest into the prod overlay and apply:
make ui-prod-set-digest # writes @sha256:… into ui/k8s/overlays/production
make ui-prod-apply
make ui-prod-verify
Expected verification output includes:
- Deployment image like:
.../budget-coach-ui@sha256:059630b47e...
- Pod env snapshot showing
USERSVC,MCPSVC,INSIGHT,ACCOUNT,WINDOW_DAYS.
Smoke tests (what “good” looks like)
- UI logs show endpoints and Streamlit URL
URL: http://0.0.0.0:8501
INFO:budget-coach:USERSVC=...
INFO:budget-coach:MCPSVC=...
INFO:budget-coach:INSIGHT=.../api
- Agent POST returns JSON (400 is fine with minimal body):
kubectl run curl --rm -it --restart=Never --image=curlimages/curl:8.7.1 -- \
sh -lc "curl -sS -D- -X POST -H 'Content-Type: application/json' \
-d '{\"transactions\":[]}' \
http://insight-agent.default.svc.cluster.local/api/budget/coach | head -n 20"
Troubleshooting quick checks
- Wrong image picked up / duplicate RS
kubectl get rs -l app=budget-coach-ui \
-o 'custom-columns=NAME:.metadata.name,IMAGE:.spec.template.spec.containers[0].image'
# delete old RS if needed
kubectl delete rs <old-rs>
- Env not updated after patch
kubectl rollout restart deploy/budget-coach-ui
POD=$(kubectl get pods -l app=budget-coach-ui -o jsonpath='{.items[-1:].metadata.name}')
kubectl exec "$POD" -- printenv | egrep '^(USERSVC|MCPSVC|INSIGHT|ACCOUNT|WINDOW_DAYS)='
LB IP shows but UI blank
- Ensure
budget-coach-ui-lbtargetsport:80 -> targetPort:8501 - Endpoints point at the Running UI pod IP on port 8501.
- Ensure
Log in or sign up for Devpost to join the conversation.