Exercise 6: Deploy workloads via Fleet¶
Time: 20 min
Previous: Exercise 5: Provision a cluster
You now have four running nodes:
- vertex-hub-01 on edge1 (Elemental-provisioned K3s)
- edge2 registered but not yet assigned to a cluster
- edge3 running RKE2 standalone (not yet in Rancher)
- edge4 running K3s standalone (not yet in Rancher)
Fleet is already watching for clusters with the right labels. A GitRepo called vertex-bank-app is pre-configured to target any cluster labeled demo=true and edge-type=x86-cluster.
The vertex-bank-app GitRepo points at the local Gitea instance on the EIB VM, not GitHub. Fleet syncs from http://192.168.122.20:3000/gitea/vertex-bank-app.git every 15 seconds. No internet access is needed.
# Confirm the GitRepo source
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 \
"kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml \
get gitrepo vertex-bank-app -n fleet-default \
-o jsonpath='{.spec.repo}'"
6.1 Trigger Fleet deployment on vertex-hub-01¶
edge1's MachineInventory has the demo=true and edge-type=x86-cluster labels from Exercise 5, but those do not propagate to the cluster Fleet actually watches. Label it explicitly:
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 \
"kubectl label clusters.fleet.cattle.io vertex-hub-01 -n fleet-default \
demo=true edge-type=x86-cluster --overwrite"
Use the full clusters.fleet.cattle.io name. This Rancher/Fleet setup has at least three separate CRDs all called Cluster, and a bare kubectl label cluster ... resolves to a different one. It succeeds with no error, but Fleet keeps showing 0/0 targeted clusters because the label never lands where Fleet is looking.
Check in Rancher UI: Continuous Delivery > Git Repos > vertex-bank-app. When vertex-hub-01 appears in the target clusters section, Fleet is deploying the app. It picks up the label within its normal 15-second poll cycle.
6.2 Import edge3 and edge4 into Rancher¶
edge3 and edge4 are running standalone clusters that Rancher does not know about yet. Import them:
In Rancher UI: Cluster Management > Import Existing > Generic. Give each cluster a name (vertex-branch-rke2, vertex-branch-k3s) and create it. Rancher then shows registration commands. This lab's Rancher uses a self-signed certificate, so copy the second one, the curl --insecure ... | kubectl apply -f - variant. The plain kubectl apply -f <url> command fails with x509: certificate signed by unknown authority.
Run it on each node:
# On edge3 (RKE2 keeps its kubectl under /var/lib/rancher/rke2/bin)
ssh -i /root/.ssh/id_ed25519 root@192.168.122.33 \
"curl --insecure -sfL <registration-manifest-url> | \
/var/lib/rancher/rke2/bin/kubectl --kubeconfig /etc/rancher/rke2/rke2.yaml apply -f -"
# On edge4 (K3s)
ssh -i /root/.ssh/id_ed25519 root@192.168.122.34 \
"curl --insecure -sfL <registration-manifest-url> | kubectl apply -f -"
Each cluster turns Active in Cluster Management within a couple of minutes. Fleet registers its own copy of each cluster a little after that, so check that both show up before you label them (labelling too early fails with NotFound):
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 \
"kubectl get clusters.fleet.cattle.io -n fleet-default"
When vertex-branch-rke2 and vertex-branch-k3s are listed, label them for Fleet:
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 "
kubectl label clusters.fleet.cattle.io vertex-branch-rke2 -n fleet-default \
demo=true edge-type=x86-cluster --overwrite
kubectl label clusters.fleet.cattle.io vertex-branch-k3s -n fleet-default \
demo=true edge-type=x86-cluster --overwrite
"
Fleet deploys vertex-bank-app to each cluster the moment the labels match. Go to Continuous Delivery > Git Repos > vertex-bank-app and watch the bundle status update per cluster.
6.3 Verify deployment¶
vertex-bank-app is a web app that shows live Kubernetes cluster vitals. Check the bundle status from the management cluster:
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 \
"kubectl get bundle -n fleet-default | grep vertex-bank"
The app itself runs on the edge clusters, not on the management cluster, so query vertex-hub-01 through the kubeconfig Rancher stores for it:
ssh -i /root/.ssh/id_ed25519 root@192.168.122.9 "
kubectl get secret vertex-hub-01-kubeconfig -n fleet-default \
-o jsonpath='{.data.value}' | base64 -d > /tmp/vertex-hub-01.yaml
kubectl --kubeconfig /tmp/vertex-hub-01.yaml get pods,svc -n vertex-bank
"
The service publishes NodePort 30080, and that is what you use. On an older copy of vertex-bank-app the service may show up as a LoadBalancer with EXTERNAL-IP stuck at <pending>, and the bundle as NotReady in Rancher, because nothing in this lab hands out LoadBalancer IPs. The app still answers on the NodePort. Open it on each node:
curl -s http://192.168.122.31:30080/ | grep -o "<title>.*</title>" # vertex-hub-01 (edge1)
curl -s http://192.168.122.33:30080/ | grep -o "<title>.*</title>" # vertex-branch-rke2 (edge3)
curl -s http://192.168.122.34:30080/ | grep -o "<title>.*</title>" # vertex-branch-k3s (edge4)
Or open http://192.168.122.31:30080 in a browser.
What you just built¶
Four nodes, four images, two provisioning paths, one management plane.
The image-first model: every node in this lab booted from a purpose-built disk image. No manual SSH config, no package installs, no configuration management applied to a running system. The image IS the configuration. If a node breaks, you re-image it. If a new site opens, you ship the image.
Why two paths exist: Elemental (edge1/edge2) is for sites where you do not know the node's final role at image-build time. You build one generic openSUSE Leap Micro image, ship it everywhere, and the management cluster decides what each node becomes after it registers. EIB standalone (edge3/edge4) is for sites where the role is known upfront: you bake K3s or RKE2 into the image and the node is a cluster the moment it boots.
Why TPM matters: without TPM, a cloned disk can register as any node in your fleet. With auth: tpm, the registration token is derived from hardware. You can revoke a specific node's registration by deleting its MachineInventory. Physical theft of the hardware does not compromise other nodes.
Why Hauler is there: in a real deployment, remote sites have unreliable internet. Hauler is a portable artifact store. Populate it once at HQ, copy it to a USB drive, and the site's nodes pull everything locally. In this lab, Hauler was pre-populated by the instructor. In production, you run hauler store sync to update and redistribute.
Two-minute comparison¶
| Elemental (edge1/edge2) | EIB standalone (edge3/edge4) | |
|---|---|---|
| Node role decided | At provisioning time (after registration) | At image-build time |
| Cluster control | Rancher provisions and manages K8s | Node runs its own K8s; optionally imported |
| Onboarding | Automatic via TPM phone-home | Manual import or pre-registered via EIB |
| Good for | Mixed-role sites, large fleets, dynamic scaling | Fixed-function nodes, strict airgap, predictable workloads |
| Recovery | Re-register = re-provision from management | Re-image = re-flash disk |