Plan: lab1 workers on pv1-2 via per-node template copy

Session note on CAPMOX v0.9.1 against the 2-node PVE cluster, the plan and its execution log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 23:16:49 +02:00
co-authored by Claude Opus 5.5
parent 81462e0e0f
commit b88f721e3b
3 changed files with 231 additions and 0 deletions
@@ -0,0 +1,89 @@
# Session: CAPMOX against a two-node PVE cluster
**Date:** 2026-09-29 23:11 CEST
## Question
`pv1-1` became the PVE cluster `homelab`, and a second node `pv1-2` joined it. Does the CAPI/CAPMOX
configuration need changes to work against the cluster?
## What was checked
- PVE API (read-only GETs with the `capmox@pve!capi` token, 2026-09-29): `/cluster/status`,
`/nodes`, `/storage`, `/nodes/<n>/network`, `/nodes/<n>/storage`, `/cluster/resources`,
`/access/acl`.
- CAPMOX v0.9.1 source (Go proxy zip): scheduler, clone path, template lookup,
`templates/cluster-template.yaml`.
- PVE `qemu-server` `PVE/API2/Qemu.pm` (HEAD, not pinned to a PVE release): the clone target check.
## Findings
PVE state:
| Item | pv1-1 | pv1-2 |
| --- | --- | --- |
| IP | 192.168.0.8 | 192.168.0.10 |
| CPU / RAM | 16 / 29.2 GiB | 12 / 40.9 GiB |
| `vmbr0` VLAN-aware | yes | **no** |
| `local-lvm` | local LVM-thin | local LVM-thin (not shared) |
| Templates | 101 `ubuntu-2404-kube-v1.35.9`, 9000, 9001 | none |
- Storage definitions (`local`, `local-lvm`, `proxy-seeds`) are cluster-wide without a `nodes`
restriction, but none of them is shared.
- The `capmox@pve` ACLs (`/`, `/storage`, `/sdn/zones/localnetwork`) are cluster-wide, so they
cover `pv1-2` as well. There are no SDN zones.
CAPMOX v0.9.1:
- **Node choice.** If the ProxmoxMachine has its own `allowedNodes`, that list replaces the
ProxmoxCluster's (`internal/service/scheduler/vmscheduler.go:52-62`).
- The scheduler balances by how many machines of this cluster are already on each node
(`status.nodeLocations`), and only picks a node where the requested memory fits
(`vmscheduler.go:75-119`).
- `memoryAdjustment` is applied per node, against that node's own total memory.
- With empty `allowedNodes` the scheduler isn't called and the VM clones on the source node
(`internal/service/vmservice/vm.go:477`).
- **Clone.** `target` goes to the PVE clone call as-is (`pkg/proxmox/goproxmox/api_client.go:84-95`).
- PVE refuses a cross-node clone when any source disk is on non-shared storage:
`can't clone VM to node '<target>' (VM uses local storage)`.
- Setting `full: true` or `storage` does not change that.
- v0.9.1 has no `localStorage` option.
- **`templateSelector`** searches the whole PVE cluster and needs exactly one match
(`api_client.go:149-212`). "One template per node with the same tags" fails with
`found 2 VM templates`.
- **API URL and credentials.** Everything goes through one API URL; nothing in the credentials
names a node.
- Cross-node requests should be forwarded by PVE (standard PVE behaviour, not checked in code).
- `192.168.0.8` is `pv1-1`, so a down `pv1-1` also means no API for CAPMOX.
- **Bridge.** `bridge`/`vlan` are written into `netX` without any check (`vm.go:338`).
Conclusion:
- Nothing has to change for the current setup to keep working. `lab1` pins `allowedNodes: [pv1-1]`
and `sourceNode: pv1-1`, which is decision #7.
- Using `pv1-2` requires two things:
1. Make `vmbr0` on `pv1-2` VLAN-aware and trunk VLAN 4 on its switch port.
2. Either put a template copy on `pv1-2`, or add shared storage.
- With a template copy per node:
- Workers get one ProxmoxMachineTemplate and one MachineDeployment per PVE node, each with
`sourceNode`, `templateID` (the node's own VMID) and `allowedNodes: [<node>]`. For
cluster-autoscaler that is one node group per PVE node.
- KubeadmControlPlane takes a single infrastructureRef, so the control plane stays on one node.
## Open questions
- How to get the template onto `pv1-2`: a second image-builder run (`PROXMOX_NODE=pv1-2`), or
`qm clone` + `qm migrate` / vzdump-restore? The image-builder run is reproducible; the copy is
faster.
- Is shared storage (NFS) worth adding instead? It gives one MachineDeployment spread over both
nodes, and the control plane can use both nodes too.
- Behaviour when a cross-node clone fails: the scheduler may keep picking the same node. This is
inferred from the code, not tested.
## Follow-ups
- The owner chose: no shared storage for now; copy template 101 to `pv1-2` as 9101; keep
`lab1-workers` and add `lab1-workers-pv1-2`. Plan:
[2026-09-29-2315-pv1-2-workers](../plans/2026-09-29-2315-pv1-2-workers.md). It covers the bridge
fix, the template copy, the second MD, the CA limits and a decision row superseding #7.
- Update the "Lab facts" in CLAUDE.md/AGENTS.md: PVE cluster `homelab`, `pv1-2` at 192.168.0.10.