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.

45 min 3 hands-on tasks

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Produce a correct manifest for a Pod, Deployment, Service, ConfigMap or Secret with a kubectl generator and --dry-run=client -o yaml, and find the generator for any other kind with --help.
  • Extract exactly the fields a question asks for with -o jsonpath, custom-columns, --sort-by and selectors.
  • 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)
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

Learn the patterns, not the commands

You do not need to memorize seventy commands. Almost every way of making or changing an object follows one of five shapes, and one sentence holds them all: run a , expose a , create everything else, set or scale to change it, and add $do to see the instead.

You wantPatternExample
A Podk run NAME --image=IMAGEk run web --image=nginx:1.27
A Service for an existing objectk expose KIND NAME --port=PORTk expose deploy web --port=80
Anything elsek create KIND NAME --flagsk create deploy web --image=nginx:1.27
A changek set, k scale, k labelk scale deploy web --replicas=5
YAML, not the objectadd $do > file.yaml to any of thesek run web --image=nginx:1.27 $do > web.yaml
Anatomy of a kubectl commandPick a pattern, then tap any part.

run always makes a Pod, so the command has no kind.

verb: What to do: run, create, expose, scale, set, label. This one word picks the pattern.

Imperative generators

The generators you will use from the first week, grouped by topic. Skim each block once and press Explain on anything new; the practice cards after them are where it sticks.

Pods and Deployments
# A Pod
k run web --image=nginx:1.27 --port=80 --labels=app=web
k run once --image=busybox:1.36 --restart=Never -- sh -c 'date'
k run web2 --image=nginx:1.27 --command -- sh -c 'sleep 3600'
k run tmp --image=busybox:1.36 -it --rm --restart=Never -- sh

# A Deployment
k create deploy web --image=nginx:1.27 --replicas=3 --port=80
Services
# A Service for an existing object (--type can also be NodePort or LoadBalancer)
k expose deploy web --name=web-svc --port=80 --target-port=8080 --type=ClusterIP

# A Service on its own (the selector becomes app=web, from the name)
k create svc clusterip web --tcp=80:8080
Configuration and namespaces
# A ConfigMap
k create cm app-cfg --from-literal=KEY=value --from-file=app.properties
k create cm app-env --from-env-file=.env

# A Secret
k create secret generic db --from-literal=password=s3cret

# A Namespace
k create ns team-a
Changing what already exists
# Scale, or change the image
k scale deploy web --replicas=5
k set image deploy/web nginx=nginx:1.26

# Labels (a trailing - removes one)
k label pod web tier=frontend --overwrite
k label pod web tier-

Every other kind of object has a generator too, and you will meet each one in the lesson about that object: and in Workloads, Roles and in RBAC, in Networking, in Scheduling and in Upgrades. The exam cheat sheet puts them all on one page. Until then, k create --help lists every kind it can make.

Generate, inspect, edit, apply: the loop behind most tasks
# 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

Practice: make it from memory

0 of 10 known

Read the task, type the command without looking up, then check. Cards you miss come back at the end of the round. Your progress stays in this browser.

Card 1 of 10

A Pod named web running nginx:1.27.

Reading the cluster

Output formats and filters
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)
# 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'

Practice: read the cluster

0 of 7 known

Same idea for reading: type the command, then check.

Card 1 of 7

Every Pod in every namespace, with the node it runs on.

Changing objects: which tool

You want toUseGood to know
Change a field that can change (image, replicas, , env)k edit, k set, k scale, k labelk edit opens the live object in vim. Save something invalid and it reopens with the error at the top.
Apply a file you generated or wrotek apply -f file.yamlCreates the object, or updates it if it exists. The usual step after $do.
Change a field that cannot changek replace --force -f file.yamlDeletes and recreates the object: the only way to change an immutable field.
Remove a Pod stuck in Terminatingk delete pod X $nowSkips the grace period. Use it on Pods only, never on or .
Fix Pods that a ownsChange the Deployment, not the PodsThe replaces the Pods for you.

Which fields are immutable? A Pod's image can change. Its ' 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 cannot. When k edit refuses to save, it writes your version to a file under /tmp and prints the path: run k replace --force -f on that file.

Contexts and namespaces

Contexts and namespaces
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 official docs· open on exam day too

Hands-on task 1 of 3fd-fluency-1
~4 min3% weightArchitecture

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.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

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

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-1/cleanup | bash
Hands-on task 2 of 3fd-fluency-2
~4 min3% weightArchitecture

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.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

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

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-2/cleanup | bash
Hands-on task 3 of 3fd-fluency-3
~4 min3% weightArchitecture

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.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

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

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/fd-fluency-3/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

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

Finished this lesson?

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