Lab Setup Solutions
SummaryStep-by-step solutions for Exercises 1 and 2: creating...
Step-by-step solutions for Exercises 1 and 2: creating...
Step-by-step solutions for Exercises 1 and 2: creating a 3-node Kind cluster with ingress port mappings, and configuring shell aliases with bash completion.
Lab Setup Solutions
Solution: Exercise 1 — Spin Up a 3-Node Kind Cluster
Step 1: Write the Cluster Configuration
Create a directory for your lab files and write the configuration:
mkdir -p ~/ckad-lab
cat > ~/ckad-lab/kind-cluster.yaml << 'EOF'
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
- containerPort: 443
hostPort: 443
- role: worker
- role: worker
EOF
The heredoc (<< 'EOF') writes the file content exactly as shown. Using single quotes around EOF prevents bash from interpreting any special characters inside the block.
Step 2: Create the Cluster
kind create cluster --config ~/ckad-lab/kind-cluster.yaml --name ckad-practice
Expected output:
Creating cluster "ckad-practice" ...
✓ Ensuring node image (kindest/node:v1.30.0) 🖼
✓ Preparing nodes 📦 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to "kind-ckad-practice"
You can now use your cluster with:
kubectl cluster-info --context kind-ckad-practice
If the image pull takes a long time on the first run, that is normal — the kindest/node image is approximately 900 MB. Subsequent cluster creations reuse the cached image.
Step 3: Verify All Nodes Are Ready
kubectl get nodes
Expected output:
NAME STATUS ROLES AGE VERSION
ckad-practice-control-plane Ready control-plane 60s v1.30.0
ckad-practice-worker Ready <none> 45s v1.30.0
ckad-practice-worker2 Ready <none> 45s v1.30.0
All three nodes must show Ready in the STATUS column. If a node shows NotReady, wait 30 seconds and run the command again. Kind’s CNI plugin (kindnet) takes a few seconds to configure networking on each node.
Step 4: Verify the Ingress Label
kubectl get nodes --show-labels | grep ingress-ready
Expected output (labels are a long comma-separated list — look for ingress-ready=true):
ckad-practice-control-plane Ready control-plane 90s v1.30.0 beta.kubernetes.io/arch=amd64,...,ingress-ready=true,...
An alternative way to verify, which is cleaner:
kubectl get nodes -l ingress-ready=true
Expected output:
NAME STATUS ROLES AGE VERSION
ckad-practice-control-plane Ready control-plane 90s v1.30.0
Only the control-plane node should appear, because only that node has the ingress-ready=true label.
Step 5: Verify Port Mappings
Check that Docker is forwarding ports 80 and 443 to the control-plane container:
docker port ckad-practice-control-plane
Expected output:
80/tcp -> 0.0.0.0:80
443/tcp -> 0.0.0.0:443
This confirms that traffic reaching your host on ports 80 and 443 will be forwarded to the control-plane container.
Troubleshooting
Problem: ERROR: failed to create cluster: node(s) already exist for a cluster with the name "ckad-practice"
Fix: A cluster with that name already exists. Delete it first:
kind delete cluster --name ckad-practice
Then rerun the create command.
Problem: Cannot connect to the Docker daemon
Fix: Docker is not running. Start it:
sudo systemctl start docker
Or on macOS, open Docker Desktop.
Problem: Port 80 or 443 already in use.
Fix: Another process (web server, reverse proxy) is bound to those ports. Find it:
sudo lsof -i :80
sudo lsof -i :443
Stop the conflicting process or change the hostPort values in your Kind configuration to different ports (e.g., 8080 and 8443).
Solution: Exercise 2 — Configure All Aliases and Verify Bash Completion
Step 1: Add the Configuration to .bashrc
Open ~/.bashrc in your preferred editor and add the following block at the end:
cat >> ~/.bashrc << 'BASHEOF'
# ============================================
# CKAD Kubernetes Shell Configuration
# ============================================
# Core aliases
alias k=kubectl
alias kaf='kubectl apply -f'
export do='--dry-run=client -o yaml'
export now='--force --grace-period 0'
# Resource shortcuts
alias kgp='kubectl get pods'
alias kgd='kubectl get deployments'
alias kgs='kubectl get services'
alias kgn='kubectl get nodes'
alias kgi='kubectl get ingress'
alias kdp='kubectl describe pod'
alias kdd='kubectl describe deployment'
alias kl='kubectl logs'
alias klf='kubectl logs -f'
alias kdel='kubectl delete'
alias kw='kubectl get pods -w'
alias kga='kubectl get all'
alias kns='kubectl config set-context --current --namespace'
# Bash completion
source <(kubectl completion bash)
complete -F __start_kubectl k
# Editor for kubectl edit
export KUBE_EDITOR="vim"
# kubectl output formatting
export COLUMNS=200
BASHEOF
Important: Using
>>(append) rather than>(overwrite) prevents destroying your existing.bashrccontent. Always double-check this before running the command.
Step 2: Source the Configuration
source ~/.bashrc
This reloads the configuration in your current terminal session. New terminal windows will load it automatically.
If you see any errors during sourcing, the most common cause is a syntax error in the aliases or a missing kubectl binary. Verify kubectl is on your PATH:
which kubectl
Step 3: Verify the k Alias
k get nodes
Expected output — identical to kubectl get nodes:
NAME STATUS ROLES AGE VERSION
ckad-practice-control-plane Ready control-plane 5m v1.30.0
ckad-practice-worker Ready <none> 4m v1.30.0
ckad-practice-worker2 Ready <none> 4m v1.30.0
Step 4: Verify Bash Completion
Type the following and press Tab twice (do not press Enter):
k get p
Then press <TAB><TAB>. You should see a list including:
persistentvolumeclaims poddisruptionbudgets pods
persistentvolumes podtemplates priorityclasses
If no completions appear, ensure bash-completion is installed:
# Check if bash-completion is installed
dpkg -l | grep bash-completion
# Install if missing
sudo apt-get install -y bash-completion
Then re-source your .bashrc.
Step 5: Verify the Dry-Run Variable
k run verify --image=nginx $do
Expected output — a valid YAML manifest printed to the terminal, with no resource created in the cluster:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: verify
name: verify
spec:
containers:
- image: nginx
name: verify
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Verify that no Pod was created:
k get pod verify
Expected output:
Error from server (NotFound): pods "verify" not found
This confirms the --dry-run=client flag worked — the command generated YAML without sending it to the API server.
Step 6: Verify Namespace Switching
# Switch to kube-system namespace
kns kube-system
Expected output:
Context "kind-ckad-practice" modified.
Now list Pods without specifying a namespace:
k get pods
Expected output — system Pods:
NAME READY STATUS RESTARTS AGE
coredns-7db6d8ff4d-xxxxx 1/1 Running 0 10m
coredns-7db6d8ff4d-yyyyy 1/1 Running 0 10m
etcd-ckad-practice-control-plane 1/1 Running 0 10m
kube-apiserver-ckad-practice-control-plane 1/1 Running 0 10m
kube-controller-manager-ckad-practice-control-plane 1/1 Running 0 10m
kube-proxy-xxxxx 1/1 Running 0 10m
kube-scheduler-ckad-practice-control-plane 1/1 Running 0 10m
Switch back to default:
kns default
Verification Checklist
Run these commands in sequence. All should succeed:
| # | Command | Expected Result |
|---|---|---|
| 1 | k get nodes | Three nodes, all Ready |
| 2 | k run test --image=nginx $do | head -3 | apiVersion: v1 / kind: Pod / metadata: |
| 3 | kns kube-system && k get pods | wc -l | At least 8 lines (header + 7 system Pods) |
| 4 | kns default | Context modified |
| 5 | echo $KUBE_EDITOR | vim |
If all five pass, Exercise 2 is complete.