# RKE2 Kubernetes Cluster — Discovery & Cold-Boot Recovery

## TL;DR

An existing RKE2 cluster (3 CP + 3 Workers) was found stopped on Proxmox.
Booting it requires only `qm start` on each VM. The cluster was fully
bootstrapped 111 days prior with ArgoCD, Cilium CNI, Ceph CSI, External
Secrets, CloudNativePG, MariaDB Operator, and Velero.

## Cluster Topology

| VMID | Name | Role | Node (dynamic) | IP | Specs |
|------|------|------|-----------------|-----|-------|
| 118 | rke2-cp-01 | Control Plane | proxmox5 | 10.0.30.51 | 4c / 12GB / 40GB |
| 130 | rke2-cp-02 | Control Plane | proxmox3 | 10.0.30.52 | 4c / 12GB / 40GB |
| 129 | rke2-cp-03 | Control Plane | proxmox2 | 10.0.30.53 | 4c / 12GB / 40GB |
| 128 | rke2-worker-01 | Worker | proxmox5 | 10.0.30.61 | 4c / 12GB / 80GB |
| 132 | rke2-worker-02 | Worker | proxmox2 | 10.0.30.62 | 4c / 12GB / 80GB |
| 131 | rke2-worker-03 | Worker | proxmox3 | 10.0.30.63 | 4c / 12GB / 80GB |

VMs are HA-managed — PVE may place them on different nodes than where
they were originally created. Always scan all nodes for VMs:
```bash
for n in proxmox1 proxmox2 proxmox3 proxmox4 proxmox5 proxmox6 proxmox7; do
  ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.10 \
    "ssh -o ConnectTimeout=3 root@$n 'qm list 2>/dev/null | grep rke2'"
done
```

## Management VM

VM 200 (mgmt-runner-01) on proxmox3 has `kubectl` and `helm` installed.
Kubeconfig at `/root/.kube/config` (must use `KUBECONFIG=/root/.kube/config`
explicitly — kubectl doesn't pick it up from default location via qm guest exec).

## Cold-Boot Procedure

### 1. Start CPs First, Then Workers

```bash
# Start all CPs
for vmid in 118 130 129; do
  node=$(find_node_for_vmid $vmid)
  ssh root@$node "qm start $vmid"
done
sleep 15
# Start all workers
for vmid in 128 132 131; do
  node=$(find_node_for_vmid $vmid)
  ssh root@$node "qm start $vmid"
done
```

### 2. Wait for Convergence (5-10 min)

Workers initially show `NotReady` — the rke2-agent needs to retrieve
serving-kubelet certs from the API server (returns 503 until CPs converge):

```
Waiting to retrieve agent configuration;
server is not ready: serving-kubelet.crt: 503 Service Unavailable
```

This is NORMAL during cold boot. Wait 5-10 minutes. Workers transition
to `Ready` once cert exchange completes.

### 3. Verify

```bash
# Via mgmt-runner
ssh root@10.0.20.10 "ssh root@proxmox3 'qm guest exec 200 -- sh -c \
  \"KUBECONFIG=/root/.kube/config kubectl get nodes\"'"
```

All 6 nodes should show `Ready`.

## What's Already Installed

| Component | Namespace | Status |
|-----------|-----------|--------|
| ArgoCD | argocd | Running (App-of-Apps) |
| Cilium CNI | kube-system | Running |
| Ceph CSI (RBD) | kube-system | 2 CrashLoopBackOff on cold boot |
| Traefik Ingress | kube-system | Running (IngressClass) |
| CoreDNS | kube-system | Running |
| Metrics Server | kube-system | Running |
| External Secrets | external-secrets | Running |
| CloudNativePG | cnpg-system | Running (PostgreSQL operator) |
| MariaDB Operator | mariadb-operator | Running |
| MariaDB Galera | mariadb | Starting (needs time) |
| PostgreSQL | postgres | 3-replica cluster |
| Velero | velero | Partially running |
| Memory (Qdrant+Ollama) | openclaw-memory | ContainerCreating |

## ArgoCD GitOps

- **Admin password**: `kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d`
- **Ingress**: `argocd` host via Traefik (https://10.0.30.70 with Host: argocd)
- **Git repo**: `http://10.0.30.105:3000/dominik/iac-homelab.git`
- **Root app path**: `clusters/main/apps` (App-of-Apps pattern)
- **Auto-sync**: `prune: true, selfHeal: true`
- **Applications**: root, operators, databases, mariadb-operator, mariadb-operator-crds, cloudnativepg-operator, velero-operator, backups, memory

## Known Cold-Boot Issues

### Ceph CSI CrashLoopBackOff

Some `ceph-csi-rbd-nodeplugin` pods enter CrashLoopBackOff after cold boot.
They typically recover after a few restart cycles. If they don't:
```bash
kubectl -n kube-system delete pod -l app=csi-rbd-nodeplugin
# Or restart the provisioner
kubectl -n kube-system rollout restart deployment ceph-csi-rbd-provisioner
```

### kube-controller-manager CrashLoopBackOff on CP-01

`kube-controller-manager-rke2-cp-01` may CrashLoop briefly during leader
election. It resolves once a stable leader is elected among the 3 CPs.

### Old Pods Stuck in Terminating

After 111 days offline, many pods show `Terminating` or `Unknown`.
These are ghosts from the previous run. They clear automatically as the
new replicas become ready. Force-delete if stubborn:
```bash
kubectl -n <namespace> delete pod <pod-name> --force --grace-period=0
```

## Kubeconfig Transfer

To set up kubectl on a new management machine:

```bash
# Get kubeconfig from CP-01 (via qm guest exec)
RAW=$(ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.10 \
  "ssh root@proxmox5 'qm guest exec 118 -- cat /etc/rancher/rke2/rke2.yaml'" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['out-data'])")

# Replace 127.0.0.1 with CP-01 IP
echo "$RAW" | sed 's/127.0.0.1/10.0.30.51/g' > ~/.kube/config
chmod 600 ~/.kube/config
```

## Migration Candidates (PVE → K8s)

Services suitable for K8s migration (ordered by ease):

| Phase | Services | Current Location |
|-------|----------|-----------------|
| 1 | (Already done) ArgoCD, Operators, DBs | RKE2 cluster |
| 2 | Grafana, Prometheus, Alertmanager, Blackbox | CT 141 (Docker) |
| 3 | Traefik (→ IngressController), Authelia | CT 99999, CT 112 |
| 4 | Gitea, Paperless-ngx, Paperless-GPT, LibreChat | CT 108, 104, 113, 133 |
| 5 | Seafile, Immich | CT 111 (Docker, 11 containers) |
| 6 | Ollama, LiteLLM, Ideogram4 | CT 123, 124, CT 111 |

Services that STAY on PVE (not K8s candidates):
- Home Assistant (USB/Zigbee, HA OS)
- Frigate (GPU passthrough — though could use K8s GPU operator)
- MariaDB Galera VMs (already clustered, K8s adds complexity)
- Hermes Agent (terminal access, persistent state)
- PBS (PVE-integrated)
- Samba shares (kernel-level file serving)
- Dovecot IMAP
