Kubernetes Setup¶
This guide installs a minimal k0s cluster (lab-cluster) on master01 and worker01 with k0sctl, then hands the cluster over to Flux, which installs every add-on from riupie/gitops-fluxcd.
| Item | Details |
|---|---|
| Stack | k0s v1.36.4+k0s.1k0sctl v0.33.1Calico flux-operator 0.61.0 (Flux 2.x) |
| Target | master01 (192.168.10.10, controller)worker01 ( 192.168.10.11, worker) |
| Run from | Fedora 44 host |
| Env | lab |
Prerequisites¶
- Prerequisites and Infrastructure Deployment completed
-
VMs running and reachable over SSH as
cloud(thessh_adminuser created by the VM module): -
kubectlinstalled on the host:kubectl version --client
Steps¶
1. Install k0sctl¶
On the host. Download the release binary and verify its checksum:
K0SCTL_VERSION=v0.33.1
dl="$(mktemp -d)" && cd "$dl"
curl -fsSLO https://github.com/k0sproject/k0sctl/releases/download/${K0SCTL_VERSION}/k0sctl-linux-amd64
curl -fsSLO https://github.com/k0sproject/k0sctl/releases/download/${K0SCTL_VERSION}/checksums.txt
sha256sum --check --ignore-missing checksums.txt
sudo install -m 0755 k0sctl-linux-amd64 /usr/local/bin/k0sctl
k0sctl version
cd - >/dev/null && rm -rf "$dl"
Note
https://get.k0s.sh installs the k0s binary, not k0sctl. k0sctl only runs on the host; it installs k0s on the nodes over SSH.
2. Review the cluster configuration¶
The configuration is k0s/k0sctl.yaml in this repo:
| Setting | Value |
|---|---|
| Controller | master01 192.168.10.10 (control plane only; it does not appear in kubectl get nodes) |
| Worker | worker01 192.168.10.11 |
| SSH user | cloud |
| CNI | Calico |
| kube-proxy | IPVS with strictARP: true (required by MetalLB L2) |
| Helm extension | flux-operator chart 0.61.0 in flux-system; nothing else |
3. Deploy the cluster¶
On the host, from the repo root:
Add --debug if a step fails. k0sctl checks SSH access, installs k0s on both nodes, initializes the controller, joins the worker and installs the flux-operator chart.
4. Get cluster access¶
mkdir -p ~/.kube
k0sctl kubeconfig --config k0s/k0sctl.yaml > ~/.kube/config
kubectl config current-context # lab-cluster
kubectl --context lab-cluster cluster-info
Warning
This overwrites ~/.kube/config. If you already have other clusters, write to a separate file and merge it, or use KUBECONFIG.
5. Connect Flux to the GitOps repository¶
flux-operator is running but has nothing to sync yet. Create the FluxInstance that points it at gitops-fluxcd:
kubectl --context lab-cluster apply -f - <<'EOF'
apiVersion: fluxcd.controlplane.io/v1
kind: FluxInstance
metadata:
name: flux
namespace: flux-system
spec:
distribution:
version: "2.x"
registry: ghcr.io/fluxcd
artifact: oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests
components:
- source-controller
- kustomize-controller
- helm-controller
- notification-controller
cluster:
type: kubernetes
size: small
networkPolicy: true
sync:
kind: GitRepository
url: https://github.com/riupie/gitops-fluxcd.git
ref: refs/heads/main
path: clusters/development
interval: 1m
EOF
From here on, Flux reconciles every add-on from clusters/development in the repo: change the add-ons by committing to gitops-fluxcd, not with kubectl apply. The kubectl apply above only bootstraps the FluxInstance. Once it syncs, Flux also reconciles the copy in clusters/development/flux-system/flux-instance.yaml, so change it there rather than re-applying the manifest above.
Verify¶
1. Nodes and system pods¶
kubectl --context lab-cluster get nodes -o wide
kubectl --context lab-cluster -n kube-system get pods
Expected output:
NAME STATUS ROLES AGE VERSION INTERNAL-IP ...
worker01 Ready <none> 4m v1.36.4+k0s 192.168.10.11 ...
kube-system contains calico-*, coredns-*, konnectivity-agent-*, kube-proxy-* and metrics-server-* pods, all Running.
2. Flux¶
kubectl --context lab-cluster -n flux-system get fluxinstance,gitrepository,kustomization
kubectl --context lab-cluster get helmrelease -A
Expected: the FluxInstance flux is READY True, the flux-system GitRepository points at https://github.com/riupie/gitops-fluxcd.git, and the Kustomizations (flux-system, infrastructure, infrastructure-configs, apps) are READY True. HelmReleases for MetalLB, cert-manager, External-DNS, External Secrets, Sealed Secrets and agentgateway appear as they reconcile.
3. MetalLB¶
Expected: IPAddressPool lb-pool with 192.168.10.100-192.168.10.110 and a matching L2Advertisement.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
k0sctl apply fails at SSH connect |
Your key isn’t on the VMs, or the user isn’t cloud |
Re-check the ssh_keys step in Infrastructure Deployment |
kubectl get nodes shows only worker01 |
Expected: the controller runs no kubelet | Nothing to fix |
FluxInstance not ready |
flux-operator can’t pull the distribution from ghcr.io |
kubectl --context lab-cluster -n flux-system logs deploy/flux-operator |
Kustomization infrastructure fails |
A manifest in gitops-fluxcd is invalid |
kubectl --context lab-cluster -n flux-system describe kustomization infrastructure, fix it in the repo |
Cleanup¶
⚠ WARNING: Destructive operation. This removes k0s and all cluster data (etcd, workloads, PVC contents on worker01) from both nodes. The VMs are left in place.
Then remove the local access you created in step 4 (this deletes ~/.kube/config entirely; skip it if the file holds other clusters):
To remove the VMs as well, follow Infrastructure Deployment, Cleanup. Removing the VMs also removes the cluster, so k0sctl reset is only needed to rebuild the cluster on the same VMs.
Next steps¶
- Set Up Dynamic DNS: BIND9 on general01, which External-DNS writes to
- Secure MCP Servers with OAuth: Keycloak in front of MCP servers on agentgateway