Chapter 15Lesson 03~105 minutes

kubectl and Kubernetes Automation

`kubectl` is a remote API client whose current context can point at very different clusters. Production Bash must verify targeting before mutation and rely on Kubernetes conditions instead of human-formatted output or guessed sleeps.

IntermediateDevOps CLI integrationHands-on lab

Learning objectives

By the end of this lesson

  • Validate current context before mutation.
  • Pass namespaces explicitly.
  • Use machine-oriented output.
  • Wait on rollout conditions.
  • Know when to use Helm, Kustomize, or GitOps instead of Bash.

1. kubectl is a client for a remote API

Unlike local process commands, kubectl usually talks to a Kubernetes API server using current context, cluster, user credentials, and namespace. A safe script must make those targeting assumptions visible.

kubectl control path
flowchart LR
  B["Bash"] --> K["kubectl"]
  K --> A["Kubernetes API"]
  A --> O["objects / status"]
  O --> K
  K --> B

2. Validate context before mutation

context=$(kubectl config current-context)

printf 'context=%s\n' "$context"

[[ $context == "$EXPECTED_CONTEXT" ]] || {
  printf 'refusing unexpected context\n' >&2
  exit 65
}
Cluster targeting is safety-critical

A script that accidentally points at production instead of staging can make perfectly valid commands disastrously wrong.

3. Pass namespace explicitly

kubectl \
  --namespace "$namespace" \
  get deployment api

Do not depend on whichever default namespace is currently stored in a user's kubeconfig.

4. Use structured output formats

image=$(
  kubectl \
    --namespace "$namespace" \
    get deployment api \
    -o jsonpath='{.spec.template.spec.containers[0].image}'
)

printf 'image=%s\n' "$image"

Prefer JSON, JSONPath, or other machine-oriented formats instead of parsing aligned table columns.

5. Use rollout and wait primitives instead of blind sleeps

kubectl \
  --namespace "$namespace" \
  rollout status deployment/api \
  --timeout=120s

A fixed sleep 30 guesses how long readiness will take. Kubernetes already exposes object conditions and rollout status.

6. Declarative apply is usually safer than imperative mutation

kubectl \
  --namespace "$namespace" \
  apply -f deployment.yaml

Declarative manifests are reviewable and rerunnable. Imperative commands can still be appropriate for narrow operational tasks, but should not become an invisible configuration system inside Bash.

7. Validate manifests before changing the cluster

kubectl \
  --namespace "$namespace" \
  apply \
  --dry-run=server \
  -f deployment.yaml

Server-side dry-run asks the API server to validate the request without persisting it, which can catch policy and schema problems earlier.

8. Patches need explicit structured payloads

patch=$(
  jq -n \
    --arg image "$image" \
    '{
      spec: {
        template: {
          spec: {
            containers: [
              {name: "api", image: $image}
            ]
          }
        }
      }
    }'
)

kubectl \
  --namespace "$namespace" \
  patch deployment api \
  --type strategic \
  --patch "$patch"

Use structured generators rather than hand-escaped JSON inside nested shell quotes.

9. Preserve kubectl status and collect focused diagnostics

if kubectl -n "$namespace" rollout status deployment/api --timeout=120s; then
  printf 'rollout=ok\n'
else
  status=$?
  kubectl -n "$namespace" get pods -o wide >&2 || true
  kubectl -n "$namespace" describe deployment api >&2 || true
  exit "$status"
fi

10. Know when Bash is orchestrating too much cluster logic

If a script starts implementing dependency ordering, drift correction, retries across many resources, rollout strategy, and environment overlays, move that logic into Kubernetes-native configuration tooling, Helm, Kustomize, GitOps, or another purpose-built layer.

11. Hands-on lab: read-only Kubernetes preflight

mkdir -p "$HOME/devops-academy/bash/chapter15/lesson03"
cd "$HOME/devops-academy/bash/chapter15/lesson03"

cat > kube-preflight.sh <<'EOF'
#!/usr/bin/env bash
set -u

namespace=${1:-default}

command -v kubectl >/dev/null 2>&1 || {
  printf 'kubectl is required\n' >&2
  exit 69
}

context=$(kubectl config current-context 2>/dev/null) || {
  printf 'no current Kubernetes context\n' >&2
  exit 69
}

printf 'context=%s namespace=%s\n' "$context" "$namespace"

if kubectl \
  --namespace "$namespace" \
  get namespace "$namespace" \
  -o name >/dev/null 2>&1; then
  printf 'api_access=ok\n'
else
  status=$?
  printf 'api_access=failed status=%d\n' "$status" >&2
  exit "$status"
fi
EOF

chmod u+x kube-preflight.sh
printf 'Run ./kube-preflight.sh NAMESPACE against a configured cluster.\n'

Verification checklist

12. Knowledge check

Question 1. Why validate the current Kubernetes context?

Question 2. Why prefer rollout status over sleep?

Question 3. Why use structured output such as JSONPath?

Question 4. When should Bash give way to Helm/Kustomize/GitOps?

13. Summary

Safe kubectl automation makes cluster, context, namespace, and output format explicit; validates before mutation; waits on Kubernetes conditions rather than time guesses; and keeps complex desired-state logic outside ad-hoc shell code.

14. Further reading

  • Kubernetes kubectl reference.
  • Kubernetes documentation — contexts and namespaces.
  • Kubernetes rollout and wait documentation.
  • Kustomize, Helm, and GitOps documentation for larger configuration workflows.
Next lesson

systemctl, journalctl, and Service Workflows

Continue Chapter 15 by applying safe Bash orchestration patterns to another core DevOps tool.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.