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 ( |
Frozen snapshot of code + dependencies |
registry ( |
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 ( |
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 |