Files
kacerrandClaude Opus 5.5 6ef55778ff lab1 workers on both PVE nodes: per-node template + MD, balanced CA
PVE is now the 2-node cluster homelab (pv1-1 + pv1-2), no shared storage:
- template 101 copied to pv1-2 as 9101; MDs lab1-workers-pv1-1 (max 6) and
  lab1-workers-pv1-2 (max 12), PMTs pinned with allowedNodes; pool .31-.50;
  lab1-workers renamed for symmetry (decision #18, #7 superseded)
- CA: balancing-label node.cluster.x-k8s.io/pool (+ pool label, capacity labels
  with os/arch, maxPods on the MDs), scan-interval 1m, v 2, default QPS (#19).
  Scale test: 16 pods -> 4 + 4 across both nodes.
- runbook, image-builder copy procedure, fixes tables, future task to report
  three CA clusterapi bugs upstream, lab facts, changelog, execution log

Plan: docs/plans/2026-09-29-2315-pv1-2-workers.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:07:17 +02:00

223 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Execution log: lab1 workers on both PVE nodes (template copy per node)
Pairs with [docs/plans/2026-09-29-2315-pv1-2-workers.md](../plans/2026-09-29-2315-pv1-2-workers.md).
## Status
- [x] Step 0 — Record the plan
- [x] Step 1 — `vmbr0` on `pv1-2` VLAN-aware (owner)
- [x] Step 2 — Template 9101 on `pv1-2` (owner)
- [x] Step 3 — Second worker MachineDeployment `lab1-workers-pv1-2`
- [x] Step 4 — Scale test across both nodes
- [x] Step 5 — Docs and history
## Step 0 — Record the plan (2026-09-29 23:15 CEST)
Plan and this log committed on their own, together with the
[session note](../sessions/2026-09-29-2311-pve-two-node-cluster.md) that motivated them.
Starting state (read-only PVE API):
- cluster `homelab`: `pv1-1` 192.168.0.8, `pv1-2` 192.168.0.10
- `vmbr0` on `pv1-2` has no `bridge_vlan_aware`
- template 101 is only on `pv1-1`, with disks `scsi0` + `efidisk0`, both on `local-lvm`
- `nextid` is 103
## Steps 1–2 — bridge and template on `pv1-2` (owner, verified 2026-09-30 08:38 CEST)
The owner made `vmbr0` VLAN-aware on `pv1-2` (`/etc/network/interfaces`, then `ifreload -a`):
```
bridge-vlan-aware yes
bridge-vids 2-10
```
This deviates from the plan: `bridge-vids` is `2-10`, not `2-4094`. That's enough for VLAN 4, and
any VLAN above 10 on `pv1-2` would need the range widened first.
Template copy, run on `pv1-1`. A cross-node clone is refused from `local-lvm`, so the copy is a
clone on the same node followed by an offline migration:
```bash
qm clone 101 9101 --full 1 --name ubuntu-2404-kube-v1.35.9 --storage local-lvm
qm migrate 9101 pv1-2 --with-local-disks
ssh root@192.168.0.10 qm template 9101
```
The PVE API shows 9101 on `pv1-2` with `template: 1`, and `scsi0`/`efidisk0` as
`local-lvm:base-9101-disk-*`.
Smoke test ([image-builder-proxmox.md](../image-builder-proxmox.md#smoke-test-of-the-template)) on
`pv1-2`, with `TPL=9101 ID=9901`: the VM got a VLAN 4 DHCP address through the guest agent, and was
destroyed afterwards. Before running it:
- `/root/jan.pub` had to be copied to `pv1-2` first: `scp ~/.ssh/id_ed25519.pub root@192.168.0.10:/root/jan.pub`.
- The doc's `ID=9001` now collides with `debian-13-template`, and the snippet ends with
`qm destroy $ID`. The snippet now uses 9901 and checks that the ID is free (`a9f5879`).
Worth noting:
- VMIDs are cluster-wide (pmxcfs), which is why the copy needs its own ID. Names don't have to be
unique.
- The copied `efidisk0` reports `size=528K` (the source says 4M). This doesn't matter: the template
boots SeaBIOS.
## Step 3 — second worker MachineDeployment (prepared 2026-09-30, apply pending)
Changes in `configs/capi/lab1.yaml`:
- new `MachineDeployment/lab1-workers-pv1-2` and `ProxmoxMachineTemplate/lab1-worker-2g-pv1-2`
(`sourceNode: pv1-2`, `templateID: 9101`, `allowedNodes: [pv1-2]`)
- `ProxmoxCluster.allowedNodes` stays `[pv1-1]` for the control plane
- header hand edits #10 and #11
Deviation from the plan: the IP pool is enlarged and the max sizes are raised (owner's request,
"20 nodes"):
- `ipv4Config.addresses` `192.168.4.31-192.168.4.39` → `192.168.4.31-192.168.4.50`
- `lab1-workers` max 5 → **6**, `lab1-workers-pv1-2` max **12**
- 6 + 12 + 1 CP = 19 of 20 addresses. The spare covers the KCP rollout surge, and CAPMOX pools
hold a freed address for 60 s (`AddressReuseGracePeriodSeconds`, `pkg/kubernetes/ipam/ipam.go:318`).
- An ARP probe from `lab-mgmt` found `.32–.60` silent; `.30` (VIP) and `.31` (CP) share the CP VM's
MAC.
- The owner confirmed `.31–.50` is outside the VLAN 4 DHCP range.
- Changing the pool is safe: the ProxmoxCluster webhook only validates the addresses, and the
controller copies them into the InClusterIPPool on every reconcile (`ipam.go:323-324`).
Sizing:
- `pv1-1`: 6 × 2 GiB within ~17.7 GiB of reservable headroom (160 %).
- `pv1-2`: 12 × 2 GiB of 65.4 GiB reservable (40.9 GiB physical); 48 vCPU on 12 cores, which is
fine for the pause-container `inflate` tests but CPU-bound under real load.
`yq` parse check: 9 documents; both worker PMTs point at the right node, template and `allowedNodes`.
Applied by the owner (`kubectl diff` / `kubectl apply -f configs/capi/lab1.yaml`). The CA
discovered `lab1-workers (max 6)` and `lab1-workers-pv1-2 (max 12)`.
## Step 4 — scale test, first run (2026-09-30 ~06:45 UTC)
The `inflate` Deployment had 16 pods (1500m / 512Mi each, 2 per worker).
- **Result:** 8 workers Ready by 06:47Z.
- `lab1-workers` 0→6: VMIDs 103–108 on `pv1-1`, IPs .34–.39.
- `lab1-workers-pv1-2` 0→2: VMIDs 109–110 on `pv1-2`, IPs .32–.33.
- No `VMProvisionFailed`. `status.proxmoxNode` and providerIDs are consistent. The pool had 9 of
20 addresses in use.
- **Checked with:** the `cluster-inspector` agent (read-only). Root causes came from the source
(`upstream-researcher`, CA tag `cluster-autoscaler-1.35.2`, CAPI v1.12.11, CAPMOX v0.9.1).
Two problems.
**1. Not balanced.** The log shows `Best option to resize: lab1-workers` → `No similar node groups
found` → `0->6 (max: 6)`. Only in the next loop did `lab1-workers-pv1-2` get `0->2`.
- The inspector guessed the `cluster.x-k8s.io/deployment-name` label. The source rules that out:
CA never reads it.
- What the source does show in CA 1.35.2 `cloudprovider/clusterapi`:
- `Labels()` reads `spec.template.spec.metadata.labels`, which doesn't exist in v1beta2
(`clusterapi_unstructured.go:207`). MD template labels therefore never reach the from-zero
template node.
- CAPMOX `ProxmoxMachineTemplate` has no `status`, so `kubernetes.io/os` and `arch` become `""` on
the template (`clusterapi_nodegroup.go:393-405`). A pod with `nodeSelector: kubernetes.io/os:
linux` would not trigger a scale-up from zero.
- The capacity key is `maxPods`; our `maximum-pods` was ignored, and 110 only applied because it
is the default.
- `MixedTemplateNodeInfoProvider` caches a template built from a real node for
`--node-info-cache-expire-time` (default 87600h). A group that has had nodes keeps that
template: ~1.78Gi allocatable, os/arch set, `node-role` label. A from-zero group gets the
synthetic template: 2Gi, empty os/arch. The generic comparator rejects that pair.
- That this cache caused this particular decision is inferred. The reason for a mismatch is only
logged at V(5).
- **Fix (`lab1.yaml` hand edit #12):**
- both MDs carry the template label `node.cluster.x-k8s.io/pool: workers`, which CAPI syncs to the
Node;
- the annotation `capacity.cluster-autoscaler.kubernetes.io/labels:
node.cluster.x-k8s.io/pool=workers,kubernetes.io/os=linux,kubernetes.io/arch=amd64`;
- `maximum-pods` → `maxPods`;
- CA `balancing-label: node.cluster.x-k8s.io/pool`, which compares only that label and turns off
the resource checks.
**2. Loop 8–26 s, throttled.**
- The log is full of `Waited before sending request … client-side throttling` on `/api`, `/apis`
and `/apis/infrastructure.cluster.x-k8s.io/v1alpha2` against the lab-mgmt API.
- Cause:
- For every MD at 0 replicas, `CanScaleFromZero` → `readInfrastructureReferenceResource` →
`getKindPreferredVersion` runs through the provider's **uncached** discovery client. The cached
one is built at `clusterapi_provider.go:195` but not passed to the controller.
- That is 3 GETs per MD per `NodeGroups()` call, and `NodeGroups()` runs about 15× per loop.
- The client uses QPS 5 / burst 10 (`--kube-client-qps` / `--kube-client-burst` defaults).
- There is no upstream fix up to 1.36.1 or master (2026-09-29).
- The owner rejected raising QPS (50/100) and wants fewer requests instead. Fix in
`configs/cluster-autoscaler/values.yaml`:
- `scan-interval: 1m` (was 10s). `loop/trigger.go:74-100`: unschedulable pods and finished
scale-ups/downs still start a loop immediately. Only idle loops are spaced out, so scale-down
takes up to about +1 min.
- `v: 2` (chart default 4). The throttling and "discovered node group" lines are V(3)/V(4).
- Considered and not taken:
- `min-size: 1`: no MD at 0 means no discovery, but two idle VMs.
- `CAPI_VERSION=v1beta1`: v1beta1 refs carry `apiVersion`, so no discovery. But v1beta1 is
deprecated and the path is untested with CAPMOX.
- An upstream patch to pass the cached discovery client (a future task).
## Rename of the pv1-1 group (owner's request, 2026-09-30)
This deviates from the plan: the plan kept `lab1-workers`. The owner wants symmetric per-node
names now that node names show the MD name (`lab1-workers-zldvn-*` next to
`lab1-workers-pv1-2-nlv6s-*`).
Changes in `configs/capi/lab1.yaml` (hand edit #13):
- `MachineDeployment/lab1-workers` → `lab1-workers-pv1-1`.
- `ProxmoxMachineTemplate/lab1-worker-2g` → `lab1-worker-2g-pv1-1`, now with explicit
`allowedNodes: [pv1-1]`.
A rename creates new objects. `kubectl apply` doesn't prune, so the owner deletes the old MD and PMT
once the old MD has scaled to 0. The timing is right because the CA was already scaling every worker
down (`inflate` at 0). The new MD comes up with 0 replicas: `replicas` is unset, and the CAPI webhook
defaults it to the autoscaler min-size annotation.
## Step 4 — re-test after the fixes (2026-09-30 07:23 UTC)
The owner applied the changes in this order:
1. `kubectl apply -f configs/capi/lab1.yaml`: new `lab1-workers-pv1-1` + `lab1-worker-2g-pv1-1`, the
pool label, `maxPods`, os/arch.
2. Deleted the old `MachineDeployment/lab1-workers` and `ProxmoxMachineTemplate/lab1-worker-2g`.
3. `helm upgrade --install ca … --version 9.59.0 -f configs/cluster-autoscaler/values.yaml`:
`scan-interval 1m`, `v 2`, `balancing-label`.
4. Scaled `inflate` 0 → 16.
The CA log (`--since=15m`):
```
orchestrator.go:752] Splitting scale-up between 2 similar node groups: {…/lab1-workers-pv1-2, …/lab1-workers-pv1-1}
orchestrator.go:263] Final scale-up plan: [{…/lab1-workers-pv1-2 0->4 (max: 12)} {…/lab1-workers-pv1-1 0->4 (max: 6)}]
```
- **Placement:** ProxmoxMachines `lab1-workers-pv1-1-*` on `pv1-1` (VMIDs 103, 104, 107, 110) and
`lab1-workers-pv1-2-*` on `pv1-2` (105, 106, 108, 109).
- **Nodes:** all 8 Ready about 1–1.5 min after creation, with labels
`node.cluster.x-k8s.io/pool=workers` and `kubernetes.io/os=linux`.
- **Loop timing:** idle loops now run on the `1m0s timer`.
- **One transient warning:** `W clusterstate.go:510 Failed to find acceptable ranges for
MachineDeployment/default/lab1-workers-pv1-1`, once, in the loop during which the new MD was
created. Acceptable ranges are snapshotted at the start of the loop, and the group appeared in a
later `NodeGroups()` call. Harmless; it did not repeat.
Worth noting:
- `ProxmoxMachineTemplate/lab1-worker` (4 GiB, decision #15) is still in the cluster, and no MD
references it. Deleting it is left to the owner.
## Step 5 — docs and history (2026-09-30 09:28 CEST)
- `docs/decisions.md`: #7 → `superseded by #18`. #18 (template copy + MD per PVE node, pool
`.31–.50`, symmetric names) and #19 (CA `balancing-label`, `scan-interval 1m`, `v 2`, QPS left at
the default) are both `active`, since the lab confirmed them in Step 4.
- `docs/phase-1-proxmox-capi.md`:
- intro and topology (PVE cluster, templates, both MDs, pool);
- Step 0: bridge on every node, per-node template;
- Step 5: annotations incl. `maxPods`, `labels`, the pool label;
- Step 6: the settings for two groups, and what a balanced scale-up logs;
- troubleshooting: 7 rows (maxPods, empty os/arch, no balancing, throttling, the
acceptable-ranges warning, cross-node clone, VLAN on a new node).
- `docs/image-builder-proxmox.md`: section "Template on other PVE nodes"; the result line names
101/9101.
- `configs/capi/clusterctl.yaml.example`: comments on `TEMPLATE_VMID` / `ALLOWED_NODES`.
- `docs/future-tasks.md`: report the three CA clusterapi bugs upstream. The namespace task now uses
the new MD names.
- `README.md` (lab inventory, key decision 7), `CLAUDE.md` / `AGENTS.md` (lab facts):
PVE cluster, `pv1-2`, template 9101.
- `.claude/settings.json`: two inspector allowlist rules `md lab1-workers` → `md lab1-workers-pv1-1`.
- `CHANGELOG.md`: entry added after the owner confirmed the setup works (2026-09-30 11:05 CEST). The owner's pending #16/#17 work went in first as its own commit (`5e5b6c5`).