kubectl fluency

The imperative commands that generate most exam YAML, the output formats and jsonpath recipes that read any cluster in one line, and the shell setup that makes all of it faster.

Foundations40 min3 tasks

After this lesson

  • 01You can produce a correct manifest for any common resource with a kubectl generator and --dry-run=client -o yaml, without opening the docs.
  • 02You can extract exactly the fields a question asks for with -o jsonpath, custom-columns, --sort-by and selectors.
  • 03You can choose between edit, apply, replace --force and delete --force based on whether the field is mutable.

Two hours, up to twenty tasks: that is six minutes per task including reading, doing and checking. The difference between passing comfortably and running out of time is almost never Kubernetes knowledge; it is how many keystrokes stand between you and a correct object. This lesson is the keystroke budget. Everything here runs on the lab exactly as written.

Shell setup: sixty seconds that pay back all exam

Type this first on every exam host (k, completion and bash are already there; the rest is yours)bash
alias k=kubectl
export do="--dry-run=client -o yaml"
export now="--force --grace-period=0"
source <(kubectl completion bash)
complete -o default -F __start_kubectl k

# Then the two commands you will use most:
k create deploy web --image=nginx:1.27 --replicas=2 $do > web.yaml
k delete pod stuck $now

Imperative generators

You needCommand
A Podk run web --image=nginx:1.27 --port=80 --labels=app=web; add --restart=Never for a one-off, --command -- sh -c '...' to override the command, -it --rm for a throwaway shell
A Deploymentk create deploy web --image=nginx:1.27 --replicas=3 --port=80
A Service for an existing objectk expose deploy web --name=web-svc --port=80 --target-port=8080 --type=ClusterIP (or NodePort, LoadBalancer)
A Service on its ownk create svc clusterip web --tcp=80:8080; k create svc nodeport web --tcp=80:8080 --node-port=30080 (the selector becomes app=web, from the name)
A ConfigMapk create cm app-cfg --from-literal=KEY=value --from-file=app.properties --from-env-file=.env
A Secretk create secret generic db --from-literal=password=s3cret; k create secret tls web-tls --cert=tls.crt --key=tls.key; k create secret docker-registry regcred --docker-server=... --docker-username=... --docker-password=...
A Jobk create job backup --image=busybox:1.36 -- sh -c 'date'; k create job manual --from=cronjob/nightly
A CronJobk create cj nightly --image=busybox:1.36 --schedule='0 2 * * *' -- sh -c 'date'
RBACk create role dev --verb=get,list,watch --resource=pods,deployments; k create clusterrole nodes-ro --verb=get,list --resource=nodes; k create rolebinding dev-rb --role=dev --user=jane --group=devs --serviceaccount=ns:sa; k create clusterrolebinding ...
A ServiceAccount and a tokenk create sa app; k create token app --duration=1h
An Ingressk create ingress web --class=nginx --rule='shop.example.com/*=web-svc:80' (add --rule per path; --annotation key=value)
A Namespace, a quota, a PDB, a PriorityClassk create ns team-a; k create quota q --hard=pods=10,requests.cpu=2; k create pdb web-pdb --selector=app=web --min-available=1; k create priorityclass high --value=100000
Autoscalingk autoscale deploy web --min=2 --max=6 --cpu-percent=70 (the Deployment must already exist; creates an autoscaling/v2 HPA)
Scale and setk scale deploy web --replicas=5; k set image deploy/web nginx=nginx:1.26; k set env deploy/web LOG=debug; k set resources deploy/web -c nginx --requests=cpu=100m,memory=64Mi --limits=cpu=200m,memory=128Mi; k set sa deploy/web app
Labels, annotations, taintsk label pod web tier=frontend --overwrite; k label pod web tier- (remove); k annotate deploy web owner=ops; k taint node cka-worker gpu=true:NoSchedule; k taint node cka-worker gpu=true:NoSchedule- (remove)
Node maintenancek cordon cka-worker; k drain cka-worker --ignore-daemonsets --delete-emptydir-data --force; k uncordon cka-worker
Generate, inspect, edit, apply: the loop behind most tasksbash
# 1. Generate a manifest without touching the cluster
k -n team-a create deploy api --image=nginx:1.27 --replicas=3 $do > api.yaml

# 2. Find the field you need to add, with the exact spelling and type
k explain deploy.spec.strategy
k explain pod.spec.containers.livenessProbe --recursive | less
k explain pod.spec.tolerations

# 3. Edit the file in vim, then apply it
k apply -f api.yaml

# Multi-resource trick: generate a pod, pipe straight into a label change, apply in one line
k run tmp --image=busybox:1.36 --restart=Never $do -- sleep 3600 | k label --local -f - tier=debug -o yaml | k apply -f -

# --help is faster than the docs for flags
k create deploy --help | less
k run --help | grep -A2 -- --command

Reading the cluster

Output formats and filtersbash
k get pods -o wide                      # node, IP, nominated node, readiness gates
k get deploy web -o yaml                # the whole object; pipe to grep or less
k get pod web -o json | less
k get pods -o name                      # pod/web, pod/api... handy for xargs and loops
k get pods -A                           # every namespace
k get pods -l app=web,tier!=cache       # label selectors; also 'env in (prod,staging)'
k get pods --show-labels
k get pods --field-selector=status.phase!=Running -A
k get pods --sort-by=.metadata.creationTimestamp
k get events --sort-by=.lastTimestamp -n team-a
k get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,IP:.status.podIP
k get pods -w                           # watch; Ctrl+C to stop
k api-resources | grep -i policy        # short names, api groups, namespaced or not
k api-versions | grep autoscaling
jsonpath recipes (single quotes outside, double quotes inside)bash
# One field
k get deploy web -o jsonpath='{.spec.replicas}'

# A list, space separated
k get pods -o jsonpath='{.items[*].metadata.name}'

# One item per line, several fields: range ... end
k get nodes -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.status.nodeInfo.kubeletVersion}{"\n"}{end}'

# Filter inside a list: the address whose type is InternalIP
k get nodes -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.status.addresses[?(@.type=="InternalIP")].address}{"\n"}{end}'

# Filter whole items: pods on one node
k get pods -A -o jsonpath='{range .items[?(@.spec.nodeName=="cka-worker")]}{.metadata.namespace}{"/"}{.metadata.name}{"\n"}{end}'

# Container images in a Deployment, taints on nodes, the InternalIP of one node
k get deploy web -o jsonpath='{.spec.template.spec.containers[*].image}'
k get nodes -o jsonpath='{range .items[*]}{.metadata.name}{": "}{.spec.taints[*].key}{"\n"}{end}'
k get node cka-worker -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}'

# Tabular output sorted by a jsonpath (sorted on the server response, works with custom-columns)
k -n kube-system get pods --sort-by=.metadata.name -o custom-columns=POD:.metadata.name,NODE:.spec.nodeName

# When jsonpath gets ugly, yq is on the exam hosts
k get deploy web -o yaml | yq '.spec.template.spec.containers[].image'

Changing objects: pick the tool by what you are changing. kubectl edit opens the live object in vim and is right for any mutable field; if you save something invalid it reopens with the error at the top. kubectl apply -f is right when you have the file, and it is what you want after generating with $do. kubectl replace --force -f file.yaml deletes and recreates, which is the only way to change an immutable field: a Pod's image is mutable, but its containers' names, its volumes, its nodeName, resources on a running Pod (without the in-place resize feature) and most of a Service's or a Job's spec are not. kubectl delete pod X $now skips the grace period when a Pod is stuck Terminating; use it on Pods, never on nodes or namespaces. Pods owned by a Deployment do not need to be edited at all: change the Deployment's template and the rollout does the rest.

Contexts and namespacesbash
k config get-contexts                            # the * marks the current one
k config use-context kind-cka
k config current-context
k config set-context --current --namespace=team-a  # every later command defaults to team-a
k config view --minify                             # the active cluster, user and namespace only
k config view --minify -o jsonpath='{..namespace}'

# When a kubeconfig is given as a file in the task
k --kubeconfig=/root/other.kubeconfig get nodes
export KUBECONFIG=/root/other.kubeconfig

In the docs · the tab you get on exam day

Taskfd-fluency-1
Architecture3%~4 min

context · kubectl config use-context kind-cka

Generate a manifest, then apply it

Using kubectl and without creating anything on the cluster yet, generate a Deployment manifest and save it to /tmp/cka/fluency/web.yaml: name web, namespace fd-fluency, image nginx:1.27, 2 replicas. Add the label tier=frontend to the Deployment's own metadata in that file, then create the Deployment from the file. Both the file and the live Deployment must carry the label and have 2 replicas.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-1/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-1/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-1/cleanup | bash
Taskfd-fluency-2
Architecture3%~4 min

context · kubectl config use-context kind-cka

Node names and InternalIPs with jsonpath

Using a single kubectl get nodes command with a jsonpath expression, write the name and the InternalIP of every node in the cluster to /tmp/cka/fluency/nodes.txt. One node per line, in the form <name> <ip> separated by one space, nothing else in the file.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-2/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-2/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-2/cleanup | bash
Taskfd-fluency-3
Architecture3%~4 min

context · kubectl config use-context kind-cka

A sorted custom-columns listing

Write a listing of all Pods in the kube-system namespace to /tmp/cka/fluency/kube-system-pods.txt. Use kubectl custom columns with exactly two columns, headed POD (the Pod name) and NODE (the node the Pod runs on), sorted by Pod name. Keep the header line.
  1. 01

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-3/setup | bash
  2. 02

    Solve it in your terminal

    Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under /tmp/cka/.

  3. 03

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-3/verify | bash
    Counts toward your readiness score.
  4. 04

    Clean up

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-3/cleanup | bash

Finished reading?

0 of 3 tasks passed. Readiness counts tasks, so run them before you move on.