Deploying an update to myApp on k3s

The loop to run every time you change your app. Five steps: edit → build → push → deploy → verify.

Assumes the registry name pi5:5000 and that you run all of this on the pi5 (control plane), from inside ~/myApp.

1. Edit the code

Make your change in flask_app.py (or wherever). Nothing special here — just save the file.

2. Build a new image

docker build -t myapp .

This reads your Dockerfile and packages the current code into a fresh image called myapp. The running pods do not see source changes until you do this — a container is a frozen snapshot, so new code means a new image.

3. Tag and push to the registry

Use a new version number each time (v2, v3, …) rather than reusing latest:

docker tag myapp pi5:5000/myapp:v2
docker push pi5:5000/myapp:v2
  • tag gives the image the registry’s address (pi5:5000/...) plus a version, so the cluster knows where to find it and which build it is.

  • push uploads it to the registry on the pi5, which is the one place all three nodes can pull from. A pod can land on any node, so the image has to live somewhere shared — that’s what the registry is for.

Note

Why a new version each time? Kubernetes caches images by tag. If you reuse :latest, the cluster thinks “I already have latest” and won’t pull your new build. A fresh tag (:v2) is unambiguously new, so it always pulls. It also gives you a clean rollback point.

4. Deploy the new version

Point the deployment at the new tag:

kubectl set image deployment/myapp myapp=pi5:5000/myapp:v2

This triggers a rolling update: Kubernetes replaces the old pods with new ones a few at a time, keeping the app serving throughout. Old pods aren’t killed until new ones are healthy (that’s what your readiness probe on /health gates), so there’s no downtime.

Watch it happen:

kubectl rollout status deployment/myapp

It prints progress and returns when the update is complete.

5. Verify

kubectl get pods -o wide                   # all replicas Running, READY 1/1
curl http://<any-node-ip>:30080/health     # should return {"status":"ok"}

-o wide shows which node each pod is on. Hitting /health on any node’s IP confirms the new version is actually serving (NodePort routes to a healthy pod wherever it lives).

If something’s wrong: roll back

One command returns to the previous version instantly:

kubectl rollout undo deployment/myapp

Kubernetes keeps the last-known-good spec, so this swaps the pods back to the prior image with the same no-downtime rolling process. This is the payoff of versioned tags — the old image is still in the registry, ready to go.

Quick reference (the whole loop)

# 1. edit flask_app.py, then:
docker build -t myapp .
docker tag myapp pi5:5000/myapp:v2
docker push pi5:5000/myapp:v2
kubectl set image deployment/myapp myapp=pi5:5000/myapp:v2
kubectl rollout status deployment/myapp
kubectl get pods -o wide

Bump v2 v3 v4 on each cycle.

What each piece is doing (the mental model)

Thing

Role

Dockerfile

Recipe for building the image from your code

image (myapp)

Frozen snapshot of code + dependencies

registry (pi5:5000)

Shared store all nodes pull images from

Deployment

Declares “I want N copies of this image running” and manages updates

Service (NodePort)

Stable entry point; routes traffic to healthy pods on any node

readiness probe (/health)

Gate that keeps traffic off a pod until it’s actually ready

rolling update

Swaps pods incrementally so the app never fully goes down

rollout undo

One-step return to the previous version