feat(bootstrap): revive the talos phase, rename the mirror patch #710
@@ -198,7 +198,7 @@ talosctl gen secrets --from-controlplane-config cp.yaml -o secrets.yaml
|
||||
talosctl gen config homelab https://k8s.renner.uno:6443 \
|
||||
--with-secrets secrets.yaml --output-types controlplane \
|
||||
--config-patch @talos/patches/cluster.yaml \
|
||||
--config-patch @talos/patches/registry-mirror.yaml \
|
||||
--config-patch @talos/patches/containerd-layer-retention.yaml \
|
||||
--config-patch @talos/patches/k8s-N.yaml -o install.yaml
|
||||
```
|
||||
|
||||
|
||||
@@ -136,11 +136,18 @@ in `talos/patches/k8s-N.yaml`.
|
||||
|
||||
## Step 2 — talos: form the cluster + get kubeconfig
|
||||
|
||||
**Do not use `kubernetes/bootstrap.sh talos`.** That phase is dead: it fails on
|
||||
a `cilium-cni.yaml` that no longer exists, and before that it failed because it
|
||||
passed the file whole (#581). Nobody has fixed it because nothing needs it —
|
||||
the procedure below is what is actually used. Its other phases — `flux`,
|
||||
`wait-apps` — are still used, in Steps 3, 5 and 6.
|
||||
`kubernetes/bootstrap.sh talos` does this, including the CNI install that used
|
||||
to be a hand-typed step. It was unusable for months because it passed
|
||||
`cilium-cni.yaml` whole and the file's `$patch: delete` document failed against
|
||||
a fresh config (#581); that file no longer exists, so the failure is gone with
|
||||
it.
|
||||
|
||||
Verified as far as it can be without wiping a cluster: `talosctl gen config`
|
||||
with exactly the patches the script passes produces a config carrying
|
||||
`cni: none` and `proxy: disabled` that passes `talosctl validate --mode metal`.
|
||||
The apply-and-bootstrap half has not been exercised since the rewrite. Read the
|
||||
manual procedure below before running the script, so you know what it is doing
|
||||
and can take over at any step if it stops.
|
||||
|
||||
The procedure below is what actually works, and is the same one
|
||||
`RUNBOOK-baremetal-conversion.md` uses.
|
||||
@@ -160,7 +167,7 @@ for N in 1 2 3; do
|
||||
talosctl gen config homelab https://k8s.renner.uno:6443 \
|
||||
--with-secrets secrets.yaml --output-types controlplane \
|
||||
--config-patch @talos/patches/cluster.yaml \
|
||||
--config-patch @talos/patches/registry-mirror.yaml \
|
||||
--config-patch @talos/patches/containerd-layer-retention.yaml \
|
||||
--config-patch @talos/patches/k8s-$N.yaml -o install-k8s-$N.yaml
|
||||
done
|
||||
```
|
||||
@@ -224,6 +231,9 @@ place it is configured, and this installs from those same values so the cluster
|
||||
comes up on what git describes rather than on a second, drifting copy. See
|
||||
PROPOSAL-cilium-single-owner.md for why it works this way.
|
||||
|
||||
`bootstrap.sh talos` already did this. What follows is the same thing by hand,
|
||||
for when the script stopped partway or you are not using it.
|
||||
|
||||
Every input is read out of `helmrelease.yaml`. Nothing here is a version or a
|
||||
URL you have to keep in step with anything — copy the block as it stands:
|
||||
|
||||
|
||||
+41
-4
@@ -21,6 +21,13 @@ set -euo pipefail
|
||||
|
||||
TALOS_DIR="$(cd "$(dirname "$0")/talos" && pwd)"
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# Cilium's single source of truth. Talos ships `cni: none` (talos/patches/
|
||||
# cluster.yaml) and nothing else, so the CNI has to be installed here, between
|
||||
# `talosctl bootstrap` and Flux — Flux's own controllers are pods and cannot
|
||||
# reconcile the network they need in order to start. Everything the install
|
||||
# needs is read out of this file at run time so there is no second copy of a
|
||||
# version or a value to keep in step. See PROPOSAL-cilium-single-owner.md.
|
||||
CILIUM_HR="$REPO_ROOT/kubernetes/infrastructure/cilium/helmrelease.yaml"
|
||||
ACCESS_STAGING="$REPO_ROOT/kubernetes/access-staging"
|
||||
NODES=(k8s-1 k8s-2 k8s-3)
|
||||
# No dedicated workers since the k8s-edge VM was retired (2026-07-02) —
|
||||
@@ -82,9 +89,11 @@ if phase_runs talos; then
|
||||
echo -e "${YELLOW}=== Phase: Talos bootstrap ===${NC}"
|
||||
command -v talosctl >/dev/null || fail "talosctl not found"
|
||||
command -v kubectl >/dev/null || fail "kubectl not found"
|
||||
command -v helm >/dev/null || fail "helm not found (needed to install the CNI)"
|
||||
command -v yq >/dev/null || fail "yq not found (needed to read the Cilium HelmRelease)"
|
||||
[[ -f "$TALOS_DIR/patches/cluster.yaml" ]] || fail "cluster.yaml patch not found"
|
||||
[[ -f "$TALOS_DIR/patches/cilium-cni.yaml" ]] || fail "cilium-cni.yaml patch not found"
|
||||
[[ -f "$TALOS_DIR/patches/registry-mirror.yaml" ]] || fail "registry-mirror.yaml patch not found"
|
||||
[[ -f "$TALOS_DIR/patches/containerd-layer-retention.yaml" ]] || fail "containerd-layer-retention.yaml patch not found"
|
||||
[[ -f "$CILIUM_HR" ]] || fail "cilium helmrelease not found at $CILIUM_HR"
|
||||
|
||||
step 0 "Generating Talos configs..."
|
||||
TALOS_GEN_DIR=$(mktemp -d)
|
||||
@@ -123,8 +132,7 @@ if phase_runs talos; then
|
||||
if err=$(talosctl apply-config --insecure -n "$node_ip" \
|
||||
--file "$base_config" \
|
||||
--config-patch "@$TALOS_DIR/patches/cluster.yaml" \
|
||||
--config-patch "@$TALOS_DIR/patches/cilium-cni.yaml" \
|
||||
--config-patch "@$TALOS_DIR/patches/registry-mirror.yaml" \
|
||||
--config-patch "@$TALOS_DIR/patches/containerd-layer-retention.yaml" \
|
||||
--config-patch "@$TALOS_DIR/patches/${node}.yaml" 2>&1); then
|
||||
echo " applied via $node_ip ${GREEN}(OK)${NC}"
|
||||
return 0
|
||||
@@ -174,6 +182,35 @@ if phase_runs talos; then
|
||||
done
|
||||
ok
|
||||
|
||||
step 0 "Installing Cilium (CNI)..."
|
||||
# Must run before the node-Ready wait below: with `cni: none` the kubelet
|
||||
# reports NotReady until a CNI is present, so that loop would time out.
|
||||
#
|
||||
# Chart repo, version constraint and values all come out of the HelmRelease
|
||||
# so this cannot drift from what Flux will reconcile minutes later. helm
|
||||
# accepts the semver range verbatim and resolves it the same way Flux does.
|
||||
#
|
||||
# serviceMonitor is the one override, and it is a bootstrap constraint
|
||||
# rather than a value: its CRD arrives with the monitoring stack much
|
||||
# later, and the object would fail to apply. Nothing else is overridden,
|
||||
# which is what lets helm-controller adopt this release instead of
|
||||
# replacing it.
|
||||
CILIUM_VALUES=$(mktemp)
|
||||
yq 'select(.kind == "HelmRelease") | .spec.values' "$CILIUM_HR" > "$CILIUM_VALUES"
|
||||
grep -q 'policyMapMax' "$CILIUM_VALUES" \
|
||||
|| fail "extracted Cilium values look wrong (no policyMapMax) — check $CILIUM_HR"
|
||||
|
||||
helm repo add cilium "$(yq -r 'select(.kind == "HelmRepository") | .spec.url' "$CILIUM_HR")" >/dev/null
|
||||
helm repo update cilium >/dev/null
|
||||
helm upgrade --install cilium cilium/cilium \
|
||||
--version "$(yq -r 'select(.kind == "HelmRelease") | .spec.chart.spec.version' "$CILIUM_HR")" \
|
||||
--namespace kube-system \
|
||||
-f "$CILIUM_VALUES" \
|
||||
--set prometheus.serviceMonitor.enabled=false \
|
||||
--wait --timeout 10m
|
||||
rm -f "$CILIUM_VALUES"
|
||||
ok
|
||||
|
||||
step 0 "Waiting for K8s nodes..."
|
||||
expected=${#ALL_NODES[@]}
|
||||
for i in $(seq 1 60); do
|
||||
|
||||
Reference in New Issue
Block a user