Challenge, Easy,  on  Kubernetes

Kubernetes Workloads: Pods

A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers, gives them a shared network namespace and shared volumes, and is the object every workload controller creates and manages. Unlike a Deployment or ReplicaSet, a bare Pod created via kubectl or the API has no controller watching it. If it is deleted, nothing recreates it (static pods are an exception).


Task 1 - Create a Pod

Create a Pod and verify it reaches Running state.

Steps:

  • Create a Pod named sleeper using image busybox, running sleep 3600

Run kubectl get pod sleeper - the STATUS column shows Running and RESTARTS shows 0. Run kubectl describe pod sleeper to see the node it was scheduled to, the cluster-internal IP, and the Events section tracing the Pod through Scheduled, Pulled, and Started.


Task 2 - Exec into the Pod

kubectl exec runs a command inside a container. Each Pod gets its own cluster-internal IP address, separate from the node it runs on.

Steps:

  • Exec into sleeper and run hostname -i to get its IP address, then type the value into the input box below

kubectl exec connects to the running container and runs the command inside it. The connection closes as soon as the command exits - it is not a persistent shell session. To open an interactive shell instead, pass -it -- sh as the arguments. The IP returned by hostname -i is the Pod IP, visible in kubectl get pod sleeper -o wide under the IP column.


Task 3 - Pod Immutability

Most Pod spec fields cannot be changed after the Pod is created. Container ports, volume mounts, and restart policy are examples. Trying to change them via kubectl edit produces a validation error. To apply the change, you must delete the Pod and recreate it with the updated spec.

Steps:

  • Run kubectl edit pod sleeper and try to add ports with containerPort: 80 under the container
  • Save and observe the validation error
  • Delete sleeper
  • Create a new Pod named sleeper with image busybox, running sleep 3600, and containerPort: 80

containerPort in the container spec is metadata only - it does not open or close any network ports on the container.


Task 4 - restartPolicy: Always

The restartPolicy field controls what the kubelet does when a container exits. It is set once, at Pod creation, and cannot be changed afterward. Always is the default. The kubelet restarts the container on any exit, whether the command failed or completed successfully. A Pod with restartPolicy: Always has no terminal phase - the container is always brought back. Every workload controller restricts which values its pod template accepts: Deployments, ReplicaSets, DaemonSets, and StatefulSets accept only Always, while Jobs and CronJobs require OnFailure or Never and reject Always.

Steps:

  • Create a Pod named policy-always with image busybox, running sh -c "echo done", and restartPolicy: Always

Run kubectl get pods and watch the STATUS column for policy-always. It cycles through Running, Completed (briefly, as the container exits), then CrashLoopBackOff as the kubelet applies exponential backoff before the next restart. The RESTARTS counter climbs with each cycle. Meanwhile, kubectl get pod policy-always -o jsonpath='{.status.phase}' always returns Running - the Pod phase and the container state are separate.

Note: STATUS column vs .status.phase vs container state

The STATUS column in kubectl get pods is not .status.phase. It is derived from the container's state. A container that just exited with code 0 can show Completed in STATUS, and a container restarting repeatedly can show CrashLoopBackOff, even while .status.phase still reports Running because restartPolicy: Always keeps the Pod running. To see the actual phase, check the Status field in kubectl describe pod, or run kubectl get pod <name> -o jsonpath='{.status.phase}'. See Pod Lifecycle - Kubernetes Docs for the Pod phase and container state definitions.


Task 5 - restartPolicy: OnFailure

OnFailure restarts the container only when it exits with a non-zero code. A container that exits successfully is left stopped. The kubelet restarts the container in place - the Pod is not replaced and its UID does not change.

Steps:

  • Create a Pod named policy-onfailure with image busybox, running sh -c "exit 1", and restartPolicy: OnFailure

Run kubectl get pod policy-onfailure and watch RESTARTS climb. The STATUS column shows CrashLoopBackOff between retries. If the same container command exited with code 0 instead, OnFailure would leave it stopped and the Pod would stop without restarting - unlike Always, which restarts even on success.


Task 6 - restartPolicy: Never

Never leaves the container terminated after it exits, regardless of the exit code. No further attempts are made by the kubelet.

Steps:

  • Create a Pod named policy-never with image busybox, running sh -c "exit 1", and restartPolicy: Never

Run kubectl get pod policy-never - STATUS shows Error and RESTARTS shows 0. The Pod phase is Failed. Run kubectl get pod policy-never -o jsonpath='{.status.containerStatuses[*].restartCount}' to confirm the count stayed at 0. The container ran once, exited with code 1, and the kubelet took no further action.

Note: phase and STATUS agree here

Note .status.phase for policy-never and the Status field shown by kubectl describe, both Failed. The STATUS column in kubectl get pods shows Error.


Task 7 - terminationGracePeriodSeconds

.spec.terminationGracePeriodSeconds sets how long the kubelet waits between sending SIGTERM and SIGKILL when a Pod is deleted. The kubelet sends SIGTERM first, giving the container a chance to shut down on its own. If the container is still running once the grace period elapses, the kubelet sends SIGKILL, which terminates it immediately. The default is 30 seconds. kubectl delete pod also accepts a --grace-period flag that overrides the Pod's configured value for that single deletion.

A process running as PID 1 does not get the kernel's default signal handling, so commands like sleep ignore SIGTERM unless they explicitly handle it. Such containers always run out the full grace period and get SIGKILLed.

On the CKAD exam, deleting Pods with the default 30 second grace period repeatedly wastes time. Passing --grace-period with a low value (or 0 with --force) speeds up cleanup between tasks.

Steps:

  • Create a Pod named slow-shutdown with image busybox, running sleep 3600, and .spec.terminationGracePeriodSeconds: 1
  • Delete slow-shutdown and time the deletion

The deletion completed in about 1 second. The --grace-period flag overrides only the single deletion without touching the Pod spec - on the CKAD exam, passing --grace-period=1 or --grace-period=0 --force on any Pod whose process ignores SIGTERM avoids stalling between tasks.