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 getkubectl describekubectl 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 diffkubectl apply --dry-run=serverkubectl 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.yamlhelm install -f values.yaml

Quick Reference

Task Command

Get all resources

kubectl get all -A

Watch pods

kubectl get pods -w

Shell into pod

kubectl exec -it <pod> — bash

View logs

kubectl logs <pod> -f

Port forward

kubectl port-forward svc/<name> 8080:80

Apply manifest

kubectl apply -f file.yaml

Scale deployment

kubectl scale deploy/<name> --replicas=3

Rollback

kubectl rollout undo deploy/<name>

Debug pod

kubectl describe pod <name>

Check events

kubectl get events --sort-by='.lastTimestamp'

Switch namespace

kubectl config set-context --current --namespace=<ns>

Resource usage

kubectl top pods