kube-lineage is a CLI tool for visualizing Kubernetes object relationships. It allows users to view all dependents or dependencies of a given Kubernetes object so that they can better understand how objects in a cluster are related to each other.
$ kubectl lineage clusterrole/system:metrics-server
NAMESPACE NAME READY STATUS AGE
ClusterRole/system:metrics-server - 5m
└── ClusterRoleBinding/system:metrics-server - 5m
kube-system └── ServiceAccount/metrics-server - 5m
kube-system ├── Pod/metrics-server-7b4f8b595-8m7rz 1/1 Running 5m
kube-system │ └── Service/metrics-server - 5m
│ ├── APIService/v1beta1.metrics.k8s.io True 5m
kube-system │ └── EndpointSlice/metrics-server-wb2cm - 5m
kube-system └── Secret/metrics-server-token-nqw85 - 5m
kube-system └── Pod/metrics-server-7b4f8b595-8m7rz 1/1 Running 5m
You can install kube-lineage as a kubectl plugin using the krew plugin manager.
kubectl krew install lineage
kubectl is the primary tool that most users use to interact with their Kubernetes cluster. Since it doesn’t have a built-in command to display related Kubernetes objects in a cluster, understanding how each them are related to one another isn’t straightforward task.
Just a few months ago, I discovered kubectl-tree — a kubectl plugin that helps to visualize object ownership in a similar manner to the tree command found in most operating systems.
$ kubectl tree deployment traefik
NAMESPACE NAME READY REASON AGE
kube-system Deployment/traefik - 14d
kube-system └─ReplicaSet/traefik-5dd496474 - 14d
kube-system └─Pod/traefik-5dd496474-cr6d8 True 14d
It allows users to view all the dependents of a given Kubernetes object with a single command. The tool was a game changer for me as I discovered lots of new owner-dependent relationships that I wasn’t aware of:
ControllerRevision being used by DaemonSet & StatefulSet for updates & rollbacks.Lease being used to represent Node heartbeats (see Efficient Node Heartbeats KEP).However I also discovered a lot of new questions that the tool wasn’t able to answer:
“Which resources are referencing this Secret?”
“Which Pods are using this ServiceAccount?”
“Which ServiceAccounts are bound to this ClusterRole?”
“Are there any Ingresses or ValidatingWebhookConfigurations dependent on this Pod?”
Some of the questions could definitely answered by other existing kubectl-plugins, but I was hoping for a single tool to answer all of them & hence I decided to build one myself as I could find any alternatives out there.
I started off with finding whether there were any existing APIs in the Kubernetes control plane that I could use for object relationship discovery. The garbage collector controller was a promising candidate as it tracked the ownership of all objects in the cluster & the data is accessible via an API:
$ kubectl get --raw /debug/controllers/garbagecollector/graph --server=$KUBE_CONTROLLER_MANAGER
strict digraph full {
// Node definitions.
0 [
label="\"uid=dd415bed-dcdb-45fb-ad8e-9c946171cf1c\nnamespace=kube-system\nPod.v1/coredns-7448499f4d-zn8cl\n\""
group=""
version="v1"
kind="Pod"
namespace="kube-system"
name="coredns-7448499f4d-zn8cl"
uid="dd415bed-dcdb-45fb-ad8e-9c946171cf1c"
...
However it wasn’t feasible for the following reasons:
kubectl access.I ended up copying kubectl-tree’s approach, which can be summarized into these four steps:
The main difference in kube-lineage’s implementation is in step #3. Instead of only looking at the .metadata.ownerReferences field of every object to identify owner-dependent relationships, we additionally implement custom logic for each Kubernetes resource type to discover other forms of relationships. For example:
events.v1 object, we look at its involvedobject.uid field & mark the object with matching UID as related to the event object.services.v1 object, we look at its spec.selector field, find all the pods in the service’s namespace that matches the selector & mark all of them as related to the service object.Now that we have the ability to visualize object relationships in the cluster, I had a new question that I wanted to answer regarding Helm, the de facto package manager for Kubernetes:
“What are the list of resources associated to a specific Helm release?”
Since a Helm release isn’t an actual Kubernetes resource but a construct that exists only in Helm, we needed either a new input format or flag for our command to specify a Helm release instead of a Kubernetes object.
One of the design goal that I had from the start was to reduce the initial learning curve for users, hence I intentionally made the input formats & flags of the command as similar to kubectl get as possible to. To avoid overloading the existing command with a new input format or flag(s), a separate helm subcommand was created for this feature instead.
The helm subcommand uses a rather similar implementation:
In Helm 3, we can obtain the list of associated Kubernetes objects via the generated manifest for a given release. For those that aren’t familiar with Helm terminology:
A manifest is a YAML-encoded representation of the Kubernetes resources that were generated from a release’s chart(s). If a chart is dependent on other charts, those resources will also be included in the manifest.
Getting the manifest of release is just a single step via the helm CLI tool:
$ RELEASE_NAME="kube-state-metrics"
$ helm get manifest $RELEASE_NAME
---
# Source: kube-state-metrics/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
helm.sh/chart: kube-state-metrics-3.5.2
app.kubernetes.io/managed-by: Helm
...
By default, this release information is stored as a Secret object in the namespace of the release. With a bit of extra effort, we can also extract the manifest via kubectl & a few other commonly used CLI tools:
$ RELEASE_SECRET_NAME="sh.helm.release.v1.kube-state-metrics.v1"
$ kubectl get secret $RELEASE_SECRET_NAME -o json \
| jq ".data.release" -r \
| base64 -d | base64 -d | gzip -d \
| jq ".manifest" -r
---
# Source: kube-state-metrics/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/name: kube-state-metrics
helm.sh/chart: kube-state-metrics-3.5.2
app.kubernetes.io/managed-by: Helm
...
Fortunately Helm has a Go package (helm.sh/helm/v3/pkg/action) to obtain the manifest of a specific release, so there wasn’t a lot of extra effort involved on my end to implement this portion of the feature.
Finally here’s how we can use kubectl lineage helm to view the list of Kubernetes objects associated to a release of a kube-state-metrics Helm chart:
$ RELEASE_NAME="kube-state-metrics"
$ kubectl lineage helm $RELEASE_NAME --depth=1
NAMESPACE NAME READY STATUS AGE
monitoring kube-state-metrics True Deployed 5m
├── ClusterRole/kube-state-metrics - 5m
├── ClusterRoleBinding/kube-state-metrics - 5m
monitoring ├── Deployment/kube-state-metrics 1/1 5m
monitoring ├── Secret/sh.helm.release.v1.kube-state-metrics.v1 - 5m
monitoring ├── Service/kube-state-metrics - 5m
monitoring └── ServiceAccount/kube-state-metrics - 5m
I’m currently exploring the idea of going beyond native Kubernetes resources by discovering object relationships for custom resources from incubating or graduated CNCF projects. Some possible examples:
Application from Argo’s Argo CD.HelmRelease from Flux’s Helm Controller.ScaledObject, ScaledJob & TriggerAuthentication from KEDA.I would love to get feedback on how to improve kube-lineage. If you have any feature suggestion or like to see support for certain custom resource(s), do feel free to create an issue on the project’s GitHub issue page or reach out to me on Twitter! 🙂