Guide Bash-Scripting Advanced

Bash + Kubernetes

Scripting Kubernetes operations with kubectl and Bash — deployment waits, rollout automation, log aggregation, and cluster audit scripts.

4 min read

kubectl Output Formats for Scripting

kubectl get pods -o json               # full JSON, parse with jq
kubectl get pods -o jsonpath='{.items[*].metadata.name}'    # built-in JSONPath, no jq required
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase
kubectl get pods --no-headers            # plain text, no header row — easier to pipe into awk

-o jsonpath avoids an external jq dependency for simple extractions; -o json | jq is more powerful for complex filtering.

Waiting for a Rollout to Complete

#!/usr/bin/env bash
set -euo pipefail

kubectl apply -f deployment.yaml
kubectl rollout status deployment/myapp --timeout=120s    # built-in wait, blocks until rollout finishes or times out

echo "Rollout complete"

kubectl rollout status is the correct tool here — it already implements proper waiting/polling logic; don’t hand-roll a while loop checking pod status when this exists.

Waiting for Pods to Be Ready (Custom Conditions)

kubectl wait --for=condition=Ready pod -l app=myapp --timeout=90s
kubectl wait --for=condition=Available deployment/myapp --timeout=90s

A Real Deployment Script

#!/usr/bin/env bash
set -euo pipefail

NAMESPACE="production"
DEPLOYMENT="myapp"
IMAGE_TAG="${1:?Usage: $0 <image_tag>}"

echo "Deploying $DEPLOYMENT:$IMAGE_TAG to $NAMESPACE..."
kubectl set image "deployment/$DEPLOYMENT" "myapp=myrepo/myapp:$IMAGE_TAG" -n "$NAMESPACE"

if ! kubectl rollout status "deployment/$DEPLOYMENT" -n "$NAMESPACE" --timeout=180s; then
    echo "Rollout failed — rolling back" >&2
    kubectl rollout undo "deployment/$DEPLOYMENT" -n "$NAMESPACE"
    exit 1
fi
echo "Deployment succeeded"

Finding and Diagnosing Problem Pods

# All pods NOT in Running state, across all namespaces
kubectl get pods --all-namespaces --field-selector=status.phase!=Running

# Pods that are CrashLoopBackOff specifically
kubectl get pods --all-namespaces | grep CrashLoopBackOff

# Loop through failing pods and grab their last logs automatically
kubectl get pods -n production --field-selector=status.phase!=Running -o jsonpath='{.items[*].metadata.name}' | \
tr ' ' '\n' | while read -r pod; do
    echo "=== $pod ==="
    kubectl logs "$pod" -n production --tail=20 --previous 2>/dev/null || \
    kubectl logs "$pod" -n production --tail=20
done

Aggregating Logs Across Multiple Pods

# Tail logs from every pod matching a label, prefixed with pod name
kubectl logs -l app=myapp -n production --all-containers --prefix -f

# Or loop manually for more control
for pod in $(kubectl get pods -n production -l app=myapp -o jsonpath='{.items[*].metadata.name}'); do
    echo "=== $pod ==="
    kubectl logs "$pod" -n production --tail=50
done

Cluster Audit Script

#!/usr/bin/env bash
set -euo pipefail

echo "=== Pods without resource limits ==="
kubectl get pods --all-namespaces -o json | \
    jq -r '.items[] | select(.spec.containers[].resources.limits == null) | "\(.metadata.namespace)/\(.metadata.name)"'

echo "=== Deployments with 0 replicas ==="
kubectl get deployments --all-namespaces -o json | \
    jq -r '.items[] | select(.spec.replicas == 0) | "\(.metadata.namespace)/\(.metadata.name)"'

echo "=== Nodes not Ready ==="
kubectl get nodes --no-headers | awk '$2 != "Ready" {print $1}'

Running a Command Inside a Pod From a Script

kubectl exec -n production deploy/myapp -- printenv NODE_ENV
RESULT=$(kubectl exec -n production deploy/myapp -- curl -sf http://localhost:8080/health)
echo "Health check: $RESULT"

Production Considerations

  • Prefer kubectl rollout status/kubectl wait over hand-rolled polling loops — they correctly handle the actual completion conditions Kubernetes defines, rather than an approximation.
  • Wrap deploy scripts with an automatic rollback (kubectl rollout undo) on rollout failure — a script that deploys but doesn’t handle failure gracefully leaves a cluster in a half-broken state.
  • Use -o jsonpath/-o json | jq rather than parsing the default human-readable table output — column widths and formatting in kubectl get output are not a stable contract for scripts to depend on.

Quick Interview Answer

“kubectl scripting relies on structured output (-o jsonpath or -o json piped to jq) rather than parsing the human-readable table format, which isn’t stable across versions. kubectl rollout status and kubectl wait --for=condition=... are the correct built-in tools for waiting on deployment/pod state, and should be preferred over hand-rolled polling. A solid deploy script pattern: apply the change, wait for rollout status, and automatically kubectl rollout undo on failure rather than leaving the cluster in a partially-deployed state.”

Common Mistakes

  • Parsing kubectl get pods’s default table output with awk/cut instead of -o jsonpath/-o json, breaking when column formatting changes.
  • Hand-rolling a polling loop for rollout completion instead of using kubectl rollout status.
  • Deploying without an automatic rollback path, leaving a failed rollout to be discovered and fixed manually later.

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form