homelab/build/config/ansible/inventory/group_vars/k3s_cluster.yml
Russell Seymour 1250c9cef6
Some checks are pending
deploy / deploy (push) Waiting to run
Initial checkin of code for managing homelab
2026-08-24 20:52:36 +01:00

205 lines
10 KiB
YAML

---
# Shared config for the whole k3s_cluster group (both k3s_control_plane and
# k3s_workers) — see roles/k3s_node and playbooks/k3s.yml.
# Used as the kubeconfig context/cluster name and as the Vault path suffix
# below, so the cluster has one name in every place it shows up.
k3s_cluster_name: homelab-utils
# Pinned, not "latest" — a rebuild months from now should produce the same
# cluster it does today, same reasoning as pinning an app's image tag in
# common/vars.yml. Bump deliberately; check the current stable release at
# https://github.com/k3s-io/k3s/releases first.
k3s_version: v1.31.4+k3s1
# One Vault path for the whole cluster, same "one path per stack" convention
# as everything under homelab/<app> — see docs/vault-secrets.md.
k3s_vault_path: "homelab/k3s-{{ k3s_cluster_name }}"
# The SSH key this group is reached with. A different path from k3s_vault_path
# above because it's a different kind of secret: that one is cluster identity
# (the join token), this one is access to the hosts, and it lives alongside
# the Unraid key under homelab/ci/ rather than with the cluster's own data.
# Fetched by playbooks/k3s_ssh_key.yml, which both k3s playbooks import.
k3s_ssh_key_vault_path: homelab/ci/ssh-k3s
# Where that play writes it, and what every play here then connects with — so
# a manual run needs Vault credentials in the environment and nothing else,
# rather than the key pre-loaded into ssh-agent. Override on the command line
# (-e ansible_ssh_private_key_file=~/.ssh/k3s_ansible) to use a local copy
# instead, e.g. if Vault itself is down.
#
# Under local/, which .gitignore already excludes — same as the kubeconfig
# below, and for the same reason: generated credential, never committed. Built
# from playbook_dir rather than repo_root, unlike k3s_kubeconfig_local_path:
# repo_root is a *play* var set in k3s.yml, so it only resolves inside the two
# plays that set it, whereas this is a connection var and has to resolve in
# every play in the file.
k3s_ssh_key_local_path: "{{ playbook_dir }}/../../../../local/k3s/{{ k3s_cluster_name }}.key"
ansible_ssh_private_key_file: "{{ k3s_ssh_key_local_path }}"
# Extra INSTALL_K3S_EXEC args appended on every node, both roles. Empty by
# default. Only flags valid on *both* `k3s server` and `k3s agent` belong
# here — see k3s_server_extra_args below for server-only flags like
# `--disable`, which `k3s agent` doesn't understand and will fail to start
# with.
k3s_extra_args: []
# Extra INSTALL_K3S_EXEC args appended on the control-plane node only.
# --disable=servicelb turns off k3s's bundled LoadBalancer controller
# (Klipper) in favour of roles/k3s_metallb — the two would otherwise both
# try to satisfy the same LoadBalancer Services. Add e.g. '--disable=traefik'
# here too if homelab-utils apps end up wanting their own ingress instead.
#
# Changing this (or k3s_extra_args) on an already-installed cluster is picked
# up on the next `ansible-playbook playbooks/k3s.yml` run — roles/k3s_node
# tracks the exec line it last installed with and reinstalls (restarting the
# k3s/k3s-agent service, not the node) whenever it drifts from what's
# configured here. See tasks/server.yml and tasks/agent.yml.
k3s_server_extra_args:
- "--disable=servicelb"
# MetalLB's LoadBalancer IP pool — see roles/k3s_metallb. Must be addresses
# on the LAN (192.168.50.0/24 — see inventory/hosts.yml) that nothing else,
# DHCP included, will ever hand out. Adjust to a range your DHCP scope
# doesn't cover before the first run.
k3s_metallb_address_range: "192.168.50.80-192.168.50.99"
# The LAN address the shared Postgres primary answers on — see
# roles/k3s_postgres, which declares a LoadBalancer Service for it alongside
# CNPG's built-in ClusterIP ones. Pinned rather than auto-assigned because
# the address gets written down elsewhere (an app's vars.yml, a connection
# string in Vault) instead of only being looked up at runtime, so it mustn't
# move when the Service is recreated.
#
# Must be inside the pool above — MetalLB only assigns from its own pools,
# and a request for an address outside them leaves the Service pending
# forever rather than failing loudly. The caveat of pinning out of the same
# pool MetalLB auto-assigns from: it hands unpinned Services the lowest free
# address, so if some other LoadBalancer Service is created before this one
# and takes .80, this Service is the one left pending. Nothing else here
# requests an IP today. If that changes, the fix is a second
# IPAddressPool with autoAssign: false in roles/k3s_metallb reserved for
# pinned addresses, not a different number here.
k3s_postgres_loadbalancer_ip: "192.168.50.81"
# Where playbooks/k3s.yml fetches the kubeconfig to, on the Ansible
# controller. Under local/, which .gitignore already excludes — it's a
# generated credential, never committed.
k3s_kubeconfig_local_path: "{{ repo_root }}/local/k3s/{{ k3s_cluster_name }}.kubeconfig"
# --- cert-manager (roles/k3s_cert_manager) ----------------------------------
#
# TLS for anything this cluster serves over Traefik. See that role's
# defaults/main.yml for why DNS-01 rather than HTTP-01, and what each of
# these does.
# Where Let's Encrypt sends expiry warnings. An ACME account is registered
# against it.
k3s_cert_manager_acme_email: russell.seymour@turtlesystems.co.uk
# >>> SET BEFORE THE FIRST RUN <<<
#
# How cert-manager proves control of the domain, rendered into the
# ClusterIssuer's solvers list as-is. Left empty deliberately — it depends on
# who runs DNS for turtlesystems.uk, which this repo has no way to know.
# roles/k3s_cert_manager fails fast while it's empty, rather than creating an
# issuer that can never satisfy an order.
#
# Cloudflare, as the most common case — the token needs Zone:DNS:Edit on the
# zone, and is stored in Vault (see k3s_cert_manager_vault_path below and
# docs/vault-secrets.md) rather than written here:
#
# k3s_cert_manager_solver:
# dns01:
# cloudflare:
# apiTokenSecretRef:
# name: cert-manager-dns-credentials
# key: CLOUDFLARE_API_TOKEN
# selector:
# dnsZones:
# - turtlesystems.uk
#
# For another provider, take the stanza from
# https://cert-manager.io/docs/configuration/acme/dns01/ and point its secret
# ref at whichever key you stored at that Vault path.
#
# Use the Let's Encrypt staging directory while working this out — override
# k3s_cert_manager_acme_server. Production allows 5 failed validations per
# hostname per hour, and exhausting it means waiting, not retrying.
k3s_cert_manager_solver:
dns01:
cloudflare:
apiTokenSecretRef:
name: cert-manager-dns-credentials
key: CLOUDFLARE_API_TOKEN
selector:
dnsZones:
- turtlesystems.uk
# --- Traefik (roles/k3s_traefik) --------------------------------------------
#
# The ingress controller k3s installs for itself; that role installs nothing,
# it only adjusts what's already there. Its defaults are enough for the
# dashboard over `kubectl port-forward` — see that role's defaults/main.yml
# and README.md → "Dashboard (Traefik)".
# >>> SET WHEN AUTHENTIK IS READY TO PROTECT IT <<<
#
# Uncomment to publish the dashboard on this hostname, behind the forward-auth
# middleware below. Until then the port-forward is the only way in, which is
# the safe default rather than a limitation. Needs a DNS record pointing at
# Traefik's MetalLB address (`kubectl -n kube-system get svc traefik`).
#
# Before uncommenting, in Authentik: create a Proxy Provider in **forward auth
# (domain level)** mode covering turtlesystems.uk, assign it to an
# application, and add that application to the embedded outpost. Domain level
# rather than single-application for a Kubernetes-specific reason — see
# roles/k3s_traefik/defaults/main.yml.
#
# k3s_traefik_dashboard_host: traefik.turtlesystems.uk
# Authentik's embedded outpost, by in-cluster DNS — the `authentik-server`
# Service in the `authentik` namespace, i.e. the chart's own service, not a
# separately deployed outpost. Unused while the host above is commented out;
# set here anyway so enabling the dashboard is one line rather than two.
# roles/k3s_traefik refuses to run if the host is set and this isn't.
k3s_traefik_dashboard_auth_address: "http://authentik-server.authentik.svc.cluster.local/outpost.goauthentik.io/auth/traefik"
# --- Apps on the cluster (roles/k3s_app) ------------------------------------
#
# The k3s equivalent of `stacks:` in host_vars/nas01.yml and `apps:` in a
# Proxmox guest's host_vars — the list of *applications* on this cluster, as
# opposed to the k3s_* cluster services above, which are the cluster's own
# infrastructure.
#
# In group_vars rather than host_vars because an app is deployed to the
# cluster, not to a node: playbooks/k3s.yml runs roles/k3s_app against
# k3s_control_plane only (that's where the manifests directory is), but
# nothing about an app belongs to that Pi specifically.
#
# Same entry shape as the other two platforms — name, src, vault_path, an
# optional `db:`, and an optional `state:`. And the same removal rule as
# Unraid: set `state: absent` to tear an app down and leave the entry here as
# a tombstone; deleting the entry removes nothing, it just stops Ansible
# visiting it. See roles/k3s_app/tasks/remove.yml.
k3s_apps:
- name: authentik
src: authentik
vault_path: homelab/authentik
db:
name: authentik
user: authentik
# The key at homelab/authentik holding this role's password. Named for
# the environment variable Authentik itself reads, because
# roles/k3s_app passes Vault keys through to the app's Secret verbatim
# — so one value serves both the provisioning step and the running app.
password_vault_key: AUTHENTIK_POSTGRESQL__PASSWORD
admin_vault_path: homelab/shared/postgres
# The CNPG LoadBalancer, not the in-cluster -rw Service: these tasks
# run on the Ansible controller (delegate_to: localhost), which is off
# the cluster and can't resolve or route to a ClusterIP. Authentik
# itself uses the ClusterIP — see
# src/authentik/ansible/kubernetes/vars.yml.
provision_host: "{{ k3s_postgres_loadbalancer_ip }}"
provision_port: "5432"