When you work with Kubernetes,
is your friend. It allows you to communicate with the Kubernetes API Server on your command line of choice which, makes it the main steering wheel used by developers and operators to control their Kubernetes clusters. When working with kubectl
kubectl
, pretty soon you will have sequences of commands you need to issue repeatedly in your daily workflow. Or maybe you are even working on a few scripts to automate Kubernetes stuff. For me it was sometimes challenging to squeeze out the functionality of kubectl
to solve my problems. That’s why I want to share a few hints for working with kubectl
.
Automatically Get a Pod’s Name With Labels
The usual way to get the names of Pods which belong to a specific ReplicaSet, such as one created by a Deployment, is very basic:
kubectl get pod -n kube-system
Expected output:
NAME READY STATUS RESTARTS AGE local-path-provisioner-7ff9579c6-xl4gd 1/1 Running 3 4d3h metrics-server-7b4f8b595-4f4dm 1/1 Running 3 4d3h coredns-66c464876b-28bfr 1/1 Running 1 3d6h coredns-66c464876b-p4lk9 1/1 Running 3 4d3h coredns-66c464876b-zbpcz 1/1 Running 1 3d6h
For StatefulSets the Pod names are static (some-pod-1
, some-pod-2
…), but for a Deployment with a ReplicaCount of more than one Pod, each Pod’s name contains a random suffix to make it uniquely identifiable among its siblings in the same ReplicaSet. In the above output we can see three different Pods for coredns
:
coredns-66c464876b-28bfr
coredns-66c464876b-p4lk9
coredns-66c464876b-zbpcz
Soon I was tired of checking for a valid Pod name each time I needed one, i.e. to get one of their shell’s for troubleshooting purposes. Here’s the good news: Pods have labels attached to them that allow you to easily determine to which Deployment a Pod belongs to.
Option 1 — Show labels for a specific deployment (jq
is only used to make the JSON output look prettier):
kubectl get deployments.apps \ -n kube-system coredns \ -o=jsonpath='{.spec.selector.matchLabels}' | jq
Expected output:
{ "k8s-app": "kube-dns" }
Option 2 — List pods a of a specific namespace while displaying all their labels:
kubectl get pod \ -n kube-system \ --show-labels
Expected output:
NAME READY STATUS RESTARTS AGE LABELS local-path-provisioner-7ff9579c6-xl4gd 1/1 Running 3 4d3h app=local-path-provisioner,pod-template-hash=7ff9579c6 metrics-server-7b4f8b595-4f4dm 1/1 Running 3 4d3h k8s-app=metrics-server,pod-template-hash=7b4f8b595 coredns-66c464876b-28bfr 1/1 Running 1 3d6h k8s-app=kube-dns,pod-template-hash=66c464876b coredns-66c464876b-p4lk9 1/1 Running 3 4d3h k8s-app=kube-dns,pod-template-hash=66c464876b coredns-66c464876b-zbpcz 1/1 Running 1 3d6h k8s-app=kube-dns,pod-template-hash=66c464876b
We can see that Deployment coredns
in Namespace kube-system
labels its Pods with key k8s-app
set to value kube-dns
. Fortunately, kubectl
provides a flag to filter query output by labels. In our case, we can use the Pod label "k8s-app": "kube-dns"
to only return Pods which belong to Deployment coredns
:
kubectl get pods -A -l k8s-app=kube-dns
Expected output:
NAME READY STATUS RESTARTS AGE coredns-66c464876b-p4lk9 1/1 Running 2 21h coredns-66c464876b-28bfr 1/1 Running 0 48m coredns-66c464876b-zbpcz 1/1 Running 0 48m
With this command, we can easily filter the Pods displayed we are looking for, even without having to know the Namespace they are living in.
But we have other plans, as we only need the name of one of the Pods to get into its shell. For purposes like this, JSONPaths can be used to select a specific property from the output. We will now extend above command with a JSONPath query:
kubectl get pods \ -n kube-system \ -l k8s-app=kube-dns \ -o=jsonpath='{.items[*].metadata.name}'
Expected output:
coredns-66c464876b-28bfr coredns-66c464876b-p4lk9 coredns-66c464876b-zbpcz
Output of this type can be used for a variety of automation tasks in your scripts.
But wait, there is more!
Getting a Service’s Port Number
JSONPaths are powerful and besides selecting a field by its path, you can also select the value of a field depending on a value the parent object of the field has. A common example for this use case: You want to use kubectl
to programmatically get the port number of a Service that has been created automatically. Let’s see this by example by a service available in many clusters:
kubectl get service \ -n ingress-nginx \ ingress-nginx-controller
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.43.18.222 <pending> 80:31452/TCP,443:31984/TCP 4d3h
What if we want to get the port NodePort which maps port 443 automatically in a script without fumbling around with string manipulation tools?
kubectl get service ingress-nginx-controller \ -n ingress-nginx \ -o=jsonpath='{.spec.ports[?(@.name=="https")].nodePort}'
Expected output:
31984
And here you get the port number for further utilization in an automation workflow.
Waiting for Conditions
Kubernetes is designed to have configuration changes applied in a declarative manner, but in some situation it makes sense to control the sequence of resource creation and the points in time in which specific configurations changes shall be executed. Let’s say you want to pause a script in your build pipeline until a specific Deployment is readily deployed, before the other actions are carried out – how is this achievable with the fewest command code possible?
helps you waiting for a Job to complete. We can demonstrate this by launching a dummy Job, that is simply doing nothing, but waiting:kubectl wait
kubectl create job dummy \ --image busybox \ -- sh -c 'sleep 30'
Expected output:
job.batch/dummy created
Immediately after the job was launched, use
to wait for the job’s completion:kubectl
kubectl wait \ --for=condition=complete \ --timeout=60s \ job/dummy
Expected output:
# ... zzZZZZZzzzzzZZZZZzzzz job.batch/dummy condition met
If you are looking at Deployments, kubectl rollout status
is your friend:
kubectl rollout status \ -n kube-system \ deployment/coredns
Expected output:
deployment "coredns" successfully rolled out
Typing Less With Shell Aliases
lIt’s only 7 characters, but typing kubectl a hundred times a day can be bothering, but shells let you define shorter aliases. For a Bash on Linux the character k
can be set as a persistent alias by running:
# Write alias into bashrc file echo alias k='kubectl' >> ~/.bashrc # Apply changes to current shell source ~/.bashrc
On Windows the CMD supports aliases (Superuser thread), as well as the PowerShell does (Stackoverflow thread).
Apply API Objects Without a YAML Manifest
kubectl create
allows you to create an object with a pure kubectl
command instead of providing a YAML manifest. The limitation: kubectl create
is an imperative comand, not a declarative command. This can be an issue if you re-run the same script several times, if the object you want to create already exists. Let’s imperatively create a config map:
kubectl create configmap \ myconfig \ --from-literal=mykey=123
Expected output:
configmap/myconfig created
Now, let’s try this once again to update mykey
to 456
:
kubectl create configmap \ myconfig \ --from-literal=mykey=456
Expected output:
Error from server (AlreadyExists): configmaps "myconfig" already exists
So, how can you workaround this error? kubectl
has the capability to let you generate the manifests resulting from the create command without applying them on the server-side (the Kubernetes cluster):
kubectl create configmap \ myconfig \ --from-literal=mykey=456 \ --dry-run=client -o=yaml
Expected output:
apiVersion: v1 data: mykey: "456" kind: ConfigMap metadata: creationTimestamp: null name: myconfig
With --dry-run=client
we generate the manifest and with -o=yaml
we get the output as YAML. Instead of writing the YAML lines to the shell’s standard output, we can pipe this into a kubectl apply
command:
kubectl create configmap \ myconfig \ --from-literal=mykey=456 \ --dry-run=client -o=yaml \ | kubectl apply -f -
Expected output
configmap/myconfig configured
We can use the following query to verify whether the change has been applied successfully:
kubectl get configmap \ myconfig \ -o=jsonpath='{.data.mykey}'
Expected output:
456
On your first attempt you may get this warning:
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
You can get around this warning, if you use flag --save-config
for all the create commands.
This is it. There is a lot of hidden functionality to discover in kubectl
, so this will definitely not be the last post on this matter.
Enjoy your day!