Verify a Talos cluster
Check a cluster that runs Talos after a rebuild or an upgrade, from the nodes and Cilium to federation, backups, OpenBao, the sandbox runtimes, the GPU and Falco.
Use this runbook after Rebuild a cluster on Talos, and after a Talos or Kubernetes upgrade. Each section checks one layer, so a failure points at its layer. Run the sections in order.
warning
This procedure changes live state by hand. It is an exception to the GitOps rule because the backup checks and the gVisor check create objects that no manifest declares. Each check deletes its object, or lets it expire.
Before you start#
- Get
kubectlaccess, and the cluster's context in~/.talos/config, as Get cluster admin access describes.
<site> is folly or offsite, and <addr> is a node's address: on folly its address in clusters/folly/inventory/nodes.yaml, the nodes file, and on offsite its entry in NODE_ADDRESSES in clusters/offsite/config/cluster-topology.json.
Check the nodes#
Run the Talos health check against the control plane.
talosctl --context <site> healthResult: Each check ends in
OK.Make sure that each node is
Ready.kubectl --context <site> get nodes -o wideMake sure that each node has the sysctls from
clusters/<site>/talos/patches.tf.talosctl --context <site> -n <addr> get kernelparamstatus | grep -E 'max_user_namespaces|max_user_watches|max_map_count'Result:
11255,524288and1048576.Make sure that local-path uses the
datavolume.kubectl --context <site> -n local-path-provisioner get configmap local-path-config -o jsonpath='{.data.config\.json}'Result: The path
/var/mnt/data.
Check the network#
Read Cilium's status.
kubectl --context <site> -n kube-system exec ds/cilium -- cilium-dbg statusResult:
KubeProxyReplacement: TrueandMasquerading: BPF.Make sure that pod DNS resolves a cluster name and an external name.
kubectl --context <site> -n kata-demo exec deploy/kata-demo -c nginx -- nslookup kubernetes.default.svc.cluster.local kubectl --context <site> -n kata-demo exec deploy/kata-demo -c nginx -- nslookup github.comResult: An address for each name.
Make sure that each node peers with the gateway.
kubectl --context <site> -n kube-system exec ds/cilium -- cilium-dbg bgp peersResult:
establishedfor the gateway. Inspect the UniFi network shows the gateway's side.Make sure that each LoadBalancer Service has an address in
LB_RANGE.kubectl --context <site> get svc -A --field-selector spec.type=LoadBalancerMake sure that the Gateway API works.
kubectl --context <site> get gatewayclass,gateway -AResult: The class
ciliumisAccepted, and each Gateway isProgrammed.
Check identity#
Make sure that the issuer and its key are the published ones.
kubectl --context <site> get --raw /.well-known/openid-configuration | jq -r .issuer kubectl --context <site> get --raw /openid/v1/jwks | jq -r '.keys[].kid' jq -r '.keys[].kid' terraform/pki/oidc/<site>/jwks.jsonResult:
https://oidc.lolwtf.ca/<site>, and the same key ID twice.Comment
atlantis plan -d clusters/folly/bootstrapon a pull request. Atlantis on offsite uses a federated token for folly.Result: A plan, with no
Unauthorized.On folly, make sure that OpenBao unsealed with its KMS key.
kubectl --context folly -n vault exec vault-openbao-0 -- bao statusResult:
Sealed false.Make sure that a pod verifies the API server with its own CA, that the CA is the pin, and that it is the only client CA.
kubectl --context <site> -n kata-demo exec deploy/kata-demo -c nginx -- curl -s -o /dev/null -w '%{http_code}\n' \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://kubernetes.default.svc/version kubectl --context <site> -n kube-system get cm kube-root-ca.crt -o jsonpath='{.data.ca\.crt}' | diff - clusters/<site>/config/kubernetes-ca.pem kubectl --context <site> -n kube-system get cm extension-apiserver-authentication -o jsonpath='{.data.client-ca-file}' | grep -c 'BEGIN CERT'Result:
401, nodiffoutput, and1.On folly, make sure that the kthx engine reaches the API server after its restart, and that Rowbutt can run
kubectl --context folly get nsin a sandbox.kubectl --context offsite -n spindrift logs deploy/spindrift-reconciler --since 1h | grep -ci x509Result:
0.
note
The API server allows TLS 1.3 only, and anonymous /version returns 401. No client is known that needs either.
Check the backups#
<namespace> holds a pod with a backup.velero.io/backup-volumes annotation, such as jellyfin on folly.
Take the first etcd snapshot.
kubectl --context <site> -n backups create job --from=cronjob/etcd-snapshot etcd-snapshot-first kubectl --context <site> -n backups wait --for=condition=complete job/etcd-snapshot-first --timeout=10mResult:
job.batch/etcd-snapshot-first condition met. The Job expires after seven days.Make sure that Velero backs up a pod volume from the kubelet root, then delete the backup.
velero backup create verify-talos --include-namespaces <namespace> --wait --kubecontext <site> velero backup describe verify-talos --details --kubecontext <site> velero backup delete verify-talos --confirm --kubecontext <site>Result: A
Pod Volume Backupslist with the phaseCompleted.
Check the workloads#
Make sure that a
kata-clhpod runs its own kernel.kubectl --context <site> -n kata-demo exec deploy/kata-demo -c nginx -- uname -rResult: A kernel version without
-talos.
note
The kata-clh overhead in clusters/base/cluster-runtimeclass.yaml is a NixOS measurement. Compare it with the cloud-hypervisor and virtiofsd memory in talosctl -n <addr> processes.
Start a gVisor pod, read its kernel, and delete it.
kubectl --context <site> -n default run gv --image=nginx:alpine --overrides='{"spec":{"runtimeClassName":"gvisor"}}' kubectl --context <site> -n default wait --for=condition=Ready pod/gv kubectl --context <site> -n default exec gv -- uname -r kubectl --context <site> -n default delete pod gvResult: A kernel version that ends in
-gvisor.Make sure that no pod fails Pod Security.
kubectl --context <site> get events -A | grep -i 'violates PodSecurity'Result: No output.
On folly, make sure that riptide offers its GPU, and that jellyfin runs on riptide.
kubectl --context folly get node riptide -o jsonpath='{.status.allocatable.gpu\.intel\.com/i915}' kubectl --context folly -n jellyfin get pods -o wideResult:
1, and aRunningjellyfin pod on riptide.Play a video in jellyfin that needs a transcode. Make sure that the dashboard shows hardware transcoding.
Make sure that Falco runs on each node.
kubectl --context <site> -n falco get pods -o wideResult: One
Runningpod for each node.Make sure that the kthx engine's releases on the cluster are ready.
kubectl --context <site> -n kthx-apps get hrResult: Each row is
True. After a rebuild, each release from the freeze in Move a cluster's data through a Talos rebuild is there.
If something goes wrong#
| Symptom | Cause | Action |
|---|---|---|
cilium-dbg bgp peers shows no session. | The node lacks the bgp-enabled label, or the gateway has no neighbor for it. | Read the node's labels and terraform/network/unifi/. |
No cilium GatewayClass. | Cilium was installed before the Gateway API CRDs, so its chart did not render the class. | Run flux --context <site> reconcile hr cilium -n kube-system --force, then kubectl --context <site> -n kube-system rollout restart deploy/cilium-operator. |
The federated plan shows x509. | clusters/<site>/config/kubernetes-ca.pem holds the old CA, or the pull request predates it. | Pin the new CA, as Make a Talos secrets bundle describes, or rebase the pull request. |
The etcd snapshot Job fails in snapshot. | The Talos API refuses the role, or the Secret etcd-snapshot-talos is missing. | Read kubectl -n backups get serviceaccounts.talos.dev etcd-snapshot-talos -o yaml for its status. |
| A sandbox pod fails only on Talos. | Workload isolation. | Read talosctl -n <addr> logs sandboxd. |
bao status shows Sealed true. | The KMS call failed. | Read the OpenBao pod's logs for the GCP error. |
Related#
Linked from 4
- RunbooksStep-by-step procedures for the hosts, clusters, network, secrets and apps, grouped by area.
- Install Cilium and Flux on TalosWrite the kubeconfig context of a new Talos cluster, install Cilium with its pod IP pool and BGP peering, and bootstrap Flux.
- Install Talos on a nodeBoot a running NixOS node into the Talos installer with kexec, check it in maintenance mode, and check its volumes after the install.
- Rebuild a cluster on TalosReplace a cluster's NixOS nodes with Talos Linux, bootstrap a fresh etcd, install Cilium and Flux, and restore the data that has a backup.