Getting custom output from kubectl with examples

One of the most common ways to interact with the Kubernetes Cluster is using the “Kubectl.” By default, in most cases, the output is returned in tabular format, which is good enough for most cases. However, sometimes if you want more control over the produced output, kubectl provides you following formats:

kubectl get pod -o --help 2>&1 |sed -r 's/.*formats are: //g;s/,/\n/g'
custom-columns
custom-columns-file
go-template
go-template-file
json
jsonpath
jsonpath-as-json
jsonpath-file
name
template
templatefile
wide
yaml

The idea of this post is to provide a starting baseline to extract the various kinds of output using kubectl. The post will provide various examples and awareness to choose the write output format depending on the task. 

Examples:

For the below examples, we will query the Pod/pods status. The examples below are in order of simple toward complex.

1. The Default output, without any output flag( -o flag unused)

kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-with-few-containers 1/3 NotReady 0 29s
simple-pod 1/1 Running 0 53s
web-server 1/2 NotReady 0 7s

2.  Using the wide option (-o wide)

Notice the extra output in the below result. In addition, info such as Pod IP, Scheduled Node, Nominated Node, and Readiness gate state also shows up. 

kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-with-few-containers 1/3 NotReady 0 3m13s 10.233.96.1 kube-worker-2
simple-pod 1/1 Running 0 3m37s 10.233.72.1 kube-worker-3
web-server 1/2 NotReady 0 2m51s 10.233.72.2 kube-worker-3

3. Using the name option(-o name)

You can use the -o name option to get the name of the object quired.  Notice that the returned result is in the format of /. E.g., pod/simple-pod

kubectl get pod -o name
pod/pod-with-few-containers
pod/simple-pod
pod/web-server

4. Skipping the Header in the output(–no-headers)

For any kubectl get command, you can skip printing the header using the -no-headers flag. Example:

kubectl get pod --no-headers
pod-with-few-containers 1/3 NotReady 0 11m
simple-pod 1/1 Running 0 12m
web-server 1/2 NotReady 0 11m

5. Printing the output in YAML format name(-o yaml)

For any kubectl get command, you can print the object’s manifest file in YAML format using the -o yaml flag. This flag is a vital output option in Kubernetes; the object is represented in YAML format by default. The -o YAML option also helps to visualize the location of the information present in the result.  For example,  if I want to cherry-pick the pod name, I will have to grab the value of the .metadata.name. I recommend grasping the structure of the YAML output to write advanced queries efficiently.  See the notes below to aid the visualization of the YAML structure. 

kubectl get pod simple-pod -o yaml
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
creationTimestamp: "2022-04-23T23:26:49Z"
labels:
run: simple-pod
namespace: default
resourceVersion: "713555"
uid: 54ad4ace-a2b4-4527-b1cc-a08c28s4eeaa
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: simple-pod
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-c7kxt
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kube-worker-3
preemptionPolicy: PreemptLowerPriority
......
..........removed for clarity....
..................................

6. Printing the output in JSON format(-o JSON)

It’s evident from the name now that returned result can be shown in JSON format using this flag.  If you are a JSON Person, You can also use this option to grasp the structure of the result. 

kubectl get pod simple-pod -o JSON
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"creationTimestamp": "2022-04-23T23:26:49Z",
"labels": {
"run": "simple-pod"
},
"name": "simple-pod",
"namespace": "default",
"resourceVersion": "713555",
"uid": "54ad4ace-a2b4-4527-b1cc-a08c2864eeaa"
},
"spec": {
"containers": [
{
"image": "nginx",
"imagePullPolicy": "Always",
"name": "simple-pod",
"resources": {},
...........Removed for clarity...........
.........................................

7. Printing the Custom columns(using -o custom-column)

This format may be used to print the desired columns; the format is -o custom-column=USER_DEFINED_COLUMN_NAME:COLUMN_PATH_IN_YAML; in the example below, I am assigning the custom column a name “POD-NAME” and its value is fetched from .metadata.name.

Example-7a: 

kubectl get pod -o custom-columns="POD-NAME":.metadata.name
POD-NAME
pod-with-few-containers
simple-pod
web-server

Example-7b: Skipping the header in the output, Notice there is a blank line when the column name is not used. (I did not use “POD-NAME” this time)

kubectl get pod -o custom-columns=:.metadata.namepod-with-few-containers
simple-pod
web-server

Example-7c: The above output can be fixed by using the -no-header flag, notice the command return only the object name.

kubectl get pod -o custom-columns=:.metadata.name --no-headers
pod-with-few-containers
simple-pod
web-server

Example-7d: Printing multiple columns

kubectl get pod -o custom-columns="POD-NAME":.metadata.name,"NAMESPACE":.metadata.namespace,"CONTAINER-IMAGES":.spec.containers[*].image
POD-NAME NAMESPACE CONTAINER-IMAGES
pod-with-few-containers default nginx,busybox,busybox
simple-pod default nginx
web-server default nginx,busybox

You might have realized that a good understanding of YAML syntax and the output of -o yaml must take advantage of this flag.

Essential Points: Before moving forward

  • To write complex queries, like custom-column, JSON-path, go-template, etc., you must have good knowledge of YAML Syntax. 
  • You must understand the type of objects, like lists, maps, etc., in YAML.  
  • You must be aware of the Kubernetes YAML Structure.  For example, in Pod manifest file, .spec.containers is a list of containers.
  • ‘kubectl explain’ is a great aid in finding the structure of the resource fields. See the example below.
How to figure out the YAML structure of the manifest file/result?

Option-I: You can always do the following to get the example YAML structure, then refer to the structure to grab the desired fields.

kubectl get pod  -o yaml

Option II: Kubectl provides explain the sub-command to get the details about the field associated with the resource. 

kubectl explain --help
List the fields for supported resources.
This command describes the fields associated with each supported API resource. Fields are identified via a simple
JSONPath identifier:
........
Add the --recursive flag to display all of the fields at once without descriptions. Information about each field is retrieved from the server in OpenAPI format.

Examples-1:

From the below snippet, it’s super easy to figure out the YAML structure. For example, from the below snippet, we can figure out the POD’s name can be fetched at .metadata.name.

kubectl explain pod --recursive 
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion
kind
metadata []object
name: string

Example-8: Extracting fields using JSON PATH(-o JSON path)

Using JSON PATH output format, the user gets the fine-grain control of dumping the desired output.  This is one of the most widely used flags for parsing the kubectl output.  Everything inside { } is the instructions, whereas anything outside {} is just a string. 

My pod status when the below commands are executed:

kubectl get pod
NAME READY STATUS RESTARTS AGE
my-pod 1/3 NotReady 0 29m
simple-pod 1/1 Running 1 (34m ago) 7h14m
web-server 1/2 NotReady 0 30m

Example-8a: Getting the container name from a POD(my-pod)

kubectl get pod my-pod -o jsonpath='{.spec.containers[*].name}{"\n"}'
container-1 container-2 container-3

Example-8b: Getting the pod name and container name of a particular pod

kubectl get pod my-pod -o jsonpath='{.metadata.name} ----> {.spec.containers[*].name}{"\n"}'
my-pod ----> container-1 container-2 container-3

Example-8c: Getting the pod name and container name for all the pods (looping over all the pods)

We use range to loop over the pods/items in this example. Everything inside {range} and {end} are iterated for each items(number of pods). This means we are extracting .metadata.name and .spec.containers[*].name for each pod. 

kubectl get pod -o jsonpath='{range .items[*]}{.metadata.name} ----> {.spec.containers[*].name}{"\n"}{end}'
my-pod ----> container-1 container-2 container-3
simple-pod ----> simple-pod
web-server ----> container-1 container-2

Example-8d: Using nested loop to get the container name and images used by all the pods.

In this example, we are looping over all the pods using {range .items[*]} and then doing inner loop using {range .spec.containers[*]} to iterate over all the containers. 

kubectl get pod -o jsonpath='{range .items[*]}{.metadata.name} ----> {range .spec.containers[*]} {.name}{"=>"}{.image} {end}{"\n"}{end}'
my-pod ----> container-1=>nginx container-2=>busybox container-3=>busybox
simple-pod ----> simple-pod=>nginx
web-server ----> container-1=>nginx container-2=>busybox

Example-8e: Get the pod and the container’s ready status

kubectl get pod -o jsonpath='{range .items[*]}{.metadata.name} {range .status.containerStatuses[*]} {.name}{"=>"}{.ready}{end}{"\n"}{end}'
my-pod container-1=>true container-2=>false container-3=>false
simple-pod simple-pod=>true
web-server container-1=>true container-2=>false

Example-8f: Getting the pod name, its container name, image, and ready status

We are printing the pod names followed by container names with the image and ready status to make it fancy. 

kubectl get pod -o jsonpath='{range .items[*]}{.metadata.name}{"\n"} {range .status.containerStatuses[*]}{"\t"}{.name}({.image})=>{.ready}{"\n"}{end}{"\n"}{end}'
pod-with-few-containers
container-1(docker.io/library/nginx:latest)=>true
container-2(docker.io/library/busybox:latest)=>false
container-3(docker.io/library/busybox:latest)=>false
simple-pod
simple-pod(docker.io/library/nginx:latest)=>true
web-server
container-1(docker.io/library/nginx:latest)=>true
container-2(docker.io/library/busybox:latest)"=>"false

Example-8g: Printing the Taints in the node, conditional check:

In this example, all the node names with their taints are printed, whose effect is NoSchedule. In JSON Path, the conditional check is done using the following syntax. Adding a  snippet for reference. 

spec:
podCIDR: 10.233.66.0/24
podCIDRs:
- 10.233.66.0/24
taints:
- effect: NoSchedule
key: key1
value: value1
kubectl get node -o jsonpath='{range .items[*]}{.metadata.name} {.spec.taints[?(@.effect == "NoSchedule")].key}{"\n"}{end}'
kube-master node-role.kubernetes.io/master
kube-worker-1 key1
kube-worker-2
kube-worker-3

There are endless ways to write JSON PATH to extract the field we need. Hopefully, the above examples will help in writing your query. 

Example-9: Printing data using go-template(-o go-template)

This is another powerful way to extract the data we need from the kubectl output.  You can find the official documentation here

kubectl get pod -o go-template='Some random string'
Some random [email protected]:$

#with a new line

kubectl get pod -o go-template='Some random string{{"\n"}}'
Some random string

Example-9a: Printing all the pod names in a namespace

Notice the 1st column; in the default namespace, there are 3 pods, so our go-template is iterating three times. 0,1 and 3rd time. 

kubectl get pod -o go-template='{{range $index,$pod := .items}}{{$index }} {{.metadata.name}}{{"\n"}}{{end}}'
0 pod-with-few-containers
1 simple-pod
2 web-server

Analogous:

To visualize using bash, we create an array called foo and store four strings. 

foo=('line one' 'line two' 'line three' 'line four')

Now printing the index of the element and the value:

for i in "${!foo[@]}"; do printf "%s\t%s\n" "$i" "${foo[$i]}"; done
0 line one
1 line two
2 line three
3 line four

In the same example, without an index, I could not think of any use case where we would need to print the index. 

kubectl get pod -o go-template='{{range $index,$pod := .items}}{{.metadata.name}}{{"\n"}}{{end}}'
pod-with-few-containers
simple-pod
web-server

OR, you may skip the index:

kubectl get pod -o go-template='{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'
pod-with-few-containers
simple-pod
web-server

Example-9b: Printing the pod name and namespace(notice the spaces between the columns, they are not so nice.)

kubectl get pod -A -o go-template='{{range $index,$pod := .items}}{{.metadata.name}} {{.metadata.namespace}} {{"\n"}}{{end}}'
pod-with-few-containers default
simple-pod default
web-server default
coredns-76b4fb4578-sv42x kube-system
coredns-76b4fb4578-k567x kube-system
dns-autoscaler-79a9ssb6659-c2wfb kube-system

Example-9c: Printing the output with the desired padding in go-template

Here we use the built-in function “printf” to get the padding between the columns as per our requirements. Notice that we do not need a separate {{“\n”}} to get a new line. 

kubectl get pod -A -o go-template='{{range $index,$pod := .items}}{{printf "%-40s %20s\n" .metadata.name .metadata.namespace}}{{end}}'
pod-with-few-containers default
simple-pod default
web-server default
coredns-76b4fb4578-2v42x kube-system
coredns-76b4fb4578-s567x kube-system
dns-autoscaler-7979fb6659-c2wfb kube-system
kube-apiserver-kube-master kube-system
kube-controller-manager-kube-master kube-system
kube-proxy-55kwq kube-system

Example-9d: Printing the pods belonging to a particular namespace(using conditional statement -> if/else)

 In the below example, we are using conditional statements to only print the pod name, which belongs to the “default” namespace.  

kubectl get pod -A -o go-template='{{range $index, $element := .items}}{{ if (eq $element.metadata.namespace "default")}}{{$element.metadata.name}}{{"\n"}}{{ end }}{{end}}'
pod-with-few-containers
simple-pod
web-server

The syntax for conditional statements, where LHS and RHS are two values.

{{ if (eq LHS RHS) }} 
  {{ If true, do this action }}
{{else}}
  {{if false do this action}}
{{end}}

Other comparison operators are:

eq
	Returns the boolean truth of arg1 == arg2
ne
	Returns the boolean truth of arg1 != arg2
lt
	Returns the boolean truth of arg1 < arg2
le
	Returns the boolean truth of arg1 <= arg2 gt Returns the boolean truth of arg1 > arg2
ge
	Returns the boolean truth of arg1 >= arg2

Example-9e: Using the multi-line go-template

The following command is the same as the above but looks more readable.  However, you will not like the output if you run the below command. It will be full of weird newlines and spaces. This is because I am not pasting the output here. 

kubectl get pod -A -o go-template='{{range $index, $element := .items}}
{{ if (eq $element.metadata.namespace "default")}}
{{$element.metadata.name}}{{"\n"}}
{{ end }}
{{end}}'

The solution for using a multi-line go template is using “{{-” and “-}}” for stripping the leading and trailing newlines and spaces. The below output is clean even when a multi-line command is used. 

kubectl get pod -A -o go-template='{{ range $index, $element := .items }}
{{- if (eq $element.metadata.namespace "default") -}}
{{$element.metadata.name}}{{"\n"}}
{{- end -}}
{{ end }}'
pod-with-few-containers
simple-pod
web-server

Example-9f: Printing the pod name, container name, and their status

 kubectl get pod -o go-template='{{range $index, $element := .items}}
{{- range $container, $status := $element.status.containerStatuses -}}
{{- if not .ready -}}
{{printf "%-40s %40s %30s\n" $element.metadata.name $status.name "not-ready" }}
{{- else -}}
{{printf "%-40s %40s %30s\n" $element.metadata.name $status.name "ready"}}
{{- end -}}
{{- end -}}
{{end}}'
pod-with-few-containers container-1 ready
pod-with-few-containers container-2 not-ready
pod-with-few-containers container-3 not-ready
simple-pod simple-pod ready
web-server container-1 ready
web-server container-2 not-ready

The above example can be further cleaned; notice that the nested loop is made over the container status.  This is one example of text formatting; there is an endless combination you can play around with. 

kubectl get pod -o go-template='{{$podname := ""}}
{{- range $index, $element := .items -}}
{{- range $container, $status := $element.status.containerStatuses -}}
{{ if (eq $container 0 )}}
{{- $podname = printf "%s %s" $element.metadata.name "\n|-->" -}}
{{else}}
{{- $podname = "|-->" -}}
{{end}}
{{- if not .ready -}}
{{ $podname }} {{ $status.name }} {{ "not-ready" }}{{"\n"}}
{{- else -}}
{{$podname}} {{ $status.name }} {{"ready"}}{{"\n"}}
{{- end -}}
{{- end -}}
{{- end -}}'
pod-with-few-containers
|--> container-1 ready
|--> container-2 not-ready
|--> container-3 not-ready
simple-pod
|--> simple-pod ready
web-server
|--> container-1 ready
|--> container-2 not-ready

Now Print the pod name, container name, and their respective images.

kubectl get pod -o go-template='{{$podname := ""}}
{{- range $index, $element := .items -}}
{{- range $container, $status := $element.status.containerStatuses -}}
{{ if (eq $container 0 )}}
{{- $podname = printf "%s %s" $element.metadata.name "\n|-->" -}}
{{else}}
{{- $podname = "|-->" -}}
{{end}}
{{- if not .ready -}}
{{ $podname }} {{ $status.name }} {{ "not-ready" }}{{"\n"}}
{{- else -}}
{{$podname}} {{ $status.name }} {{"ready"}}{{"\n"}}
{{- end -}}
{{- end -}}
{{- end -}}'
pod-with-few-containers
|--> container-1(docker.io/library/nginx:latest) ready
|--> container-2(docker.io/library/busybox:latest) not-ready
|--> container-3(docker.io/library/busybox:latest) not-ready
simple-pod
|--> simple-pod(docker.io/library/nginx:latest) ready
web-server
|--> container-1(docker.io/library/nginx:latest) ready
|--> container-2(docker.io/library/busybox:latest) not-ready

Another Variation: Perhaps simpler than the last one. 

kubectl get pod -o go-template='{{$ready :=""}}{{range .items}}{{ printf "POD:\t%-40s(%s)\n" .metadata.name (.metadata.namespace)}}{{range .status.containerStatuses}}{{ if .ready }}{{$ready = "ready"}}{{else}}{{$ready = "not-ready"}}{{end}}{{printf "\t|->%-20s %20s %20s\n" .name .image $ready}}{{end}}{{"\n"}}{{end}}'
POD: pod-with-few-containers (default)
|->container-1 docker.io/library/nginx:latest ready
|->container-2 docker.io/library/busybox:latest not-ready
|->container-3 docker.io/library/busybox:latest not-ready
POD: simple-pod (default)
|->simple-pod docker.io/library/nginx:latest ready
POD: web-server (default)
|->container-1 docker.io/library/nginx:latest ready
|->container-2 docker.io/library/busybox:latest not-ready

Be Creative!

Example-10: USing go-template-file to print custom output

Sometimes, the template size grows like crazy if you want an excellent output. In such cases, we keep the template stored in a file. In this example, we are using the sample template as the above example but holding it in a file called abc.go-template. Note that the format does not matter, but for keeping it explanatory. 

cat abc.go-template
{{$podname := ""}}
{{- range $index, $element := .items -}}
{{- range $container, $status := $element.status.containerStatuses -}}
{{ if (eq $container 0 )}}
{{- $podname = printf "%s %s" $element.metadata.name "\n|-->" -}}
{{else}}
{{- $podname = "|-->" -}}
{{end}}
{{- if not .ready -}}
{{ $podname }} {{ $status.name }}({{$status.image}}) {{ "not-ready" }}{{"\n"}}
{{- else -}}
{{$podname}} {{ $status.name }}({{$status.image}}) {{"ready"}}{{"\n"}}
{{- end -}}
{{- end -}}
{{- end -}}

Now call the template file as below:

kubectl get pod -o go-template-file=abc.go-template
pod-with-few-containers
|--> container-1(docker.io/library/nginx:latest) ready
|--> container-2(docker.io/library/busybox:latest) not-ready
|--> container-3(docker.io/library/busybox:latest) not-ready
simple-pod
|--> simple-pod(docker.io/library/nginx:latest) ready
web-server
|--> container-1(docker.io/library/nginx:latest) ready
|--> container-2(docker.io/library/busybox:latest) not-ready

Common idioms in go-template:

  1. Getting the number of Pods(or any other object):

The following will return the number of pods in the default namespace. Here, “len” is an inbuilt function in the go-template. So the template below prints the items’ length (a list).  

kubectl get pod -o go-template='{{printf "%d\n" (len  .items)}}'
2. Getting the nodes with the taints:

Here, we are looping over the list of the nodes and taking the length of the .spec.taints; if it is zero, then the node has no taints. However, if the size is non-zero, the node has at least one taint. Note that I have used or $element.spec.taints "" to default the taints to "" (zero) if there is no taint present. 

 By changing the ne to eq, You can flip the nodes.

kubectl get node  -o go-template='{{ range $element := .items }}
                                {{- if (ne (len (or $element.spec.taints ""))  0) -}}
                                    {{ $element.metadata.name}}{{"\n"}}
                                {{- end -}}
                            {{ end }}'
3. Getting the Resources request and limits of all the pods

The below command would print the resources requests and limits:

kubectl get pod -o go-template='{{- range $index, $element := .items -}}
{{- range $container, $status := $element.spec.containers -}}
{{- printf "%s,%s,%s,%s,%s,%s,%s\n" $element.metadata.namespace $element.metadata.name $status.name (or $status.resources.requests.cpu "null" ) (or $status.resources.requests.memory "null") (or $status.resources.limits.memory "null") (or $status.resources.limits.cpu "null") -}}
{{- end -}}
{{- end -}}' -A

Sample output: The below output shows the namespace, podname, containername, cpu-request,memory-request,cpu-limit,memory-limit, for the containers that does not have request/limits set, “null” string is getting set as a default. 

default,curl,curl,null,null,null,null
default,curl,istio-proxy,10m,40Mi,1Gi,2
default,httpd,httpd,null,null,null,null
default,httpd,istio-proxy,10m,40Mi,1Gi,2
default,webserver-649fc84487-chjvp,nginx,null,null,null,null
default,webserver-649fc84487-chjvp,istio-proxy,10m,40Mi,1Gi,2
default,webserver-649fc84487-crrvf,nginx,null,null,null,null
default,webserver-649fc84487-crrvf,istio-proxy,10m,40Mi,1Gi,2
default,webserver-649fc84487-hwqdf,nginx,null,null,null,null
default,webserver-649fc84487-hwqdf,istio-proxy,10m,40Mi,1Gi,2
A Common mistake:

A typical catch when people try to get the number of pods (or any other object) is they do “wc -l”. But they also take the header into account.

kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system helm-install-traefik-crd--1-bfjhf 0/1 Completed 0 53d
kube-system helm-install-traefik--1-cwtvb 0/1 Completed 0 53d
kube-system svclb-traefik-tw6q9 2/2 Running 2 (26d ago) 53d
kube-system local-path-provisioner-84bb864455-dhdlt 1/1 Running 1 (26d ago) 53d
kube-system traefik-56c4b88c4b-8fgjz 1/1 Running 1 (26d ago) 53d
kube-system metrics-server-ff9dbcb6c-7wlq7 1/1 Running 1 (26d ago) 53d
kube-system coredns-96cc4f57d-5qn94 1/1 Running 1 (26d ago) 53d
default entry-to-vps-6b74c4cc99-5s7t2 1/1 Running 0 4d18h

#Note: From the above output, the number of pods is 8, but wc -l is returning nine as it considers the header.
kubectl get pod -A |wc -l
9

You must use –no-headers to remove the header from the output.

kubectl get pod -A --no-headers |wc -l
8

1 thought on “Getting custom output from kubectl with examples”

  1. When some one searches for his vital thing, therefore he/she desires to be available
    that in detail, so that thing is maintained over here.

Leave a Reply to Twicsy Cancel Reply

Your email address will not be published.

Scroll to Top