Kubernetes

Five Hints for Working With Kubectl7 min read

When you work with Kubernetes, kubectl 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, 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 tehir 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?

kubectl wait 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 create job dummy \
    --image busybox \
    -- sh -c 'sleep 30'

Expected output:

job.batch/dummy created

Immediately after the job was launched, use kubectl to wait for the job’s completion:

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!

Image sources for this page:

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA


The following GDPR rules must be read and accepted:
This form collects your name, email and content so that I can keep track of the comments placed on the website. Your current IP address will also be collected in order to prevent spam comments from automated bots. For more info check the privacy policy where you can educate yourself on where, how and why your data is stored.