cloud-demo/docs/QUICKSTART.md

4.1 KiB
Raw Blame History

Quickstart: manual cluster (no Terraform)

Gets a real k3s cluster running today, by hand, so you can learn/practice Flux and GitOps immediately instead of waiting on the Terraform/libvirt provider work in docs/SETUP.md to be sorted out. One throwaway VM, created directly with virt-install rather than the dmacvicar/libvirt Terraform provider. When the Terraform track is ready, tear this down (last section) and provision the "real" 3-node cluster properly instead.

Shares steps 14 of docs/SETUP.md as prerequisites — do those first if you haven't:

  • Step 1: KVM/libvirt installed on the T630.
  • Step 2: the unprivileged k8s user exists, with ~/.ssh/k3s_homelab generated.
  • Step 3: openssl rand -hex 32 isn't needed here (no agents joining, so no cluster token) — skip it.
  • Step 4: cloud-demo pushed to Forgejo, with the flux-write token generated.

Everything below runs as k8s on the T630 (sudo -iu k8s).

1. Create the VM

mkdir -p ~/vms
curl -L -o ~/vms/noble-base.img \
  https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

# a writable overlay on top of the base image, sized up to 20G
qemu-img create -f qcow2 -F qcow2 -b ~/vms/noble-base.img ~/vms/k3s-manual.qcow2 20G

Write the cloud-init user-data — paste in the contents of ~/.ssh/k3s_homelab.pub where marked:

cat > ~/vms/k3s-manual-user-data.yaml <<'EOF'
#cloud-config
hostname: k3s-manual
manage_etc_hosts: true

users:
  - name: k3s
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - PASTE ~/.ssh/k3s_homelab.pub CONTENTS HERE

package_update: true
packages:
  - curl

runcmd:
  - curl -sfL https://get.k3s.io | sh -s - server
  - mkdir -p /home/k3s/.kube
  - k3s kubectl config view --raw > /home/k3s/.kube/config
  - chown -R k3s:k3s /home/k3s/.kube
EOF

This is a single, standalone server — no join token, no agents, no custom network (uses libvirt's default NAT network + DHCP). A k3s server node schedules workloads on itself by default, so this alone is a complete, usable cluster.

virt-install \
  --name k3s-manual \
  --memory 2048 \
  --vcpus 2 \
  --disk ~/vms/k3s-manual.qcow2 \
  --import \
  --os-variant ubuntu24.04 \
  --network network=default \
  --cloud-init user-data=~/vms/k3s-manual-user-data.yaml \
  --graphics none \
  --noautoconsole

2. Find its IP and get kubectl talking to it

Give cloud-init ~2 minutes to finish installing k3s after the VM boots.

virsh domifaddr k3s-manual   # note the IP under the default network

ssh k3s@<VM_IP> sudo cat /etc/rancher/k3s/k3s.yaml \
  | sed "s/127.0.0.1/<VM_IP>/" > ~/.kube/config-manual

export KUBECONFIG=~/.kube/config-manual
kubectl get nodes   # expect 1 Ready node

3. Bootstrap Flux against Forgejo

Same repo, same token, same target path as the full guide would use — nothing about this is cluster-specific:

flux bootstrap git \
  --url=https://git.boglabob.com/codegit/cloud-demo \
  --branch=main \
  --path=clusters/homelab \
  --username=codegit \
  --password=<FLUX_WRITE_TOKEN> \
  --token-auth \
  --kubeconfig ~/.kube/config-manual

4. Continue with the shared steps

From here, docs/SETUP.md steps 8 onward apply exactly as written, regardless of how the cluster was created — verifying the podinfo GitOps loop, registering the Forgejo Actions runner, exposing apps through Caddy, the Dashboard, remote kubectl access. Just use ~/.kube/config-manual as the kubeconfig throughout instead of the Terraform-provisioned one.

Tearing this down

Once the Terraform track in docs/SETUP.md is ready and you tofu apply the real 3-node cluster, remove this one:

# as k8s
virsh destroy k3s-manual              # stop it
virsh undefine k3s-manual --remove-all-storage   # delete VM + its disk
rm ~/.kube/config-manual
rm -rf ~/vms

Nothing else needs cleaning up — Flux's state lived entirely inside that VM's cluster and goes away with it. The Forgejo repo, deploy tokens, and the Forgejo Actions runner registration are all cluster-independent and carry over to the real cluster unchanged; just re-run flux bootstrap against its kubeconfig once it's up.