Skip to main content

Deploy and Verify

What You Will Learn

  • How to commit and push your manifests to trigger a deployment
  • How to use the Flux CLI to speed up reconciliation
  • How to verify that every piece is running correctly
  • How to troubleshoot common deployment issues

Step 1: Commit and Push

Your manifests are ready. Time to commit them to the dds-k8s-cluster repo. Remember: all commits at Junovy are GPG signed. Your YubiKey needs to be inserted and ready.

# Navigate to the Flux repo
cd ~/workspace/junovy/dds-k8s-cluster

# Stage only the files you created
git add clients/com.junovy.godot-demo/

# Check what you are about to commit
git status

You should see your five files listed as new:

new file:   clients/com.junovy.godot-demo/kustomization.yaml
new file:   clients/com.junovy.godot-demo/010-namespace.yaml
new file:   clients/com.junovy.godot-demo/040-deployment.yaml
new file:   clients/com.junovy.godot-demo/050-service.yaml
new file:   clients/com.junovy.godot-demo/055-ingress.yaml

Now commit and push:

# Commit with a descriptive message
# Your YubiKey will prompt for a PIN -- watch for the pinentry dialog
git commit -m "feat(clients): add hst-godot-demo tenant for Godot HTML5 game"

# Push to the remote
git push origin main

If the GPG signing prompt times out or fails, do not panic. Just run the git commit command again and enter your PIN when prompted.


Step 2: Trigger Flux Reconciliation

Flux polls the Git repo on a schedule (usually every few minutes). You can skip the wait by triggering reconciliation manually:

# Tell Flux to pull the latest from Git and apply changes
# --with-source: also refreshes the GitRepository source (pulls latest commit)
flux reconcile kustomization junovy-hosting --with-source

This command tells Flux: "Go check the repo right now and apply anything new." You will see output like:

> source GitRepository/flux-system updated
> kustomization junovy-hosting reconciliation triggered

Give it 30 to 60 seconds to finish applying resources.


Step 3: Verify the Deployment

Now check that each piece is running. Work from the bottom up: namespace, then Pods, then Service, then Ingress, then TLS.

Check the Namespace

# Confirm the namespace was created
kubectl get namespace hst-godot-demo

Expected output:

NAME            STATUS   AGE
hst-godot-demo  Active   1m

Check the Pods

# List Pods in the tenant namespace
kubectl get pods -n hst-godot-demo

Expected output:

NAME                          READY   STATUS    RESTARTS   AGE
godot-demo-6d4f8b7c59-x2k9m  1/1     Running   0          45s

The key things to look for: READY is 1/1 and STATUS is Running.

Check the Service

# Confirm the Service was created and has a ClusterIP
kubectl get service -n hst-godot-demo

Expected output:

NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
godot-demo    ClusterIP   10.233.42.17    <none>        80/TCP    1m

Check the Ingress

# Confirm the Ingress is configured with the correct hostname
kubectl get ingress -n hst-godot-demo

Expected output:

NAME          CLASS   HOSTS                          ADDRESS        PORTS     AGE
godot-demo    traefik-external   game.godot-demo.junovy.com     10.233.0.1     80, 443   1m

The PORTS column should show both 80 and 443, meaning TLS is configured.

Check TLS

TLS certificates are managed by Traefik's built-in ACME resolver. Unlike cert-manager, Traefik does not create separate Certificate resources -- it handles everything internally. To verify TLS is working:

# Quick TLS check using curl (from a machine with internet access)
curl -sI https://game.godot-demo.junovy.com | head -5

You should see a 200 OK response over HTTPS. If you see certificate errors, wait a few minutes for Traefik to complete the ACME challenge, then try again.


Step 4: Open the Browser

Navigate to:

https://game.godot-demo.junovy.com

Your Godot game should load in the browser. You will see the Godot loading screen, followed by your game's main scene.


Troubleshooting

If something is not working, here are the most common issues and how to fix them:

Symptom Likely Cause How to Fix
Pod status: ImagePullBackOff The cluster cannot pull the image from ECR Check the image path in 040-deployment.yaml. Confirm ECR credentials are configured. Run kubectl describe pod -n hst-godot-demo for details.
Pod status: CrashLoopBackOff The container starts but crashes immediately Check container logs: kubectl logs -n hst-godot-demo deployment/godot-demo. Usually a bad nginx config or missing files.
Pod status: Pending No node has enough resources to schedule the Pod Check events: kubectl describe pod -n hst-godot-demo. Reduce resource requests if needed.
Ingress shows no ADDRESS The Ingress controller has not processed it yet Wait 1-2 minutes. If still empty, check: kubectl describe ingress -n hst-godot-demo for errors.
Browser shows 502 Bad Gateway The Ingress controller cannot reach the Service/Pod Confirm the Pod is Running and READY 1/1. Check that Service selector labels match Deployment labels.
Browser shows 503 Service Unavailable No ready endpoints for the Service The readiness probe is failing. Check logs: kubectl logs -n hst-godot-demo deployment/godot-demo.
Certificate stuck at READY: False cert-manager cannot complete the ACME challenge Check: kubectl describe certificate -n hst-godot-demo and kubectl describe certificaterequest -n hst-godot-demo. DNS may not be configured yet.
Game shows blank white screen Missing CORS headers or wrong MIME types Open the browser console (F12). Look for SharedArrayBuffer errors. Verify the nginx config in the Dockerfile has the cross-origin headers.
Game loads but assets are missing PCK file not served correctly Confirm application/octet-stream pck; is in the nginx MIME types. Check the browser network tab for 404 errors.

The Debug Command Cheat Sheet

# View Pod logs (most useful for diagnosing crashes)
kubectl logs -n hst-godot-demo deployment/godot-demo

# Get detailed info about a Pod (events, conditions, node placement)
kubectl describe pod -n hst-godot-demo

# Get all resources in the namespace at once
kubectl get all -n hst-godot-demo

# Watch Pods in real-time (useful while waiting for startup)
kubectl get pods -n hst-godot-demo --watch

# Check Flux reconciliation status
flux get kustomizations | grep junovy-hosting

You Did It

Take a moment to appreciate what you just accomplished. You:

  1. Exported a Godot project as an HTML5 web app
  2. Wrote a Dockerfile with the correct headers for WebAssembly
  3. Built a multi-architecture container image and pushed it to ECR
  4. Created a tenant in the Flux repo with a namespace and manifests
  5. Wrote a Deployment, Service, and Ingress from scratch
  6. Pushed to Git and watched Flux deploy it automatically
  7. Verified every layer of the stack

You went from game source code to a live, TLS-secured web app running on a production Kubernetes cluster. That is the full deployment pipeline -- and you built every piece of it yourself.


Key Takeaways

  • Always verify with git status before committing and watch for the GPG signing prompt
  • Use flux reconcile kustomization junovy-hosting --with-source to trigger immediate deployment
  • Verify from the bottom up: namespace, Pods, Service, Ingress, certificate
  • Most issues can be diagnosed with kubectl describe and kubectl logs
  • The troubleshooting table covers the most common failure modes you will encounter

What Is Next

Next up: Chapter 10 - Project: Onboard the Radical Faeries where you will onboard a real tenant from scratch, including planning, multiple services, and a full handoff.