004
kubectl-fleet
A kubectl plugin for multi-cluster operations. Fans queries out across every kubeconfig context in parallel and merges the results into one context-aware table: incident triage, rollout parity checks, and fleet-wide audits without juggling terminal tabs.
- Origin
- Open Source
- Stack
- Goclient-goCobraGoReleaser
- Release
- v0.1.1
- Licence
- Apache-2.0
# Three kind clusters, three real kubeconfig contexts.
# Nothing here is merged ahead of time.
$ ./bin/kubectl-fleet contexts --filter '^kind-fleet-demo-' --check
CONTEXT CLUSTER NAMESPACE AUTH REACHABLE VERSION LATENCY ERROR
kind-fleet-demo-a kind-fleet-demo-a kind-fleet-demo-a yes v1.36.1 4.106625ms
kind-fleet-demo-b kind-fleet-demo-b kind-fleet-demo-b yes v1.36.1 4.956541ms
kind-fleet-demo-c kind-fleet-demo-c kind-fleet-demo-c yes v1.36.1 4.401833ms
# Reachability is a live probe of each API server, not a cached list.
$ ./bin/kubectl-fleet status --contexts '^kind-fleet-demo-' --since 10m
CONTEXT VERSION NODES PODS PENDING CRASHLOOP RESTARTS_10m0s
kind-fleet-demo-a v1.36.1 1/1 11 0 0 0
kind-fleet-demo-b v1.36.1 1/1 12 0 1 1
kind-fleet-demo-c v1.36.1 1/1 12 0 0 0
# One cluster is unhealthy. The fleet view is how you noticed.
$ ./bin/kubectl-fleet get deploy -n payments --contexts '^kind-fleet-demo-'
CONTEXT NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
kind-fleet-demo-a payments api 2/2 2 2 5m
kind-fleet-demo-b payments api 2/2 2 2 5m
kind-fleet-demo-b payments crashy 0/1 1 0 5m
kind-fleet-demo-c payments api 3/3 3 3 5m
# One table, three API servers, queried in parallel. The gap this fills
kubectl is built around one cluster at a time. That assumption is invisible
until the estate has a hundred and fifty of them, and then it is the whole
problem.
The question during an incident is almost never “what is happening in this
cluster”. It is “which clusters are affected”, “is this version everywhere”, or
“did that rollout land in all three regions”. Answering any of those with stock
tooling means a for loop over contexts, output that arrives interleaved, and
no column telling you which cluster a row came from. Most people end up with a
terminal tab per cluster and their own eyes as the join.
$ kubectl fleet status --contexts '^prod-'
CONTEXT VERSION NODES PODS PENDING CRASHLOOP
prod-eu-1 v1.31.2 6/6 142 0 0
prod-us-1 v1.31.2 6/6 138 2 1
prod-ap-1 v1.30.6 4/4 97 0 0
One command, one table, and the version drift on prod-ap-1 is visible without
reading anything carefully.
How it works
Contexts are selected by regex against the kubeconfig, queried in parallel with
client-go, and merged into one table with a leading CONTEXT column so
identically named resources across clusters line up.
The plugin plumbs genericclioptions.ConfigFlags, so the standard kubeconfig
flags behave exactly as they do in kubectl: --kubeconfig, --context,
--namespace, --user, --cluster, --token, --server. KUBECONFIG is
honoured including colon-separated multi-file merges. Nothing about the mental
model changes, which is most of why it gets used.
Three commands do the work:
kubectl fleet contextslists contexts, with an optional parallel probe of each one’s/versionendpoint reporting reachability and latency.kubectl fleet getis a parallelkubectl getacross the selected contexts.kubectl fleet statusis a per-cluster health snapshot: node readiness, pod counts, server version, and the three noisiest namespaces by non-Running pods.
Everything honours -o table, wide, json and yaml, so a fleet-wide
question can be piped into jq:
kubectl fleet status -o json | jq '.[] | select(.PodsCrashLoop > 0)'
The decision that matters most
A failed cluster never aborts the run. Per-context errors land in the table
with <error> in the name column and a warn: line on stderr; every other
cluster still answers.
This sounds like an implementation detail and it is the difference between a tool that is usable during an incident and one that is not. Something is always unreachable across a fleet that size. A tool that exits non-zero on the first timeout is a tool that stops working precisely when a cluster is down, which is the only time anyone reaches for it.
Distribution
Released through GoReleaser: multi-arch archives for linux, darwin and windows
across amd64 and arm64, with checksums attached to the GitHub release and notes
grouped from conventional commit messages. go install and a build from source
both work.
Krew is deliberately deferred until the plugin is feature-complete. Submitting
to krew-index early means committing to an interface before it has been used
enough to know whether it is the right one.