Skip to main content
mastering ckad certified kubernetes application developer

Imperative Commands and Aliases

10 min read Chapter 74 of 87
Summary

Shell environment setup (aliases, bash completion, .vimrc), imperative...

Shell environment setup (aliases, bash completion, .vimrc), imperative kubectl commands for Pods, Deployments, Services, ConfigMaps, Secrets, Jobs, and CronJobs, the --dry-run=client -o yaml pattern, kubectl explain, api-resources, and context switching.

Imperative Commands and Aliases

The first two minutes of your exam should be spent setting up your shell. The remaining 118 minutes benefit from every shortcut you configure during that window.

Shell Setup: The First Two Minutes

Open your terminal and type these commands before touching a single task.

Alias: k for kubectl

alias k=kubectl

This saves 6 characters per command. Over the course of an exam with 200+ kubectl invocations, that adds up to over a thousand keystrokes. More importantly, it reduces cognitive load — k get pods reads faster than kubectl get pods.

The Dry-Run Export Variable

export do="--dry-run=client -o yaml"

This variable lets you append $do to any imperative command to generate YAML instead of creating the resource:

k run nginx --image=nginx $do > pod.yaml

That single line generates a complete Pod manifest. Without this variable, you would type --dry-run=client -o yaml every time — 27 characters that you now replace with 3.

Bash Completion

source <(kubectl completion bash)

This enables tab completion for kubectl subcommands, resource types, resource names, and flags. After running this, typing k get dep<TAB> expands to k get deployments, and typing k get pods my-<TAB> completes to the full Pod name.

To make the alias k work with bash completion:

complete -o default -F __start_kubectl k

Without this second line, tab completion works for kubectl but not for k. Run both commands.

.vimrc for YAML Editing

The exam environment uses vim as the default editor. YAML is whitespace-sensitive — a single misaligned space breaks your manifest. Configure vim to handle YAML correctly:

cat <<EOF >> ~/.vimrc
set tabstop=2
set shiftwidth=2
set expandtab
set ai
EOF

What each setting does:

  • tabstop=2 — Displays tab characters as 2 spaces wide.
  • shiftwidth=2 — Indentation operations (>>, <<) move text by 2 spaces.
  • expandtab — Pressing Tab inserts spaces, not a tab character. YAML does not allow tab characters.
  • ai (auto-indent) — New lines inherit the indentation level of the previous line. When you press Enter inside a YAML block, the cursor starts at the correct indentation.

Complete Setup Block

Here is the entire setup as a single paste:

alias k=kubectl
export do="--dry-run=client -o yaml"
source <(kubectl completion bash)
complete -o default -F __start_kubectl k
cat <<EOF >> ~/.vimrc
set tabstop=2
set shiftwidth=2
set expandtab
set ai
EOF

Memorize this block. Practice typing it until you can enter it in under 30 seconds without looking at notes.

Imperative Commands: Why They Matter

The CKAD exam does not grade your YAML. It grades whether the requested resource exists in the cluster with the correct configuration. A Pod created via kubectl run is identical to a Pod created by applying a YAML file — Kubernetes does not distinguish between them.

Writing YAML from scratch is slow and error-prone. A Pod spec has 10+ fields even for a minimal definition. An imperative command creates the same Pod in one line:

k run nginx --image=nginx --port=80

Versus writing this by hand:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    run: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80

The imperative command is faster, and the declarative YAML is what you need when the task requires fields that imperative flags do not cover — like resource limits, volume mounts, or probes. The strategy: generate the scaffold imperatively, then edit the parts that need customization.

Pod Creation: kubectl run

Create a Pod with a specific image:

k run my-pod --image=nginx

Add labels:

k run my-pod --image=nginx --labels="app=web,tier=frontend"

Set resource requests and limits:

k run my-pod --image=nginx --requests="cpu=100m,memory=128Mi" --limits="cpu=200m,memory=256Mi"

Expose a container port:

k run my-pod --image=nginx --port=80

Run a Pod with a specific command:

k run my-pod --image=busybox --command -- sh -c "echo hello && sleep 3600"

Note the --command flag and -- separator. Everything after -- is the container command.

Generate YAML without creating the Pod:

k run my-pod --image=nginx --port=80 $do > my-pod.yaml

Open the file, add what you need (probes, volumes, environment variables), then apply:

k apply -f my-pod.yaml

Deployment Creation: kubectl create deployment

k create deployment my-deploy --image=nginx --replicas=3

This creates a Deployment with 3 replicas running the nginx image. The Deployment name, label selector, and Pod template are all configured automatically.

Set the container port:

k create deployment my-deploy --image=nginx --replicas=3 --port=80

Generate the YAML scaffold:

k create deployment my-deploy --image=nginx --replicas=3 $do > deploy.yaml

Common exam pattern: Create a Deployment imperatively, then edit the YAML to add resource limits, environment variables, or volume mounts before applying.

To scale an existing Deployment:

k scale deployment my-deploy --replicas=5

To update the image (rolling update):

k set image deployment/my-deploy nginx=nginx:1.25

To check rollout status:

k rollout status deployment/my-deploy

To undo the last rollout:

k rollout undo deployment/my-deploy

Service Exposure: kubectl expose

Expose a Pod:

k expose pod my-pod --port=80 --target-port=80 --name=my-svc

Expose a Deployment:

k expose deployment my-deploy --port=80 --target-port=80 --type=ClusterIP

Create a NodePort service:

k expose deployment my-deploy --port=80 --target-port=80 --type=NodePort

Generate the Service YAML:

k expose deployment my-deploy --port=80 --target-port=80 $do > svc.yaml

Key distinction: --port is the Service port (what clients connect to). --target-port is the container port (where traffic is forwarded). If they are the same, you can omit --target-port.

ConfigMap Creation: kubectl create configmap

From literal values:

k create configmap my-config --from-literal=DB_HOST=mysql --from-literal=DB_PORT=3306

From a file:

k create configmap my-config --from-file=config.properties

From an entire directory:

k create configmap my-config --from-file=./config-dir/

Generate YAML:

k create configmap my-config --from-literal=key1=value1 $do > cm.yaml

Secret Creation: kubectl create secret

Generic secret from literal values:

k create secret generic my-secret --from-literal=username=admin --from-literal=password=s3cret

From a file:

k create secret generic my-secret --from-file=ssh-key=./id_rsa

Docker registry secret:

k create secret docker-registry my-reg --docker-server=registry.example.com --docker-username=user --docker-password=pass

Generate YAML:

k create secret generic my-secret --from-literal=pass=s3cret $do > secret.yaml

Exam note: Secrets created via kubectl create secret are base64-encoded automatically. If you write YAML by hand, you must base64-encode the values yourself using echo -n 'value' | base64.

Job and CronJob Creation

Create a Job:

k create job my-job --image=busybox -- echo "processing complete"

Create a Job from an existing CronJob (useful for testing):

k create job test-run --from=cronjob/my-cronjob

This triggers an immediate run of a CronJob — valuable when you need to verify CronJob behavior without waiting for the schedule.

Create a CronJob:

k create cronjob my-cron --image=busybox --schedule="*/5 * * * *" -- echo "tick"

Generate YAML for a CronJob:

k create cronjob my-cron --image=busybox --schedule="*/5 * * * *" $do > cron.yaml

Then edit the YAML to add concurrencyPolicy, activeDeadlineSeconds, successfulJobsHistoryLimit, or other fields not available as imperative flags.

The —dry-run=client -o yaml Pattern

This is the most important pattern in the entire exam. It converts any imperative create/run command into a YAML template without creating the resource:

k run nginx --image=nginx --port=80 $do > pod.yaml
k create deployment web --image=nginx --replicas=3 $do > deploy.yaml
k create configmap app-config --from-literal=env=prod $do > cm.yaml
k create secret generic db-cred --from-literal=pass=abc $do > secret.yaml
k create job batch --image=busybox $do > job.yaml
k create cronjob report --image=busybox --schedule="0 * * * *" $do > cron.yaml
k expose pod nginx --port=80 $do > svc.yaml

The workflow for every exam task:

  1. Generate — Run the imperative command with $do to produce YAML.
  2. Edit — Open the file in vim and add fields that imperative flags do not support (probes, volumes, nodeSelector, tolerations, securityContext).
  3. Applyk apply -f file.yaml
  4. Verifyk get, k describe, or k logs to confirm correctness.

This workflow takes 2–3 minutes per task. Writing YAML from memory takes 5–10 minutes and introduces indentation errors.

kubectl explain: The Built-In Reference

When you forget the exact field structure for a spec, use kubectl explain:

k explain pod.spec.containers

This prints the fields available under pod.spec.containers with types and descriptions.

For a recursive view showing the entire tree:

k explain pod.spec.containers --recursive

This outputs every nested field without descriptions — useful for seeing the structure at a glance.

Practical examples:

k explain deployment.spec.strategy
k explain pod.spec.volumes.persistentVolumeClaim
k explain cronjob.spec.jobTemplate.spec.template
k explain networkpolicy.spec.ingress

kubectl explain is faster than searching the documentation website when you know the resource type but forget a field name. Use it as your first lookup method.

kubectl api-resources: Short Names

k api-resources

This lists all resource types with their short names, API group, and whether they are namespaced. The output you care about during the exam:

Short NameFull Name
popods
deploydeployments
svcservices
cmconfigmaps
secretsecrets
pvcpersistentvolumeclaims
pvpersistentvolumes
nsnamespaces
nonodes
ingingresses
netpolnetworkpolicies
saserviceaccounts
cjcronjobs
dsdaemonsets
stsstatefulsets
rsreplicasets

Use short names everywhere:

k get po
k get deploy
k get svc
k describe cm my-config
k get pvc -n storage

Context Switching

Every exam task specifies which context and namespace to use. If you solve a task in the wrong context, you get zero points for that task. Switch context at the start of every task:

kubectl config use-context <context-name>

The task provides the exact context name. Copy-paste it — do not type it by hand.

Set the default namespace for the current context:

kubectl config set-context --current --namespace=<namespace>

This means subsequent kubectl commands operate in that namespace without requiring -n <namespace> on every command. This prevents one of the most common exam mistakes: creating a resource in the default namespace instead of the specified one.

Verify your current context and namespace:

kubectl config current-context
kubectl config view --minify | grep namespace

Pattern for every task:

  1. Read the task — note the context name and namespace.
  2. kubectl config use-context <context>
  3. kubectl config set-context --current --namespace=<ns>
  4. Solve the task.
  5. Verify the resource is in the correct namespace: k get <resource> -n <ns>

This five-step pattern eliminates namespace errors. It takes 10 seconds and saves the 10 minutes you would lose redoing a task in the wrong namespace.

Putting It All Together: A Sample Task

Task: Create a Pod named log-collector in namespace monitoring using the busybox:1.36 image. The Pod should run the command tail -f /var/log/app.log. Mount a volume named log-vol of type emptyDir at /var/log.

Approach:

  1. Switch context and namespace:
k config set-context --current --namespace=monitoring
  1. Generate scaffold:
k run log-collector --image=busybox:1.36 --command $do -- tail -f /var/log/app.log > log-collector.yaml
  1. Edit the YAML to add the volume and volume mount:
vim log-collector.yaml

Add under spec:

  volumes:
  - name: log-vol
    emptyDir: {}

Add under spec.containers[0]:

    volumeMounts:
    - name: log-vol
      mountPath: /var/log
  1. Apply:
k apply -f log-collector.yaml
  1. Verify:
k get pod log-collector -n monitoring
k describe pod log-collector -n monitoring | grep -A5 Mounts

Total time: under 3 minutes. The imperative command generated 80% of the YAML. You edited two blocks — volumes and volumeMounts. No indentation errors because vim is configured correctly. No namespace errors because you set the context first.

This is the speed difference that separates candidates who finish 15 tasks from those who finish 10.