kubectl
Kubernetes cluster management with kubectl - deployments, services, debugging, and k3s specifics.
kubectl Basics
# Cluster info
kubectl cluster-info
kubectl get nodes -o wide
kubectl version --short
# Namespaces
kubectl get namespaces
kubectl create namespace dev
kubectl config set-context --current --namespace=dev
# Get resources (common pattern)
kubectl get pods
kubectl get pods -A # All namespaces
kubectl get pods -o wide # More columns
kubectl get pods -o yaml # Full YAML
kubectl get pods -o json | jq '.items[].metadata.name'
# Describe (detailed info + events)
kubectl describe pod <name>
kubectl describe node <name>
MUSCLE MEMORY: kubectl get → kubectl describe → kubectl logs
Output Formatting
# JSONPath extraction
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.addresses[0].address}{"\n"}{end}'
# Custom columns
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,IP:.status.podIP
# Go template
kubectl get pods -o go-template='{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'
# Combine with jq for complex queries
kubectl get pods -o json | jq -r '.items[] | select(.status.phase=="Running") | .metadata.name'
# Sort by field
kubectl get pods --sort-by=.metadata.creationTimestamp
kubectl get pods --sort-by=.status.startTime
# Field selector (server-side filtering)
kubectl get pods --field-selector=status.phase=Running
kubectl get pods --field-selector=metadata.namespace!=kube-system
POWER MOVE: JSONPath for simple extractions, jq for complex transformations.
Pod Operations
# Run ad-hoc pod
kubectl run debug --image=busybox --rm -it -- sh
kubectl run curl --image=curlimages/curl --rm -it -- sh
# Exec into running pod
kubectl exec -it <pod> -- bash
kubectl exec -it <pod> -c <container> -- sh # Multi-container
# Copy files
kubectl cp <pod>:/path/file ./local
kubectl cp ./local <pod>:/path/file
# Port forward (local access)
kubectl port-forward pod/<name> 8080:80
kubectl port-forward svc/<name> 8080:80
kubectl port-forward deployment/<name> 8080:80
# Logs
kubectl logs <pod>
kubectl logs <pod> -c <container> # Specific container
kubectl logs <pod> --previous # Previous instance
kubectl logs <pod> -f # Follow
kubectl logs <pod> --tail=100 # Last N lines
kubectl logs -l app=nginx # By label
kubectl logs -l app=nginx --all-containers
GOTCHA: kubectl exec uses -- to separate kubectl args from container command.
Deployments & Scaling
# Create deployment
kubectl create deployment nginx --image=nginx:alpine
kubectl create deployment nginx --image=nginx --replicas=3
# Scale
kubectl scale deployment nginx --replicas=5
kubectl autoscale deployment nginx --min=2 --max=10 --cpu-percent=80
# Update image
kubectl set image deployment/nginx nginx=nginx:1.25
kubectl rollout status deployment/nginx
# Rollback
kubectl rollout history deployment/nginx
kubectl rollout undo deployment/nginx
kubectl rollout undo deployment/nginx --to-revision=2
# Restart (triggers rolling update)
kubectl rollout restart deployment/nginx
# Pause/resume (for batch changes)
kubectl rollout pause deployment/nginx
kubectl set image deployment/nginx nginx=nginx:1.26
kubectl set resources deployment/nginx -c=nginx --limits=cpu=500m,memory=256Mi
kubectl rollout resume deployment/nginx
PATTERN: Pause → multiple changes → resume = single rollout instead of multiple.
Services & Networking
# Expose deployment as service
kubectl expose deployment nginx --port=80 --type=ClusterIP
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl expose deployment nginx --port=80 --type=LoadBalancer # Requires MetalLB
# Service types
# ClusterIP: Internal only (default)
# NodePort: Exposed on node IPs
# LoadBalancer: External IP (cloud or MetalLB)
# ExternalName: DNS CNAME alias
# Get service endpoints
kubectl get endpoints
kubectl get svc -o wide
# DNS debugging
kubectl run dnsutils --image=tutum/dnsutils --rm -it -- nslookup kubernetes
kubectl run dnsutils --image=tutum/dnsutils --rm -it -- nslookup nginx.default.svc.cluster.local
# Network policies
kubectl get networkpolicies -A
kubectl describe networkpolicy <name>
HOME LAB: MetalLB provides LoadBalancer IPs on bare metal k3s.
ConfigMaps & Secrets
# ConfigMap from literal
kubectl create configmap myconfig --from-literal=key1=value1 --from-literal=key2=value2
# ConfigMap from file
kubectl create configmap nginx-conf --from-file=nginx.conf
# View ConfigMap
kubectl get configmap myconfig -o yaml
kubectl describe configmap myconfig
# Secret from literal
kubectl create secret generic mysecret --from-literal=password=s3cr3t
# Secret from file (e.g., TLS)
kubectl create secret tls my-tls --cert=cert.pem --key=key.pem
# Decode secret (base64)
kubectl get secret mysecret -o jsonpath='{.data.password}' | base64 -d
# Edit in-place
kubectl edit configmap myconfig
kubectl edit secret mysecret
SECURITY: Secrets are base64 encoded, NOT encrypted by default. Use external secrets operator or Vault for production.
Declarative Management (apply)
# Apply manifest
kubectl apply -f deployment.yaml
kubectl apply -f https://raw.githubusercontent.com/...
# Apply directory
kubectl apply -f ./manifests/
kubectl apply -R -f ./manifests/ # Recursive
# Dry run (client-side)
kubectl apply -f deployment.yaml --dry-run=client
# Server-side dry run (validates against API)
kubectl apply -f deployment.yaml --dry-run=server
# Diff before apply
kubectl diff -f deployment.yaml
# Generate YAML (don't apply)
kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > deployment.yaml
# Delete what's in manifest
kubectl delete -f deployment.yaml
# Prune (remove resources not in manifests)
kubectl apply -f ./manifests/ --prune -l app=myapp
WORKFLOW: kubectl diff → kubectl apply --dry-run=server → kubectl apply
Debugging & Troubleshooting
# Pod not starting? Check events
kubectl describe pod <name> | tail -20
kubectl get events --sort-by='.lastTimestamp'
kubectl get events --field-selector=type=Warning
# Resource usage
kubectl top nodes
kubectl top pods
kubectl top pods --containers
# Debug with ephemeral container (k8s 1.25+)
kubectl debug -it <pod> --image=busybox --target=<container>
# Node shell (via privileged pod)
kubectl debug node/<name> -it --image=busybox
# API resources (what can I manage?)
kubectl api-resources
kubectl api-resources --namespaced=false # Cluster-scoped only
# Explain fields
kubectl explain pod.spec.containers
kubectl explain deployment.spec.strategy
# Check RBAC
kubectl auth can-i create pods
kubectl auth can-i create pods --as=system:serviceaccount:default:mysa
kubectl auth can-i --list
DEBUGGING ORDER: events → describe → logs → exec → debug container
Labels & Selectors
# Add/update labels
kubectl label pod nginx env=prod
kubectl label pod nginx env=staging --overwrite
# Remove label
kubectl label pod nginx env-
# Select by label
kubectl get pods -l app=nginx
kubectl get pods -l 'app in (nginx, apache)'
kubectl get pods -l 'app notin (test)'
kubectl get pods -l app=nginx,env=prod
# Label all pods in namespace
kubectl label pods --all env=dev
# Show labels as columns
kubectl get pods --show-labels
kubectl get pods -L app,env # Specific labels as columns
# Annotations (metadata, not selectors)
kubectl annotate pod nginx description="web server"
kubectl annotate pod nginx description- # Remove
LABELS: Used for selection. ANNOTATIONS: Used for metadata/tooling.
Contexts & Clusters
# View kubeconfig
kubectl config view
kubectl config view --minify # Current context only
# List contexts
kubectl config get-contexts
kubectl config current-context
# Switch context
kubectl config use-context prod-cluster
# Set default namespace for context
kubectl config set-context --current --namespace=monitoring
# Create new context
kubectl config set-context dev --cluster=dev-cluster --user=dev-user --namespace=dev
# Delete context
kubectl config delete-context old-cluster
# Merge kubeconfigs
KUBECONFIG=~/.kube/config:~/new-config kubectl config view --flatten > ~/.kube/merged
# Temporary context switch
kubectl --context=prod-cluster get pods
HOME LAB: k3s kubeconfig at /etc/rancher/k3s/k3s.yaml or ~/.kube/config
Power One-Liners
# Delete all pods in namespace
kubectl delete pods --all -n dev
# Delete stuck namespace
kubectl get namespace stuck -o json | jq '.spec.finalizers = []' | kubectl replace --raw "/api/v1/namespaces/stuck/finalize" -f -
# Force delete pod
kubectl delete pod nginx --grace-period=0 --force
# Get all images in cluster
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort -u
# Find pods using most CPU
kubectl top pods -A --sort-by=cpu | head -10
# Find pods with restart counts
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.status.containerStatuses[0].restartCount}{"\n"}{end}' | sort -t$'\t' -k3 -rn | head
# Watch pod status changes
kubectl get pods -w
# Get pod IPs
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'
# Cordon/drain node for maintenance
kubectl cordon node-01
kubectl drain node-01 --ignore-daemonsets --delete-emptydir-data
# ... do maintenance ...
kubectl uncordon node-01
k3s Specifics
# k3s uses containerd, not docker
sudo k3s crictl ps
sudo k3s crictl images
sudo k3s crictl logs <container-id>
# Check k3s service
sudo systemctl status k3s
sudo journalctl -u k3s -f
# k3s kubectl shortcut
sudo k3s kubectl get nodes
# kubeconfig location
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
# Or copy to user (change server: to node IP if remote)
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER ~/.kube/config
# Traefik (default ingress)
kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
# Local path provisioner (default storage)
kubectl get storageclass
# Uninstall k3s
/usr/local/bin/k3s-uninstall.sh # Server
/usr/local/bin/k3s-agent-uninstall.sh # Agent
HOME LAB: k3s is lightweight k8s for home infrastructure.
Helm Integration
# Helm repos
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
# Install chart
helm install nginx bitnami/nginx
helm install nginx bitnami/nginx --namespace web --create-namespace
# List releases
helm list
helm list -A # All namespaces
# Show values
helm show values bitnami/nginx
helm get values nginx
# Upgrade with new values
helm upgrade nginx bitnami/nginx -f values.yaml
helm upgrade nginx bitnami/nginx --set service.type=LoadBalancer
# Rollback
helm history nginx
helm rollback nginx 1
# Uninstall
helm uninstall nginx
# Template (render without installing)
helm template nginx bitnami/nginx > rendered.yaml
# Dry run
helm install nginx bitnami/nginx --dry-run
WORKFLOW: helm show values → customize values.yaml → helm install -f values.yaml
Quick Reference
| Task | Command |
|---|---|
Get all resources |
|
Watch pods |
|
Shell into pod |
|
View logs |
|
Port forward |
|
Apply manifest |
|
Scale deployment |
|
Rollback |
|
Debug pod |
|
Check events |
|
Switch namespace |
|
Resource usage |
|