Skip to content

Lab 1: An Ingress-Ready Cluster + kubectl Drills

Pairs with Phase 1. You'll create the cluster used by all later labs, then drill the kubectl verbs on a throwaway pod.

Step 1: Create the cluster

We create it with a config file instead of plain kind create cluster, because Lab 4 needs ports 80/443 mapped from your Mac into the cluster. Doing it now means never recreating the cluster later.

Save as lab-1/kind-config.yaml:

yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
bash
kind create cluster --name k8dev --config lab-1/kind-config.yaml

Expected output (trimmed):

Creating cluster "k8dev" ...
 ✓ Ensuring node image (kindest/node:...)
 ✓ Preparing nodes
 ✓ Starting control-plane
Set kubectl context to "kind-k8dev"

Already created a cluster without the config?

kind delete cluster --name k8dev and recreate it. kind clusters are disposable - deleting and recreating is normal, not a failure.

Step 2: Confirm kubectl is pointed at it

bash
kubectl config get-contexts
kubectl cluster-info
kubectl get nodes

Expected:

NAME                  STATUS   ROLES           AGE   VERSION
k8dev-control-plane   Ready    control-plane   1m    v1.3x.x

If STATUS shows NotReady, wait 30 seconds and retry - the node is still booting.

Step 3: kubectl drills

Run a throwaway nginx pod and use every core verb on it:

bash
kubectl run web --image=nginx
kubectl get pods

Expected:

NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          10s

(If STATUS is ContainerCreating, it's pulling the image - watch it transition with kubectl get pods -w, then Ctrl-C.)

Now drill each verb:

bash
kubectl describe pod web        # scroll to Events at the bottom - your future #1 debugging tool
kubectl logs web                # nginx startup logs
kubectl port-forward pod/web 8080:80   # open http://localhost:8080, then Ctrl-C
kubectl exec -it web -- sh      # you're inside the container; try `ls /etc/nginx`, then `exit`
kubectl explain pod.spec.containers   # field docs without leaving the terminal

Step 4: The lesson

Delete the pod and watch what does NOT happen:

bash
kubectl delete pod web
kubectl get pods

Expected:

No resources found in default namespace.

The pod is gone forever. Nothing recreates it, because nothing declared it should exist - you created it imperatively, like docker run. Lab 2 fixes this with a Deployment.

Step 5: Meet k9s

bash
k9s

Navigate: :pods Enter, :deployments Enter, ? for help, q to quit. Spend five minutes wandering. Use it alongside every later lab.

Verify it worked

  • [ ] kubectl get nodes shows one Ready node
  • [ ] docker ps shows a k8dev-control-plane container (the cluster IS a Docker container - that's kind)
  • [ ] You ran get, describe, logs, exec, and port-forward without looking back at this page
  • [ ] You can explain why the deleted pod stayed dead

Next: Lab 2: Deployments and Services

A VineLab lab. Released under the MIT License.