DevTools

Cheatsheet Kubernetes

Orquestração de contentores para deploy, escala e gestão automatizada

Back to languages
Kubernetes
114 cards found
Categories:
Versions:

Cluster and Context


12 cards
Cluster Info
kubectl cluster-info
kubectl cluster-info dump

kubectl get nodes
kubectl get nodes -o wide
kubectl top nodes

kubectl version --short

cluster-info shows the API server address and services. get nodes lists the cluster nodes with their state (Ready, NotReady). top nodes shows CPU/memory usage (requires metrics-server).

Node Status
kubectl get nodes -o wide
kubectl describe node worker-1

kubectl get nodes --show-labels
kubectl get nodes -l kubernetes.io/the=linux

kubectl cordon worker-1
kubectl uncordon worker-1

describe node shows capacity, conditions and scheduled pods. cordon marks the node the unavailable for new pods (maintenance). uncordon reverts it. Labels such the kubernetes.io/the allow filtering nodes.

Labels and Selectors
kubectl get pods -l app=web
kubectl get pods -l 'env in (prod,staging)'
kubectl get pods -l app=web,tier=frontend

kubectl label pod my-pod tier=frontend
kubectl label pod my-pod tier-
kubectl get pods --show-labels

Labels are key/value pairs that identify resources. -l filters by selector. label pod tier=frontend adds; tier- removes. --show-labels shows all the labels of each resource.

Namespaces
kubectl get namespaces
kubectl get ns

kubectl create namespace dev
kubectl create namespace staging

kubectl get pods -n dev
kubectl get pods --all-namespaces
kubectl get pods -A

Namespaces logically isolate resources within the same cluster. -n or --namespace specifies the target namespace. -A or --all-namespaces lists resources from all namespaces.

Drain a Node
kubectl drain worker-1 \
  --ignore-daemonsets \
  --delete-emptydir-data

kubectl drain worker-1 --force

kubectl uncordon worker-1

drain evacuates a node: terminates pods gracefully and prevents new scheduling. --ignore-daemonsets is needed because DaemonSets cannot be evacuated. After maintenance, uncordon re-enables the node.

Annotations
kubectl annotate pod my-pod \
  description="Test pod"

kubectl annotate pod my-pod description-

kubectl get pod my-pod -o jsonpath=\
  '{.metadata.annotations}'

Annotations store non-identifying metadata (descriptions, URLs, contacts). Unlike labels, they are not used for selection. annotate key- removes the annotation.

Contexts and kubeconfig
kubectl config get-contexts
kubectl config current-context

kubectl config use-context prod-cluster
kubectl config set-context --current \
  --namespace=dev

kubectl config view

A context combines cluster + user + namespace. use-context switches cluster/environment. set-context --current --namespace sets the default namespace of the current context. The ~/.kube/config file stores everything.

System Components
kubectl get componentstatuses
kubectl get pods -n kube-system

kubectl get pods -n kube-system -l \
  k8s-app=kube-dns

kubectl logs -n kube-system \
  -l k8s-app=kube-dns

The kube-system namespace contains the core components: API server, etcd, scheduler, controller-manager and CoreDNS. Checking these pods is the first step when diagnosing cluster problems.

Delete Resources
kubectl delete pod my-pod
kubectl delete pods --all -n dev
kubectl delete deployment web

kubectl delete -f manifest.yaml
kubectl delete pods -l app=legacy

kubectl delete pod my-pod \
  --grace-period=0 --force

delete removes resources by name, label (-l) or file (-f). --grace-period=0 --force forces immediate removal (useful for pods stuck in Terminating). Careful with --all.

Explore the API
kubectl api-resources
kubectl api-resources --namespaced=true
kubectl api-versions

kubectl explain pod
kubectl explain pod.spec.containers
kubectl explain deployment.spec --recursive

api-resources lists all available resource types with their shortnames. explain shows a field documentation directly in the terminal — useful for exploring the structure of any object without leaving the CLI.

Formatted Output
kubectl get pods -o wide
kubectl get pods -o yaml
kubectl get pods -o json

kubectl get pod my-pod -o jsonpath=\
  '{.status.podIP}'

kubectl get pods -o custom-columns=\
  NAME:.metadata.name,STATUS:.status.phase

-o controls the output format: wide (more columns), yaml, json. jsonpath extracts specific fields. custom-columns creates custom tables with the fields you want.

Dry-run and Generate YAML
kubectl run nginx --image=nginx \
  --dry-run=client -o yaml > pod.yaml

kubectl create deployment web \
  --image=nginx --replicas=3 \
  --dry-run=client -o yaml > deploy.yaml

kubectl apply -f pod.yaml --dry-run=server

--dry-run=client simulates without creating and -o yaml generates the manifest — ideal for creating templates. --dry-run=server validates against the API server without persisting. Saves time and avoids YAML syntax errors.

Pods


12 cards
List Pods
kubectl get pods
kubectl get pods -o wide
kubectl get pods -A
kubectl get pods -w

kubectl get pods --field-selector \
  status.phase=Running

kubectl get pods --sort-by=.status.startTime

get pods lists the pods of the current namespace. -w (watch) updates in real time. --field-selector filters by fields such the status.phase. -o wide adds each pod's IP and node.

Run Commands
kubectl exec -it my-pod -- sh
kubectl exec -it my-pod -- bash

kubectl exec my-pod -- ls /app
kubectl exec my-pod -- cat /etc/config/app.yaml

kubectl exec -it my-pod -c sidecar -- sh

exec runs commands inside a running container. -it gives an interactive terminal. Everything after -- is the command. -c selects the container in pods with multiple ones. Essential for inspecting the inside of pods.

Multi-container Pods
spec:
  containers:
  - name: app
    image: myapp:1.0
  - name: sidecar
    image: fluentd:latest
  initContainers:
  - name: init-db
    image: busybox
    command: ['sh', '-c', 'sleep 5']

A pod can have several containers that share network and volumes. initContainers run sequentially before the main ones (setup, migrations). Common patterns: sidecar (logging, proxy) and ambassador (network proxy).

Create a Pod Imperatively
kubectl run nginx --image=nginx
kubectl run app --image=myapp:1.0 \
  --port=8080

kubectl run test --image=busybox \
  --rm -it -- sh

kubectl run debug --image=nicolaka/netshoot \
  --rm -it -- bash

run creates a pod directly. --rm -it creates an ephemeral pod with an interactive terminal that deletes itself on exit — perfect for debugging. --port exposes the container port (does not create a Service).

Port-forward
kubectl port-forward my-pod 8080:80
kubectl port-forward svc/web 8080:80
kubectl port-forward deployment/web 8080:80

kubectl port-forward my-pod 8080:80 \
  --address=0.0.0.0

port-forward creates a local tunnel to a pod, Service or Deployment. 8080:80 maps local port 8080 to port 80 of the pod. Ideal for testing without creating a Service. --address=0.0.0.0 allows external access.

Probes (Health Checks)
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
readinessProbe:
  httpGet:
    path: /ready
    port: 8080

livenessProbe restarts the container if it fails (stuck app). readinessProbe removes the pod from the Service if it is not ready (still starting up). Types: httpGet, tcpSocket, exec. initialDelaySeconds gives startup time.

Create a Pod Declaratively
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: web
spec:
  containers:
  - name: app
    image: nginx:1.25
    ports:
    - containerPort: 80

A Pod is the smallest schedulable unit. spec.containers defines the containers (there can be several). containerPort is informational — real exposure is done via a Service. Apply with kubectl apply -f pod.yaml.

Describe a Pod
kubectl describe pod my-pod

kubectl get pod my-pod -o yaml
kubectl get events --field-selector \
  involvedObject.name=my-pod

describe shows full details: spec, state, conditions and Events at the end. The events reveal scheduling errors, image pull issues or failed probes. It is the first command for diagnosing a troubled pod.

Copy Files
kubectl cp my-pod:/app/log.txt ./log.txt
kubectl cp ./config.yaml my-pod:/etc/config/

kubectl cp my-pod:/data ./backup \
  -c sidecar

cp copies files between the local system and a pod (like docker cp). Format: pod:/path for remote. -c specifies the container. Useful for extracting logs, configs or injecting debug files.

Logs
kubectl logs my-pod
kubectl logs -f my-pod
kubectl logs my-pod --tail=100
kubectl logs my-pod --since=1h

kubectl logs my-pod -c sidecar
kubectl logs my-pod --previous

logs shows the container output. -f follows in real time (like tail -f). -c specifies the container in multi-container pods. --previous shows the logs of the previous container (after a crash/restart).

Pod States
Pending     # waiting for scheduling/pull
Running     # running
Succeeded   # finished successfully
Failed      # finished with an error
Unknown     # undetermined state

CrashLoopBackOff  # restarting in a loop
ImagePullBackOff  # failed to pull the image

Pending indicates a lack of resources or an image being downloaded. CrashLoopBackOff means the container keeps terminating repeatedly. ImagePullBackOff indicates an image error (wrong name, private registry, missing credentials).

Delete Pods
kubectl delete pod my-pod
kubectl delete pods --all
kubectl delete pods -l app=legacy

kubectl delete pod my-pod \
  --grace-period=0 --force

kubectl delete pod my-pod --wait=false

Pods managed by Deployments are automatically recreated when deleted. --grace-period=0 --force forces immediate removal (stuck pods). --wait=false does not wait for termination. To stop for good, delete the Deployment.

Deployments e Rollouts


12 cards
Create a Deployment
kubectl create deployment web \
  --image=nginx --replicas=3

kubectl apply -f deployment.yaml

kubectl get deployments
kubectl get rs

A Deployment manages ReplicaSets that keep the desired number of pods. create deployment is imperative; apply -f is declarative (preferred). get rs shows the created ReplicaSets.

Pause and Resume a Rollout
kubectl rollout pause deployment/web
kubectl set image deployment/web \
  nginx=nginx:1.26
kubectl set resources deployment/web \
  --limits=cpu=500m,memory=256Mi
kubectl rollout resume deployment/web

pause freezes the rollout to apply multiple changes at once. On resume, Kubernetes performs a single rollout with all the changes — avoids multiple intermediate rollouts and reduces downtime.

Restart a Deployment
kubectl rollout restart deployment/web

kubectl rollout status deployment/web

rollout restart restarts all pods in a controlled way (rolling update) without changing the image. Useful after updating a mounted ConfigMap or Secret, or to clear in-memory state of the containers.

Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.25

spec.selector.matchLabels must match the template.metadata.labels — the Deployment uses that to know which pods to manage. template is the pod template the ReplicaSet replicates.

RollingUpdate Strategy
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

spec:
  strategy:
    type: Recreate

RollingUpdate replaces pods gradually (default). maxSurge = extra pods during the update; maxUnavailable: 0 guarantees zero-downtime. Recreate terminates all before creating new ones (there is downtime).

Revision History Limit
spec:
  revisionHistoryLimit: 5
  progressDeadlineSeconds: 300

revisionHistoryLimit controls how many old revisions to keep (default: 10). progressDeadlineSeconds sets the timeout for the rollout to be considered failed if it does not progress. Adjust to the speed of your deploys.

Update the Image
kubectl set image deployment/web \
  nginx=nginx:1.26

kubectl apply -f deployment.yaml

kubectl rollout status deployment/web
kubectl rollout status deployment/web \
  --timeout=60s

set image updates a container image without editing YAML. The format is container=new-image. rollout status tracks the update progress until all pods are ready.

ReplicaSet
kubectl get rs
kubectl get rs -l app=web
kubectl describe rs web-abc123

kubectl scale rs web-abc123 --replicas=5
kubectl delete rs web-abc123

The ReplicaSet ensures that N pods with the right labels are always running. It is created automatically by the Deployment. It is rarely created directly — always use a Deployment to get rollouts and history.

MinReadySeconds
spec:
  minReadySeconds: 10
  template:
    spec:
      containers:
      - name: app
        image: myapp:1.0

minReadySeconds defines how long a pod must be Ready before being considered available. It prevents the rollout from advancing too fast if the app takes time to stabilize after the readiness probe passes.

Rollback
kubectl rollout history deployment/web
kubectl rollout history deployment/web \
  --revision=2

kubectl rollout undo deployment/web
kubectl rollout undo deployment/web \
  --to-revision=2

rollout undo reverts to the previous revision. --to-revision=N reverts to a specific revision. rollout history lists the revisions. By default, Kubernetes keeps 10 revisions (revisionHistoryLimit).

Scale a Deployment
kubectl scale deployment web --replicas=5
kubectl scale deployment web --replicas=0

kubectl get deployment web -o jsonpath=\
  '{.spec.replicas}'

scale adjusts the number of replicas immediately. --replicas=0 pauses the app without deleting the Deployment (all pods terminate). Useful for staging environments that do not need to be always active.

Inspect a Deployment
kubectl get deployment web -o yaml
kubectl describe deployment web

kubectl get deployment web -o jsonpath=\
  '{.status.readyReplicas}'

kubectl get deployment web -o jsonpath=\
  '{.status.conditions[*].type}'

describe shows replicas, conditions and events. status.readyReplicas indicates how many pods are ready. conditions reveals Available, Progressing and ReplicaFailure for quick diagnosis.

Services e Networking


12 cards
Service Types
ClusterIP     # internal (default)
NodePort      # port on each node
LoadBalancer  # external LB (cloud)
ExternalName  # DNS CNAME

kubectl get svc
kubectl get svc -o wide

ClusterIP exposes only inside the cluster (default). NodePort opens a port (30000-32767) on all nodes. LoadBalancer provisions an external balancer (AWS, GCP, Azure). ExternalName creates a CNAME to an external DNS.

Internal DNS
# Format:
# <svc>.<namespace>.svc.cluster.local

curl http://web.default.svc.cluster.local
nslookup web.default

# Same namespace:
curl http://web

Each Service gets a DNS record: <name>.<namespace>.svc.cluster.local. Within the same namespace, the name is enough (http://web). CoreDNS in kube-system resolves these names automatically.

Service Without a Selector
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  ports:
  - port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-db
subsets:
- addresses:
  - ip: 10.0.0.50
  ports:
  - port: 5432

A Service without a selector does not create Endpoints automatically. You can create manual Endpoints pointing to external IPs — useful for databases outside the cluster, keeping the same internal DNS pattern.

Create a Service Imperatively
kubectl expose deployment web \
  --port=80 --target-port=8080

kubectl expose deployment web \
  --port=80 --type=NodePort

kubectl expose pod my-pod --port=8080

expose creates a Service from a Deployment or Pod. --port is the Service port; --target-port is the container port. The selector is automatically inherited from the exposed resource labels.

Headless Service
spec:
  clusterIP: None
  selector:
    app: web
  ports:
  - port: 80

A Headless Service (clusterIP: None) has no virtual IP. DNS returns the pod IPs directly. Used with StatefulSets to give each pod a stable DNS name (pod-0.svc.ns) or for manual service discovery.

Session Affinity
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600

sessionAffinity: ClientIP routes requests from the same IP always to the same pod (sticky sessions). Default is None (round-robin). Useful for apps with in-memory state, but prefer stateless + external cache.

Service Manifest
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP

The Service selector must match the labels of the target pods. port is the Service port; targetPort is the container port. The Service distributes traffic across the pods matching the selector (internal load balancing).

Endpoints
kubectl get endpoints web
kubectl describe endpoints web

kubectl get endpointslice

Endpoints lists the IPs of the pods the Service is routing to. If it is empty, the selector matches no pods or the pods are not Ready. EndpointSlice is the scalable version for large clusters.

Inspect Services
kubectl get svc -o wide
kubectl describe svc web

kubectl get svc web -o jsonpath=\
  '{.spec.clusterIP}'

kubectl get svc -A

describe svc shows selector, ports, endpoints and type. clusterIP is the internal virtual IP. If the Endpoints are empty, check whether the pod labels match the Service selector.

NodePort
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080

kubectl get svc web -o jsonpath=\
  '{.spec.ports[0].nodePort}'

NodePort exposes the Service on a fixed port (30000-32767) of all nodes. Accessible via node-IP:nodePort. If nodePort is not specified, Kubernetes assigns one automatically. Good for dev/testing.

Multi-port Service
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: metrics
    port: 9090
    targetPort: 9090

A Service can expose multiple ports. Each port needs a name when there is more than one. Useful for exposing the app (80) and metrics (9090) on the same Service. The Ingress references the port by name.

Basic NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-web
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - port: 8080

NetworkPolicy controls traffic between pods (L3/L4 firewall). podSelector defines the affected pods. ingress.from specifies who can communicate. Requires a CNI that supports NetworkPolicy (Calico, Cilium).

ConfigMaps e Secrets


12 cards
Create a ConfigMap
kubectl create configmap app-config \
  --from-file=config.yaml

kubectl create configmap vars \
  --from-literal=ENV=prod \
  --from-literal=DEBUG=false

kubectl get configmaps

ConfigMap stores non-sensitive configuration in key/value pairs. --from-file loads files; --from-literal sets values inline. Each key becomes available the an environment variable or mounted file.

Create a Secret
kubectl create secret generic db-pass \
  --from-literal=password=s3cret

kubectl create secret docker-registry \
  regcred --docker-server=registry.io \
  --docker-user=admin --docker-password=pass

kubectl get secrets

Secret stores sensitive data (passwords, tokens, keys). Values are encoded in base64 (not encrypted by default). docker-registry creates credentials for private registries. Enable etcd encryption in production.

ImagePullSecrets
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: registry.io/myapp:1.0

imagePullSecrets provides credentials for pulling images from private registries. Create it with kubectl create secret docker-registry. You can configure it on the ServiceAccount to apply to all pods in the namespace.

ConfigMap Manifest
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  ENV: "production"
  LOG_LEVEL: "info"
  app.properties: |
    server.port=8080
    server.host=0.0.0.0

data accepts simple key/value pairs or full files (with | for multi-line). Keys with a dot (e.g. app.properties) are treated the file names when mounted the a volume.

Secret Manifest
apiVersion: v1
kind: Secret
metadata:
  name: db-creds
type: Opaque
stringData:
  username: admin
  password: s3cret

# or with base64:
data:
  password: czNjcmV0

stringData accepts plain-text values (Kubernetes encodes them). data requires values already in base64. type: Opaque is the default; special types: kubernetes.io/tls, kubernetes.io/dockerconfigjson.

Edit ConfigMap/Secret
kubectl edit configmap app-config
kubectl edit secret db-creds

kubectl create configmap vars \
  --from-literal=ENV=staging \
  --dry-run=client -o yaml | kubectl apply -f -

edit opens the editor to modify directly. The --dry-run=client -o yaml | kubectl apply -f - pattern recreates the resource with new values (useful in CI/CD). Mounted volumes update; env vars require a pod restart.

ConfigMap the Env
env:
- name: ENV
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: ENV

envFrom:
- configMapRef:
    name: app-config

configMapKeyRef injects a specific key the a variable. envFrom.configMapRef injects all the ConfigMap keys the environment variables (key name = variable name). Simpler for configs with many keys.

Secret the Env
env:
- name: DB_PASS
  valueFrom:
    secretKeyRef:
      name: db-creds
      key: password

envFrom:
- secretRef:
    name: db-creds

secretKeyRef injects a Secret key the a variable. envFrom.secretRef injects all the keys. The value is decoded automatically. The app sees plain text — base64 is only transport/storage.

ConfigMap vs Secret
# ConfigMap: non-sensitive data
# - configs, URLs, flags
# - plain text in etcd

# Secret: sensitive data
# - passwords, tokens, keys
# - base64 in etcd (enable encryption)

kubectl get cm app-config -o yaml
kubectl get secret db-creds -o yaml

Use ConfigMap for non-sensitive configuration and Secret for credentials. Both have a 1 MiB limit. Secrets are base64 (not encrypted by default) — enable EncryptionConfiguration in etcd in production.

ConfigMap the Volume
volumes:
- name: config-vol
  configMap:
    name: app-config
volumeMounts:
- name: config-vol
  mountPath: /etc/config
  readOnly: true

Mounting a ConfigMap the a volume creates one file per key in mountPath. Updates to the ConfigMap propagate automatically (with a delay). readOnly: true prevents accidental writes.

Secret the Volume
volumes:
- name: secret-vol
  secret:
    secretName: db-creds
    defaultMode: 0400
volumeMounts:
- name: secret-vol
  mountPath: /etc/secrets
  readOnly: true

Mounting a Secret the a volume creates files with restricted permissions. defaultMode: 0400 guarantees read only by the owner. Updates propagate automatically. Prefer volumes over env vars for large secrets.

Immutable ConfigMap/Secret
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
immutable: true
data:
  ENV: "production"

immutable: true prevents changes after creation (improves cluster performance — the kubelet does not watch). To update, delete and recreate. Ideal for versioned configs that change via a new Deployment.

Storage e Volumes


10 cards
PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard

A PVC requests storage from the cluster. accessModes: ReadWriteOnce (1 node), ReadOnlyMany (several nodes, read), ReadWriteMany (several nodes, write). The StorageClass defines the provisioner.

View Volumes and Classes
kubectl get pv
kubectl get pvc
kubectl get storageclass
kubectl get sc

kubectl describe pvc data-pvc
kubectl get pvc -o wide

get pv shows the cluster volumes (global). get pvc shows claims per namespace. States: Bound (attached), Pending (waiting). describe pvc reveals provisioning events and bind errors.

Expand a Volume
kubectl edit pvc data-pvc
# Change: storage: 10Gi -> 20Gi

kubectl get pvc data-pvc

Expansion requires allowVolumeExpansion: true on the StorageClass. Edit the PVC to increase the size. Some provisioners expand online; others require a pod restart. Shrinking is not supported.

Use a PVC in a Pod
volumes:
- name: data
  persistentVolumeClaim:
    claimName: data-pvc
containers:
- name: app
  volumeMounts:
  - name: data
    mountPath: /var/data

The pod references the PVC by name in volumes and mounts it at mountPath. The data persists even if the pod is deleted or recreated. The PVC must be in the same namespace the the pod.

emptyDir
volumes:
- name: cache
  emptyDir: {}

- name: shared
  emptyDir:
    medium: Memory
    sizeLimit: 256Mi

emptyDir creates a temporary volume that lives the long the the pod exists. Ideal for shared cache between containers of the same pod. medium: Memory uses RAM (tmpfs) for performance. Data is lost when the pod is deleted.

Volume subPath
volumeMounts:
- name: config-vol
  mountPath: /etc/app/config.yaml
  subPath: config.yaml
- name: data
  mountPath: /data/user1
  subPath: user1

subPath mounts only one file/subdirectory of the volume instead of the whole volume. Useful for mounting a single file from a ConfigMap without replacing the entire directory. It does not auto-update with ConfigMaps.

PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs
spec:
  capacity:
    storage: 50Gi
  accessModes:
  - ReadWriteMany
  nfs:
    server: 10.0.0.10
    path: /exports/data

A PV is the actual storage resource (NFS, iSCSI, cloud disk). It can be provisioned statically (admin creates it) or dynamically (via StorageClass). The PVC binds to a compatible PV automatically.

hostPath
volumes:
- name: logs
  hostPath:
    path: /var/log/app
    type: DirectoryOrCreate

hostPath mounts a node directory into the pod. The data persists on the node but does not migrate with the pod. type: DirectoryOrCreate creates it if it does not exist. Use with caution — it couples the pod to a specific node (avoid in production).

StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

StorageClass enables dynamic provisioning: when a PVC is created, the PV is created automatically. provisioner defines the plugin (cloud, NFS, local). WaitForFirstConsumer delays creation until a pod uses the PVC.

Reclaim Policies
# Retain: PV keeps data after PVC deletion
# Delete: PV and data are deleted
# Recycle: data deleted, PV reusable

kubectl patch pv pv-nfs -p \
  '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'

reclaimPolicy defines what happens to the PV when the PVC is deleted. Retain preserves data (safe). Delete removes everything (cloud default). Recycle is deprecated. Configure it on the StorageClass or PV.

Scale and Resources


12 cards
Scale Manually
kubectl scale deployment web --replicas=5
kubectl scale deployment web --replicas=0

kubectl get deployment web
kubectl get pods -l app=web

scale adjusts replicas immediately. The ReplicaSet creates or terminates pods to reach the desired count. --replicas=0 stops the app without deleting the Deployment. Scaling down terminates pods (graceful shutdown).

ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-dev
  namespace: dev
spec:
  hard:
    pods: "20"
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi

ResourceQuota limits the total resources per namespace. It prevents a team from exhausting AS cluster. If the quota is reached, new pods are rejected. Combine it with LimitRange for per-pod defaults.

Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: web

The PDB guarantees minimum availability during voluntary disruptions (drain, upgrade). minAvailable: 2 ensures at least 2 active pods. Alternative: maxUnavailable: 1. Protects against downtime during maintenance.

HPA (Horizontal Pod Autoscaler)
kubectl autoscale deployment web \
  --min=2 --max=10 --cpu-percent=80

kubectl get hpa
kubectl describe hpa web
kubectl delete hpa web

The HPA adjusts replicas automatically based on metrics. --cpu-percent=80 scales when the average CPU exceeds 80%. It requires metrics-server and requests defined on the pods to calculate percentages.

LimitRange
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "256Mi"
    defaultRequest:
      cpu: "100m"
      memory: "64Mi"
    type: Container

LimitRange defines requests/limits defaults for containers without explicit values. It can also enforce min/max per container. Applied per namespace. Ensures no pod is left without defined limits.

Topology Spread
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: web

topologySpreadConstraints distributes pods evenly across nodes, zones or regions. maxSkew: 1 allows a maximum difference of 1 pod between topologies. DoNotSchedule rejects if it cannot distribute. Ensures high availability.

Declarative HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 75

autoscaling/v2 supports multiple metrics (CPU, memory, custom). scaleTargetRef points to the Deployment. averageUtilization is a percentage of the request. The HPA checks metrics every 15-30 seconds.

VPA (Vertical Pod Autoscaler)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  updatePolicy:
    updateMode: "Auto"

The VPA adjusts requests/limits automatically based on real usage. updateMode: Auto restarts pods with new values. Off only recommends. Do not use VPA and HPA on CPU at the same time (conflict). Requires extra installation.

Node Affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values: [ssd]

nodeAffinity controls which nodes the pods are scheduled on. required is mandatory; preferred is soft (weight). More flexible than a simple nodeSelector. Use it for SSDs, GPUs or specific zones.

Requests and Limits
resources:
  requests:
    cpu: "250m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "256Mi"

requests = guaranteed resources (used for scheduling). limits = maximum allowed. CPU in millicores (250m = 0.25 core). Memory: Mi (mebibytes). Exceeding the memory limit causes OOMKilled; CPU is throttled.

Priority and Preemption
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "Critical pods"
---
spec:
  priorityClassName: high-priority

PriorityClass defines a numeric priority. Pods with higher priority can preempt (terminate) lower-priority pods to obtain resources. Essential for critical workloads in shared clusters.

Pod Affinity/Anti-affinity
spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: web
          topologyKey: kubernetes.io/hostname

podAntiAffinity spreads pods of the same app across different nodes (HA). podAffinity groups related pods on the same node (local cache). topologyKey defines the domain: hostname, zone or region.

Advanced


12 cards
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

Ingress routes external HTTP/HTTPS traffic to internal Services. host defines the domain; path defines the route. Requires an Ingress Controller (nginx, traefik, AWS ALB). Supports TLS, rewrites and rate limiting.

ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
---
spec:
  serviceAccountName: app-sa
  containers:
  - name: app
    image: myapp:1.0

A ServiceAccount gives pods an identity to communicate with the API. Each pod uses the default SA if none is specified. Create dedicated SAs with minimal RBAC. The token is automatically mounted at /var/run/secrets/.

StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres-headless
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: db
        image: postgres:15
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

A StatefulSet is for stateful apps (DBs, queues). It guarantees: stable names (postgres-0, postgres-1), individual DNS, persistent storage per pod and ordered startup/shutdown. Requires a Headless Service.

Ingress with TLS
spec:
  tls:
  - hosts:
    - app.example.com
    secretName: tls-secret
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

tls enables HTTPS on the Ingress. secretName references a Secret of type kubernetes.io/tls with tls.crt and tls.key. Combine with cert-manager for automatic certificates via Let's Encrypt.

Jobs
apiVersion: batch/v1
kind: Job
metadata:
  name: backup
spec:
  completions: 1
  backoffLimit: 3
  template:
    spec:
      containers:
      - name: backup
        image: busybox
        command: ["sh", "-c", "echo backup done"]
      restartPolicy: Never

A Job runs a task until it completes. completions = successful runs required. backoffLimit = retries before failing. restartPolicy: Never or OnFailure (never Always). Ideal for migrations and backups.

NetworkPolicy (egress)
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: database
    ports:
    - port: 5432

policyTypes: Egress controls outgoing traffic. This example only allows the web pod to communicate with port 5432 in the database namespace. Without a NetworkPolicy, all traffic is allowed by default (zero-trust).

RBAC (Role and RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: reader
  namespace: dev
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
---
kind: RoleBinding
metadata:
  name: reader-binding
subjects:
- kind: User
  name: dev
roleRef:
  kind: Role
  name: reader

A Role defines permissions (resources + verbs) within a namespace. A RoleBinding binds the Role to users/groups/ServiceAccounts. ClusterRole/ClusterRoleBinding are global. Principle of least privilege.

CronJobs
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  template:
    spec:
      containers:
      - name: backup
        image: busybox
        command: ["sh", "-c", "echo backup"]
      restartPolicy: OnFailure

A CronJob creates Jobs on a schedule (cron format). concurrencyPolicy: Allow, Forbid (no overlap), Replace (cancels the previous one). successfulJobsHistoryLimit controls how many completed Jobs to keep.

Pod Security Standards
metadata:
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Pod Security Standards labels on the namespace: privileged (no restrictions), baseline (minimum), restricted (maximum). enforce blocks, audit logs, warn warns. Replaces the deprecated PodSecurityPolicy.

Imperative RBAC
kubectl create role reader \
  --verb=get,list,watch \
  --resource=pods,services -n dev

kubectl create rolebinding reader-bind \
  --role=reader --user=dev -n dev

kubectl auth can-i get pods -n dev
kubectl auth can-i delete pods --the=dev

create role and create rolebinding create permissions via the CLI. auth can-i checks whether an action is allowed. --the=dev impersonates another user. Essential for validating permissions before granting access.

DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: log-agent
spec:
  selector:
    matchLabels:
      app: log-agent
  template:
    metadata:
      labels:
        app: log-agent
    spec:
      containers:
      - name: fluentd
        image: fluentd:latest

A DaemonSet runs exactly 1 pod on every node (or a subset via nodeSelector). When a new node joins, the pod is created automatically. Used for logging (fluentd), monitoring (prometheus-node-exporter) and networking (CNI).

kubectl debug
kubectl debug my-pod -it --image=busybox
kubectl debug my-pod --image=nicolaka/netshoot \
  --target=app

kubectl debug node/worker-1 -it \
  --image=ubuntu

kubectl debug deployment/web \
  --image=curlimages/curl

debug creates an ephemeral container in an existing pod (no restart). --target shares the target container's network namespace. debug node/ creates a privileged pod on the node. Ideal for diagnosing pods without debug tools.

Manifests YAML


12 cards
Basic Structure
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: web
  annotations:
    team: backend
spec:
  containers:
  - name: app
    image: nginx

Every manifest has 4 required fields: apiVersion (API version), kind (resource type), metadata (name, labels) and spec (desired state). metadata.name must be unique within the namespace.

Labels and Selectors in Manifests
metadata:
  labels:
    app: web
    version: v1
    tier: frontend
spec:
  selector:
    matchLabels:
      app: web
    matchExpressions:
    - key: version
      operator: In
      values: [v1, v2]

matchLabels requires an exact match. matchExpressions supports operators: In, NotIn, Exists, DoesNotExist. The Deployment selector is immutable after creation — plan your labels well.

Security Context
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
  containers:
  - name: app
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

securityContext defines privileges: runAsNonRoot prevents root, readOnlyRootFilesystem prevents writes, drop: ALL removes Linux capabilities. Essential for hardening in production.

Multi-document YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: config
data:
  ENV: prod
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  ports:
  - port: 80

--- separates multiple resources in a single file. kubectl apply -f processes them all sequentially. Ideal for grouping related resources (Deployment + Service + ConfigMap) in one versioned file.

Environment Variables
spec:
  containers:
  - name: app
    env:
    - name: DB_HOST
      value: "postgres.default"
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: NODE_IP
      valueFrom:
        fieldRef:
          fieldPath: status.hostIP

env defines environment variables. fieldRef injects pod metadata (name, IP, namespace) — called the Downward API. Useful for logging, service discovery and pod identification without hardcoding.

Tolerations and Taints
# Taint on the node:
kubectl taint nodes worker-1 gpu=true:NoSchedule

# Toleration on the pod:
spec:
  tolerations:
  - key: "gpu"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"

Taints repel pods from nodes. Tolerations allow a pod to tolerate the taint. Effects: NoSchedule (does not schedule), PreferNoSchedule (avoids), NoExecute (evicts). Use for dedicated nodes (GPU, maintenance).

Apply vs Create
kubectl apply -f manifest.yaml
kubectl create -f manifest.yaml

kubectl apply -f ./manifests/
kubectl apply -f https://url/manifest.yaml

kubectl diff -f manifest.yaml

apply is declarative and idempotent: it creates or updates (merge). create fails if the resource already exists. apply -f ./dir/ applies every YAML in the directory. diff shows what would change without applying.

Commands and Args
spec:
  containers:
  - name: app
    command: ["sh", "-c"]
    args: ["echo $ENV && sleep 3600"]
    env:
    - name: ENV
      value: "production"

command overrides the Dockerfile ENTRYPOINT. args overrides the CMD. Both accept arrays. Environment variables defined in env are expanded in args. Essential for init scripts and debugging.

Owner References and GC
kubectl get rs -o yaml | grep ownerReferences -A5

kubectl delete deployment web \
  --cascade=foreground
kubectl delete deployment web \
  --cascade=orphan

ownerReferences link resources together (Deployment → ReplicaSet → Pod). Deleting the owner deletes the dependents (garbage collection). --cascade=orphan removes the owner but keeps the pods. foreground waits for the dependents to be deleted.

Kustomize
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
namePrefix: prod-
replicas:
- name: web
  count: 5
commonLabels:
  env: production

Kustomize customizes manifests without templates. namePrefix, replicas, patches and commonLabels transform the base. Built into kubectl: kubectl apply -k ./overlays/prod/. An alternative to Helm.

Lifecycle Hooks
lifecycle:
  postStart:
    exec:
      command: ["sh", "-c", "echo ready"]
  preStop:
    exec:
      command: ["sh", "-c", "sleep 10"]

postStart runs after the container starts (no ordering guarantee with ENTRYPOINT). preStop runs before termination — ideal for graceful shutdown (deregister from the LB, drain connections). The pod waits for preStop to complete.

Validate Manifests
kubectl apply -f manifest.yaml --dry-run=client
kubectl apply -f manifest.yaml --dry-run=server

kubectl apply -f manifest.yaml --validate=strict

kubeconform -f manifest.yaml
kubeval manifest.yaml

--dry-run=client validates syntax locally. --dry-run=server validates against the API server (more complete). --validate=strict rejects unknown fields. External tools: kubeconform, kubeval, datree.

Tips and Troubleshooting


8 cards
Cluster Events
kubectl get events --sort-by=\
  .metadata.creationTimestamp

kubectl get events -n dev --field-selector \
  type=Warning

kubectl get events -A --watch

get events shows recent events (pod creation, pull failures, OOM). --sort-by orders chronologically. type=Warning filters errors only. Events expire after ~1 hour — use centralized logging for history.

Backup and Restore (etcd)
kubectl get all -A -o yaml > backup-all.yaml

kubectl get deployment,svc,cm,secret \
  -n prod -o yaml > prod-backup.yaml

kubectl apply -f prod-backup.yaml

# etcd snapshot (admin):
ETCDCTL_API=3 etcdctl snapshot save /backup.db

Export resources with -o yaml for a declarative backup. get all does not include everything (PVCs, Secrets, Roles are missing). For a full cluster backup, snapshot etcd. Tools: Velero, kube-backup.

JSONPath and Custom Output
kubectl get pods -o jsonpath=\
  '{.items[*].metadata.name}'

kubectl get pods -o jsonpath=\
  '{.items[?(@.status.phase=="Running")].metadata.name}'

kubectl get nodes -o jsonpath=\
  '{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[-1].type}{"\n"}{end}'

jsonpath extracts specific data from the JSON. .items[*] iterates arrays. ?(@.field=="value") filters. range/end creates loops with formatting. Combine with xargs for script automation.

Namespaces the Environments
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace prod

kubectl config set-context dev-ctx \
  --cluster=my-cluster --namespace=dev

kubectl get pods -n prod
kubectl top pods -n staging

Use namespaces to isolate environments (dev, staging, prod) or teams. Combine with ResourceQuota to limit resources and NetworkPolicy to isolate traffic. Contexts with a default namespace avoid the constant -n.

Useful Aliases
alias k='kubectl'
alias kgp='kubectl get pods'
alias kgs='kubectl get svc'
alias kgd='kubectl get deployments'
alias kaf='kubectl apply -f'
alias kdp='kubectl describe pod'
alias kl='kubectl logs -f'
alias kex='kubectl exec -it'

# Autocomplete:
source <(kubectl completion bash)
complete -o default -F __start_kubectl k

Aliases speed up daily kubectl usage. completion bash enables autocomplete for resources and names. Add them to .bashrc or .zshrc. Tools like kubectx and kubens streamline context/namespace switching.

Production Best Practices
# 1. Always define requests/limits
# 2. Use liveness + readiness probes
# 3. runAsNonRoot: true
# 4. readOnlyRootFilesystem: true
# 5. Images with a specific tag (not :latest)
# 6. PodDisruptionBudget for HA
# 7. topologySpreadConstraints
# 8. Encrypted Secrets (etcd encryption)

Production requires: requests/limits (avoids noisy neighbors), probes (health checks), a restrictive securityContext, fixed image tags (reproducibility), PDB (availability during upgrades) and topologySpread (HA).

Debugging Problematic Pods
kubectl get pods
kubectl describe pod my-pod
kubectl logs my-pod --previous
kubectl exec -it my-pod -- sh

kubectl get events --field-selector \
  involvedObject.name=my-pod

kubectl top pod my-pod

Debug flow: 1) get pods (status), 2) describe (events), 3) logs --previous (previous crash), 4) exec (inside), 5) top (resources). CrashLoopBackOff → check logs; Pending → check resources/nodes.

Essential Tools
# Management:
kubectl, kubectx, kubens, k9s

# Debug:
kubectl debug, stern (logs), ktop

# Security:
kube-bench, trivy, falco, opa

# Deploy:
helm, kustomize, argocd, flux

# Monitoring:
prometheus, grafana, loki

Essential ecosystem: k9s (TUI), stern (multi-pod logs), helm (package manager), argocd (GitOps), prometheus+grafana (monitoring), trivy (image scanning), kube-bench (CIS benchmarks).