Use exec and Port Forwarding for Debugging

KubernetesBeginner
Practice Now

Introduction

The previous lab focused on logs and events. Those are the first signals, but sometimes you also need to inspect a running container or temporarily reach a Service from your terminal. Kubernetes provides two common tools for this: kubectl exec and kubectl port-forward.

In this lab, you will deploy a small web application, use kubectl exec to inspect files and processes inside one Pod, and use kubectl port-forward to open a temporary local connection to the Service. The workflow is intentionally small so you can focus on the debugging techniques instead of application complexity.

Prepare a Debugging Namespace

In this step, you will prepare a clean namespace for exec and port-forward practice.

Start in /home/labex/project. You will keep the application manifest in this directory.

mkdir -p /home/labex/project
cd /home/labex/project

Check that the Kubernetes API server is reachable. If the VM has just started and the node is not ready yet, wait a few seconds and run the command again.

kubectl get nodes --request-timeout=5s

Expected output includes:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

Wait until the cluster networking Pod is Ready. Port-forwarding uses the API server connection, but the application still needs a healthy Pod and Service.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

Expected output:

pod/calico-node-... condition met

Create a namespace named ckad-prep-debug and set it as the current namespace:

kubectl create namespace ckad-prep-debug
kubectl config set-context --current --namespace=ckad-prep-debug

Confirm the current namespace:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

Expected output:

ckad-prep-debug

Deploy a Small Web Application

In this step, you will deploy a simple application to inspect and access.

The Pod writes an index.html file and runs BusyBox httpd on port 8080. A Service named debug-web exposes the Pods inside the cluster on port 80. The Service is included because kubectl port-forward can target a Service as well as a Pod.

Create debug-web.yaml:

cat > debug-web.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: debug-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: debug-web
  template:
    metadata:
      labels:
        app: debug-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: web
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - |
          mkdir -p /www
          echo "debug-web-ready" > /www/index.html
          httpd -f -p 8080 -h /www
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 1
          periodSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: debug-web
spec:
  selector:
    app: debug-web
  ports:
  - port: 80
    targetPort: 8080
EOF

Apply the manifest and wait for the Deployment:

kubectl apply -f debug-web.yaml
kubectl rollout status deployment/debug-web --timeout=120s

Expected output includes:

deployment.apps/debug-web created
service/debug-web created
deployment "debug-web" successfully rolled out

Inspect the Pods and Service:

kubectl get pods -l app=debug-web
kubectl get service debug-web

Expected output includes two Running Pods and a ClusterIP Service:

debug-web-...   1/1   Running
debug-web       ClusterIP

Inspect a Running Container with exec

In this step, you will use kubectl exec to run commands inside one application Pod.

kubectl exec is useful when the Pod is running but you need to inspect what the container sees. It is not a replacement for logs, and it should not be used to make hidden manual fixes during normal deployment. In a debugging workflow, it helps you confirm files, processes, environment, and local network behavior from inside the container.

Store one Pod name in a shell variable:

POD_NAME="$(kubectl get pods -l app=debug-web -o jsonpath='{.items[0].metadata.name}')"
echo "$POD_NAME"

Expected output is one Pod name:

debug-web-...

Run hostname inside the container. The output should match the Pod name because Kubernetes sets the container hostname from the Pod name.

kubectl exec "$POD_NAME" -- hostname

Expected output:

debug-web-...

List the running processes inside the container:

kubectl exec "$POD_NAME" -- ps

Expected output includes the httpd process:

PID   USER     TIME  COMMAND
...   root     ...   httpd -f -p 8080 -h /www

Read the file served by the web server:

kubectl exec "$POD_NAME" -- cat /www/index.html

Expected output:

debug-web-ready

Finally, test the web server from inside the same container:

kubectl exec "$POD_NAME" -- wget -qO- http://127.0.0.1:8080

Expected output:

debug-web-ready

Test the Service with Port Forwarding

In this step, you will use kubectl port-forward to access the Service from your terminal.

Port-forwarding creates a temporary tunnel from a local port to a Pod or Service in the cluster. It is useful for quick debugging because it does not require changing Service type, creating an Ingress, or opening external access. A foreground kubectl port-forward command keeps running until you stop it, so this lab starts it in the background, tests it, and then stops it.

Start a port-forward from local port 18080 to Service port 80. The output is written to /tmp/debug-web-port-forward.log so the terminal stays usable.

kubectl port-forward service/debug-web 18080:80 > /tmp/debug-web-port-forward.log 2>&1 &
PF_PID=$!
sleep 3
cat /tmp/debug-web-port-forward.log

Expected output includes:

Forwarding from 127.0.0.1:18080 -> 8080

Send a request through the local forwarded port:

wget -qO- http://127.0.0.1:18080

Expected output:

debug-web-ready

Stop the background port-forward process. This cleanup matters because otherwise the local port would remain in use.

kill "$PF_PID"
wait "$PF_PID" 2>/dev/null || true

Confirm the port-forward log recorded a handled connection:

cat /tmp/debug-web-port-forward.log

Expected output includes:

Handling connection for 18080

Summary

You practiced two practical debugging tools. You used kubectl exec to inspect a running container's hostname, processes, files, and localhost HTTP response. You also used kubectl port-forward to create a temporary local connection to a ClusterIP Service, tested it from the terminal, and stopped the background forwarding process cleanly.