Add workload placement node labels
This commit is contained in:
parent
dfe7bbf4a7
commit
7b0b060a1c
18
README.md
18
README.md
|
|
@ -174,6 +174,24 @@ worker_nodes = {
|
|||
Stateful apps currently pin retained local PVs to the `debian` node. Move or
|
||||
duplicate those PV manifests when you want storage on another node.
|
||||
|
||||
## Workload Placement
|
||||
|
||||
`bootstrap/cluster` labels nodes with homelab placement metadata:
|
||||
|
||||
- `homelab.dev/node-role=control-plane` and `homelab.dev/storage=local` on the
|
||||
Debian control plane
|
||||
- `homelab.dev/node-role=edge-app` and `homelab.dev/storage=local` on the
|
||||
Raspberry Pi worker
|
||||
- `homelab.dev/node-role=app` and `homelab.dev/storage=nvme` on automated Pimox
|
||||
worker clones
|
||||
|
||||
Override `control_plane_node_labels`, `worker_node_labels`,
|
||||
`LAB_RASPBERRY_NODE_LABELS_JSON`, or `LAB_PIMOX_WORKER_NODE_LABELS_JSON` when
|
||||
the physical layout changes. The current website, demos, registry, and Gitea
|
||||
manifests are not moved automatically because the public NodePort path and
|
||||
retained OpenEBS hostpath PVs are node-local. Move workloads only after their
|
||||
storage and edge path are ready on the target node.
|
||||
|
||||
The website and demos NodePorts are reachable from the OCI jump box through the
|
||||
Raspberry Pi Tailscale interface. `bootstrap/cluster` installs a persistent
|
||||
`homelab-tailscale-nodeport.service` on the configured worker to restore the
|
||||
|
|
|
|||
|
|
@ -635,6 +635,69 @@ EOT
|
|||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
control_plane_node_label_pairs = [
|
||||
for label, value in var.control_plane_node_labels : {
|
||||
node_name = var.control_plane_node_name
|
||||
label = label
|
||||
value = value
|
||||
}
|
||||
]
|
||||
|
||||
worker_node_label_pairs = flatten([
|
||||
for worker_key, worker in var.worker_nodes : [
|
||||
for label, value in lookup(var.worker_node_labels, worker_key, {}) : {
|
||||
node_name = worker.node_name
|
||||
label = label
|
||||
value = value
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
node_label_pairs = concat(local.control_plane_node_label_pairs, local.worker_node_label_pairs)
|
||||
}
|
||||
|
||||
resource "null_resource" "node_labels" {
|
||||
for_each = {
|
||||
for pair in local.node_label_pairs : "${pair.node_name}/${pair.label}" => pair
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
null_resource.kubeadm_control_plane,
|
||||
null_resource.kubeadm_worker,
|
||||
]
|
||||
|
||||
triggers = {
|
||||
kubeconfig_path = var.kubeconfig_path
|
||||
node_name = each.value.node_name
|
||||
label = each.value.label
|
||||
value = each.value.value
|
||||
}
|
||||
|
||||
provisioner "local-exec" {
|
||||
interpreter = ["/bin/bash", "-lc"]
|
||||
environment = {
|
||||
KUBECONFIG_PATH = self.triggers.kubeconfig_path
|
||||
NODE_NAME = self.triggers.node_name
|
||||
NODE_LABEL = self.triggers.label
|
||||
NODE_LABEL_VALUE = self.triggers.value
|
||||
}
|
||||
command = <<EOT
|
||||
set -euo pipefail
|
||||
|
||||
for _ in $(seq 1 60); do
|
||||
if kubectl --kubeconfig "$${KUBECONFIG_PATH}" get node "$${NODE_NAME}" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
kubectl --kubeconfig "$${KUBECONFIG_PATH}" get node "$${NODE_NAME}" >/dev/null
|
||||
kubectl --kubeconfig "$${KUBECONFIG_PATH}" label node "$${NODE_NAME}" "$${NODE_LABEL}=$${NODE_LABEL_VALUE}" --overwrite
|
||||
EOT
|
||||
}
|
||||
}
|
||||
|
||||
output "kubeconfig_path" {
|
||||
value = var.kubeconfig_path
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ variable "control_plane_node_name" {
|
|||
default = "debian"
|
||||
}
|
||||
|
||||
variable "control_plane_node_labels" {
|
||||
type = map(string)
|
||||
default = {
|
||||
"homelab.dev/node-role" = "control-plane"
|
||||
"homelab.dev/storage" = "local"
|
||||
}
|
||||
}
|
||||
|
||||
variable "control_plane_advertise_address" {
|
||||
type = string
|
||||
default = "192.168.100.68"
|
||||
|
|
@ -62,6 +70,17 @@ variable "worker_nodes" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "worker_node_labels" {
|
||||
type = map(map(string))
|
||||
|
||||
default = {
|
||||
raspberrypi = {
|
||||
"homelab.dev/node-role" = "edge-app"
|
||||
"homelab.dev/storage" = "local"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "tailscale_nodeport_access" {
|
||||
type = object({
|
||||
enabled = bool
|
||||
|
|
|
|||
21
lab.sh
21
lab.sh
|
|
@ -270,10 +270,12 @@ write_cluster_worker_var_file() {
|
|||
local var_file="$2"
|
||||
|
||||
LAB_INCLUDE_RASPBERRY_WORKER="${LAB_INCLUDE_RASPBERRY_WORKER:-true}" \
|
||||
LAB_RASPBERRY_HOST="${LAB_RASPBERRY_HOST:-192.168.100.89}" \
|
||||
LAB_RASPBERRY_USER="${LAB_RASPBERRY_USER:-jv}" \
|
||||
LAB_RASPBERRY_NODE_NAME="${LAB_RASPBERRY_NODE_NAME:-raspberry}" \
|
||||
LAB_RASPBERRY_SSH_KEY_PATH="${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}" \
|
||||
LAB_RASPBERRY_HOST="${LAB_RASPBERRY_HOST:-192.168.100.89}" \
|
||||
LAB_RASPBERRY_USER="${LAB_RASPBERRY_USER:-jv}" \
|
||||
LAB_RASPBERRY_NODE_NAME="${LAB_RASPBERRY_NODE_NAME:-raspberry}" \
|
||||
LAB_RASPBERRY_SSH_KEY_PATH="${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}" \
|
||||
LAB_RASPBERRY_NODE_LABELS_JSON="${LAB_RASPBERRY_NODE_LABELS_JSON:-{\"homelab.dev/node-role\":\"edge-app\",\"homelab.dev/storage\":\"local\"}}" \
|
||||
LAB_PIMOX_WORKER_NODE_LABELS_JSON="${LAB_PIMOX_WORKER_NODE_LABELS_JSON:-{\"homelab.dev/node-role\":\"app\",\"homelab.dev/storage\":\"nvme\"}}" \
|
||||
python3 - "${spec_file}" "${var_file}" <<'PY'
|
||||
import json
|
||||
import os
|
||||
|
|
@ -281,6 +283,13 @@ import sys
|
|||
|
||||
spec_file, var_file = sys.argv[1:3]
|
||||
nodes = {}
|
||||
node_labels = {}
|
||||
|
||||
try:
|
||||
raspberry_labels = json.loads(os.environ["LAB_RASPBERRY_NODE_LABELS_JSON"])
|
||||
pimox_labels = json.loads(os.environ["LAB_PIMOX_WORKER_NODE_LABELS_JSON"])
|
||||
except json.JSONDecodeError as exc:
|
||||
raise SystemExit(f"Invalid node label JSON: {exc}") from exc
|
||||
|
||||
if os.environ["LAB_INCLUDE_RASPBERRY_WORKER"].lower() not in {"0", "false", "no", "off", "disabled"}:
|
||||
nodes["raspberrypi"] = {
|
||||
|
|
@ -289,6 +298,7 @@ if os.environ["LAB_INCLUDE_RASPBERRY_WORKER"].lower() not in {"0", "false", "no"
|
|||
"node_name": os.environ["LAB_RASPBERRY_NODE_NAME"],
|
||||
"ssh_key_path": os.environ["LAB_RASPBERRY_SSH_KEY_PATH"],
|
||||
}
|
||||
node_labels["raspberrypi"] = raspberry_labels
|
||||
|
||||
with open(spec_file, encoding="utf-8") as handle:
|
||||
for line in handle:
|
||||
|
|
@ -302,9 +312,10 @@ with open(spec_file, encoding="utf-8") as handle:
|
|||
"node_name": node_name,
|
||||
"ssh_key_path": ssh_key_path,
|
||||
}
|
||||
node_labels[key] = pimox_labels
|
||||
|
||||
with open(var_file, "w", encoding="utf-8") as handle:
|
||||
json.dump({"worker_nodes": nodes}, handle, indent=2)
|
||||
json.dump({"worker_nodes": nodes, "worker_node_labels": node_labels}, handle, indent=2)
|
||||
handle.write("\n")
|
||||
PY
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue