Si el CKA certifica qui manté el clúster viu, el CKAD certifica qui construeix a sobre. És la certificació del perfil que escriu el manifest d'api-reserves, decideix quina sonda fer servir, consumeix un Secret sense exposar-lo, ajusta els resources perquè el pod no mori sota càrrega i publica una versió nova sense tallar el servei. Tot això ja ho has fet a Rutas Norte; aquesta lliçó ho reordena en clau d'examen.

El CKAD té una particularitat que el fa enganyosament dur: el temari és més senzill que el del CKA, però l'examen és més aclaparador. Hi ha moltes tasques i molt poc temps. La competència que es mesura no és només saber Kubernetes: és saber Kubernetes ràpid. Per això la meitat d'aquesta lliçó està dedicada a la velocitat amb kubectl, que és el que separa qui aprova de qui es queda a tres tasques del final.

Avís imprescindible. Preu, durada exacta, nombre de tasques, percentatge d'aprovat i repartiment de dominis canvien amb el temps. El que segueix és orientatiu i correspon al moment d'escriure. Consulta sempre el temari oficial vigent al web de la Linux Foundation / CNCF (training.linuxfoundation.org, cncf.io/certification/ckad) abans de matricular-te.

Contingut

  1. Què certifica el CKAD i en què es diferencia del CKA
  2. El format de l'examen i per què el temps és l'enemic
  3. Els dominis del temari i el seu pes orientatiu
  4. Mapa complet: cada objectiu del CKAD i la seva lliçó en aquest curs
  5. Els temes on sol fallar la gent
  6. La caixa d'eines de velocitat
  7. Taula mestra: el que et demanaran → la manera més ràpida de fer-ho
  8. Vuit tasques tipus CKAD resoltes contrarellotge

  1. Què certifica el CKAD i en què es diferencia del CKA

El CKAD acredita que saps dissenyar, construir, configurar i desplegar aplicacions natives del núvol a Kubernetes. No et pregunta com es restaura etcd ni com s'uneix un node al clúster: et dona un clúster funcionant i et demana que hi posis aplicacions a córrer correctament.

1.1 La frontera exacta entre CKA i CKAD

Pregunta CKA? CKAD?
Restaurar etcd, actualitzar el clúster amb kubeadm, arreglar un kubelet aturat No
Crear un PersistentVolume Poques vegades (sí el PVC)
Crear un Deployment amb sondes i recursos Sí, amb més profunditat
Triar entre initContainer i sidecar Marginal Sí, central
Consumir un ConfigMap de les quatre formes possibles Sí, exhaustivament
securityContext a nivell de contenidor Marginal
RBAC del clúster Sí, central Només el bàsic de ServiceAccounts
NetworkPolicies i Ingress

En una frase: el CKA administra la casa, el CKAD mobla les habitacions. La zona comuna és gran, però l'angle amb què es pregunta és diferent.

1.2 Taula comparativa CKA / CKAD / CKS

Criteri CKA CKAD CKS
Perfil Administrador / SRE / plataforma Desenvolupador / enginyer d'aplicacions Enginyer de seguretat
Focus Clúster: nodes, control plane, etcd, xarxa, RBAC Aplicacions: manifests, config, cicle de vida Enduriment, detecció i resposta
Requisit previ Cap Cap CKA vigent obligatori
Durada orientativa ~2 hores ~2 hores ~2 hores
Nre. de tasques orientatiu 15-20 15-20 15-16
Aprovat orientatiu ~66 % ~66 % ~67 %
Dificultat percebuda Mitjana-alta (diagnòstic profund) Mitjana, amb pressió de temps extrema Alta (temari dens i eines externes)
El que més suspèn No saber diagnosticar el pla de control No tenir temps d'acabar Desconèixer Falco, kube-bench, AppArmor
Documentació permesa kubernetes.io kubernetes.io kubernetes.io + Falco, Trivy i AppArmor

Ordre recomanat segons el teu perfil:

  • Desenvolupador: CKAD → CKA → (CKS si t'especialitzes en seguretat).
  • Operacions / SRE / plataforma: CKA → CKS → (CKAD per tancar el cercle).
  • Des de zero, sense experiència: CKAD primer; és més accessible i prepara el terreny per al CKA.

1.3 A qui li serveix

Desenvolupadors l'equip dels quals desplega a Kubernetes i volen deixar de dependre de l'equip de plataforma; enginyers de DevOps que escriuen manifests cada dia; perfils de QA o de release que gestionen desplegaments, sondes i configuració.


  1. El format de l'examen i per què el temps és l'enemic

Aspecte Descripció orientativa
Tipus 100 % pràctic, terminal al navegador, supervisat
Durada Al voltant de dues hores
Nombre de tasques Aproximadament entre 15 i 20
Aprovat Aproximadament un 66 %
Clústers Uns quants, amb canvi de context per tasca
Puntuació Per tasca, amb puntuacions parcials
Documentació Només kubernetes.io/docs, en una pestanya addicional
Intents Sol incloure un segon intent gratuït
Validesa Al voltant de dos anys

2.1 L'aritmètica que cal interioritzar

120 minuts / 17 tasques                         = 7,0 min per tasca
- 10 minuts de revisió final                    = 6,4 min per tasca
- ~30 s de llegir l'enunciat i canviar context  = 5,9 min per tasca

Sis minuts per llegir, entendre, executar i verificar. Escriure a mà en vim un manifest de Deployment costa 4-5 minuts. Aquí hi ha tot el problema i tota la solució: qui genera el YAML amb --dry-run i el retoca triga 90 segons; qui el tecleja sencer, no acaba l'examen.

2.2 El canvi de context

Cada tasca comença amb el seu context i el seu namespace:

kubectl config use-context rutas-norte-dev
kubectl config set-context --current --namespace=rutas-norte-dev

Resoldre una tasca al clúster equivocat val zero. I al CKAD gairebé totes indiquen un namespace concret. Tria una estratègia i sigues consistent:

  • A: fixar el namespace al context en començar cada tasca (més ràpid, exigeix recordar-se de canviar-lo).
  • B: posar -n <ns> absolutament a totes les comandes (més lent, impossible d'oblidar).

2.3 Puntuació parcial: la teva xarxa de seguretat

Una tasca típica encadena tres o quatre requisits ("crea un pod amb dos contenidors que comparteixin un emptyDir, el primer escriu i el segon llegeix"). Si fas el pod i el volum però falles el segon contenidor, ja puntues. Un pod a mitges val més que un pod inexistent: no deixis mai una tasca buida.


  1. Els dominis del temari i el seu pes orientatiu

Repartiment publicat en el moment d'escriure; verifica'l al web oficial.

Domini Pes orientatiu De què va
Disseny i construcció d'aplicacions ~20 % Patrons multicontenidor, Jobs/CronJobs, volums d'aplicació, imatges
Desplegament d'aplicacions ~20 % Deployments, estratègies, rollouts i rollbacks, Helm i Kustomize bàsics
Observabilitat i manteniment ~15 % Sondes, logs, depuració, versionatge d'API i deprecacions
Entorn, configuració i seguretat de l'aplicació ~25 % ConfigMaps, Secrets, securityContext, ServiceAccounts, resources, CRDs, quotes
Serveis i xarxes ~20 % Services, NetworkPolicies, Ingress

El domini de més pes és entorn, configuració i seguretat. És també el que més se subestima perquè "sembla fàcil": no ho és quan et demanen la variant concreta que no vas practicar.


  1. Mapa complet: cada objectiu del CKAD i la seva lliçó en aquest curs

4.1 Disseny i construcció d'aplicacions (~20 %)

Objectiu oficial Lliçó del curs
Definir, construir i modificar imatges de contenidor 08-05-seguretat-dimatges, 11-03-cicd-amb-kubernetes
Triar i fer servir patrons multicontenidor (sidecar, adapter, ambassador) 06-04-init-containers-sidecars-i-patrons
Comprendre Jobs i CronJobs 06-03-treballs-i-cronjobs
Fer servir volums d'aplicació (emptyDir, PVC) 05-01-volums, 05-03-reclamacions-de-volums-persistents
Init containers 06-04-init-containers-sidecars-i-patrons
Triar el recurs: Deployment, Pod o StatefulSet 02-01-pods, 02-03-deployments, 06-01-statefulsets

4.2 Desplegament d'aplicacions (~20 %)

Objectiu oficial Lliçó del curs
Fer servir Deployments i realitzar rolling updates 02-03-deployments, 02-04-actualitzacions-rollbacks-i-estrategies
Rollbacks i control de l'historial de revisions 02-04-actualitzacions-rollbacks-i-estrategies
Estratègies de desplegament: blue-green i canary 11-04-estrategies-blue-green-i-canary
Fer servir Helm per desplegar paquets existents 10-03-helm
Fer servir Kustomize per a variants de configuració 10-04-kustomize
Escalar aplicacions 02-03-deployments, 09-01-autoescalat-horitzontal-de-pods

4.3 Observabilitat i manteniment (~15 %)

Objectiu oficial Lliçó del curs
Comprendre el versionatge de l'API i les deprecacions 01-06-objectes-manifests-yaml-i-model-declaratiu
Implementar sondes de liveness, readiness i startup 07-01-verificacions-de-salut-i-sondes
Fer servir eines de monitoratge integrades 07-02-servidor-de-metriques-i-kubectl-top
Fer servir els logs dels contenidors 07-05-registre-centralitzat-amb-efk, 07-06-depuracio-i-esdeveniments-del-cluster
Depurar a Kubernetes 07-06-depuracio-i-esdeveniments-del-cluster

4.4 Entorn, configuració i seguretat de l'aplicació (~25 %)

Objectiu oficial Lliçó del curs
Descobrir i fer servir recursos que estenen Kubernetes (CRD, operadors) 06-06-definicions-de-recursos-personalitzats, 06-07-operadors-i-el-patro-controlador
Autenticació, autorització i control d'admissió 08-01-control-dacces-basat-en-rols, 08-03-politiques-de-seguretat-de-pods
Definir requests, limits i quotes 03-04-quotes-i-limits-de-recursos, 03-05-limitranges-i-classes-de-qos
Comprendre ConfigMaps 03-01-configmaps, 03-03-variables-dentorn
Crear i consumir Secrets 03-02-secrets
Comprendre ServiceAccounts 03-06-serviceaccounts-i-acces-a-la-api
Contextos de seguretat d'aplicació 08-02-contextos-de-seguretat-i-enduriment

4.5 Serveis i xarxes (~20 %)

Objectiu oficial Lliçó del curs
Comprensió bàsica de NetworkPolicies 04-06-politiques-de-xarxa
Proveir i diagnosticar accés des de fora del clúster 04-02-tipus-de-serveis, 04-04-controladors-dingress
Fer servir Services per exposar aplicacions 04-02-tipus-de-serveis, 04-03-dns-intern-i-descobriment-de-serveis
Ingress i regles d'encaminament 04-04-controladors-dingress, 04-05-tls-i-certificats-amb-cert-manager

  1. Els temes on sol fallar la gent

Aquests vuit blocs concentren la majoria dels suspesos.

5.1 Multicontenidor i initContainers

Un initContainer corre i acaba abans que arrenquin els contenidors principals; un sidecar corre en paral·lel durant tota la vida del pod. Des de Kubernetes 1.29 existeix el sidecar natiu: un initContainer amb restartPolicy: Always, que arrenca abans i es manté viu.

apiVersion: v1
kind: Pod
metadata:
  name: api-reserves-amb-sidecar
  namespace: rutas-norte-dev
spec:
  initContainers:
  - name: espera-postgres            # initContainer clàssic: bloqueja fins a acabar
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z postgres-reserves 5432; do sleep 2; done']
  - name: recollector-logs           # sidecar NATIU (1.29+): no acaba mai
    image: busybox:1.36
    restartPolicy: Always
    command: ['sh', '-c', 'tail -F /var/log/api/app.log']
    volumeMounts:
    - { name: logs, mountPath: /var/log/api }
  containers:
  - name: api
    image: node:20-alpine
    command: ['sh', '-c', 'while true; do date >> /var/log/api/app.log; sleep 5; done']
    volumeMounts:
    - { name: logs, mountPath: /var/log/api }
  volumes:
  - name: logs
    emptyDir: {}

Errors típics: posar el sidecar a containers quan demanen que arrenqui abans; oblidar que els contenidors d'un pod comparteixen xarxa (es veuen a localhost) però no el sistema de fitxers, que exigeix un volum compartit.

5.2 Jobs i CronJobs

Camp Significat On va
completions Execucions reeixides necessàries Job.spec
parallelism Pods simultanis Job.spec
backoffLimit Reintents abans de donar el Job per fallit Job.spec
activeDeadlineSeconds Temps màxim total Job.spec
ttlSecondsAfterFinished Esborrat automàtic en acabar Job.spec
schedule Expressió cron CronJob.spec
concurrencyPolicy Allow / Forbid / Replace CronJob.spec
startingDeadlineSeconds Marge per llançar una execució retardada CronJob.spec
suspend Pausar el CronJob CronJob.spec
successfulJobsHistoryLimit Jobs acabats que es conserven CronJob.spec
apiVersion: batch/v1
kind: CronJob
metadata: { name: informes-ocupacio, namespace: rutas-norte-pro }
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 1800
      template:
        spec:
          restartPolicy: OnFailure        # obligatori: mai "Always"
          containers:
          - name: informes
            image: rutas-norte/informes:1.4

Error clàssic: deixar restartPolicy: Always a la plantilla d'un Job; l'API el rebutja. Segon error: confondre els nivells, posant backoffLimit a CronJob.spec en lloc de a jobTemplate.spec.

5.3 Triar bé la sonda

Sonda Què passa si falla Quan fer-la servir
livenessProbe El contenidor es reinicia Detectar processos penjats
readinessProbe El pod surt dels endpoints del Service El procés viu però encara no pot atendre
startupProbe Reinicia i desactiva liveness i readiness fins a passar Arrencades lentes (JVM, migracions)
    startupProbe:
      httpGet: { path: /healthz, port: 3000 }
      failureThreshold: 30
      periodSeconds: 5          # fins a 150 s de marge per arrencar
    readinessProbe:
      httpGet: { path: /ready, port: 3000 }
      periodSeconds: 10
    livenessProbe:
      httpGet: { path: /healthz, port: 3000 }
      periodSeconds: 15
      failureThreshold: 3

Errors típics: posar livenessProbe on l'enunciat diu "no ha de rebre trànsit fins que..." (això és readiness); fer servir initialDelaySeconds gegants en lloc d'una startupProbe.

5.4 securityContext a nivell de contenidor

Camp Pod Contenidor
runAsUser, runAsGroup, runAsNonRoot Sí (guanya el del contenidor)
fsGroup No
seccompProfile
allowPrivilegeEscalation No
readOnlyRootFilesystem No
capabilities No
privileged No
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 2000
  containers:
  - name: api
    image: rutas-norte/api-reserves:2.3
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]
        add: ["NET_BIND_SERVICE"]

Error clàssic: posar capabilities o readOnlyRootFilesystem a nivell de pod. L'API rebutja el manifest i perds dos minuts buscant per què.

5.5 ConfigMaps i Secrets: les quatre formes

L'enunciat et demanarà una forma concreta. Aquesta taula cal saber-se-la.

Forma Sintaxi Quan la demanen
Variable individual env[].valueFrom.configMapKeyRef "la variable X pren el valor de la clau Y"
Totes les claus com a variables envFrom[].configMapRef "totes les claus com a variables d'entorn"
Fitxer en un volum volumes[].configMap + volumeMounts "muntat a /etc/config"
Només una clau com a fitxer volumes[].configMap.items "només la clau app.conf, a /etc/config/app.conf"
    env:
    - name: NIVELL_LOG                     # 1. variable individual
      valueFrom:
        configMapKeyRef: { name: config-api, key: nivell_log }
    - name: DB_PASSWORD                    # 1bis. des d'un Secret
      valueFrom:
        secretKeyRef: { name: credencials-postgres, key: password }
    envFrom:
    - configMapRef: { name: config-api }   # 2. totes les claus
    - secretRef: { name: credencials-postgres }
    volumeMounts:
    - { name: config, mountPath: /etc/config, readOnly: true }
  volumes:
  - name: config
    configMap:
      name: config-api
      items:                               # 4. només una clau (sense items → forma 3)
      - { key: app.conf, path: app.conf }

Creació imperativa, que és la que estalvia temps:

k create configmap config-api --from-literal=nivell_log=info --from-literal=timeout=30
k create configmap config-fitxer --from-file=app.conf
k create secret generic credencials-postgres \
  --from-literal=usuari=reserves --from-literal=password='S3cr3t0!'
k create secret docker-registry regcred --docker-server=registry.rutasnorte.es \
  --docker-username=ci --docker-password=xxx
k create secret tls botiga-web-tls --cert=tls.crt --key=tls.key

5.6 resources i el seu efecte real

Concepte Efecte
requests El que fa servir el planificador per triar node. Reserva garantida.
limits Sostre. La CPU s'estrangula; la memòria provoca OOMKilled.
Només limits Kubernetes copia limits a requests
Res Classe BestEffort: primer a morir sota pressió
Classe de QoS Condició
Guaranteed Tots els contenidors tenen requests iguals a limits, en CPU i memòria
Burstable Hi ha requests però no coincideixen amb limits (o en falten alguns)
BestEffort Ni requests ni limits en cap contenidor

Pregunta típica: "fes que el pod tingui QoS Guaranteed". Resposta: requests i limits idèntics de CPU i memòria a tots els contenidors.

5.7 Actualitzacions i reversió de Deployments

k set image deployment/api-reserves api=rutas-norte/api-reserves:2.4
k rollout status deployment/api-reserves
k rollout history deployment/api-reserves
k rollout history deployment/api-reserves --revision=3
k rollout undo deployment/api-reserves                  # a la revisió ANTERIOR
k rollout undo deployment/api-reserves --to-revision=2

# Pausar per agrupar diversos canvis en un sol rollout
k rollout pause deployment/api-reserves
k set resources deployment/api-reserves -c=api --limits=memory=512Mi
k rollout resume deployment/api-reserves
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # pods extra permesos per damunt de replicas
      maxUnavailable: 0    # cap no pot faltar → desplegament sense tall

5.8 NetworkPolicies

La fallada número u és no entendre que una NetworkPolicy que selecciona un pod el converteix en aïllat per als policyTypes declarats: a partir d'aquí només passa el que està explícitament permès.

# Denegar tot al namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: denegar-tot, namespace: rutas-norte-pro }
spec:
  podSelector: {}          # {} = tots els pods del namespace
  policyTypes: [Ingress, Egress]
---
# Permetre a api-reserves parlar amb postgres i resoldre DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: api-egress, namespace: rutas-norte-pro }
spec:
  podSelector:
    matchLabels: { app: api-reserves }
  policyTypes: [Egress]
  egress:
  - to:
    - podSelector:
        matchLabels: { app: postgres-reserves }
    ports:
    - { protocol: TCP, port: 5432 }
  - to:                          # DNS: gairebé sempre cal permetre'l
    - namespaceSelector:
        matchLabels: { kubernetes.io/metadata.name: kube-system }
    ports:
    - { protocol: UDP, port: 53 }

Detall que costa punts: el guionet ho canvia tot.

  # A) UNA entrada amb dos selectors (AND): pods app=api EN namespaces entorn=pro
  - namespaceSelector: { matchLabels: { entorn: pro } }
    podSelector: { matchLabels: { app: api } }

  # B) DUES entrades (OR): qualsevol pod de namespaces entorn=pro, MÉS
  #    els pods app=api del namespace de la política
  - namespaceSelector: { matchLabels: { entorn: pro } }
  - podSelector: { matchLabels: { app: api } }

  1. La caixa d'eines de velocitat

Aquesta és la secció que decideix l'aprovat. Tot el que segueix es tecleja als primers 60 segons.

6.1 El bloc d'arrencada

alias k=kubectl
export do='--dry-run=client -o yaml'
export now='--force --grace-period=0'
source <(kubectl completion bash)
complete -o default -F __start_kubectl k

Amb això, k run api --image=nginx $do > api.yaml genera l'esquelet i k delete pod api $now esborra a l'instant.

6.2 --dry-run=client -o yaml: el generador universal

# Pods
k run botiga-web --image=nginx:1.27-alpine $do > pod.yaml
k run debug --image=busybox:1.36 $do --command -- sleep 3600 > debug.yaml
k run api --image=node:20 --labels=app=api,tier=backend --port=3000 \
  --env=NIVELL_LOG=debug $do > api.yaml

# Deployments i Services
k create deployment api-reserves --image=rutas-norte/api:2.3 --replicas=3 $do > deploy.yaml
k expose deployment api-reserves --port=80 --target-port=3000 --name=api-svc $do > svc.yaml
k create service clusterip api-svc --tcp=80:3000 $do > svc.yaml
k create service nodeport botiga-svc --tcp=80:80 --node-port=30080 $do > svc.yaml

# Configuració, treballs, Ingress i RBAC
k create configmap config-api --from-literal=nivell=info $do > cm.yaml
k create secret generic cred --from-literal=pass=xxx $do > sec.yaml
k create job migracio --image=rutas-norte/migra:1.0 $do > job.yaml
k create cronjob informes --image=rutas-norte/informes:1.4 --schedule="0 3 * * *" $do \
  -- /bin/sh -c "informes.sh" > cj.yaml
k create ingress botiga --rule="www.rutasnorte.es/*=botiga-web-svc:80" $do > ing.yaml
k create serviceaccount suport $do > sa.yaml
k create role lector --verb=get,list --resource=pods $do > role.yaml
k create rolebinding lector-b --role=lector --serviceaccount=ns:suport $do > rb.yaml
k create quota quota-qa --hard=cpu=4,memory=8Gi,pods=20 $do > quota.yaml
k create poddisruptionbudget api-pdb --selector=app=api --min-available=2 $do > pdb.yaml

El que NO té generador imperatiu i cal copiar de la documentació: PersistentVolume, PersistentVolumeClaim, NetworkPolicy, StorageClass, securityContext complet, sondes amb tots els seus camps i qualsevol CRD.

6.3 kubectl explain: la documentació sense sortir del terminal

k explain pod.spec.containers.livenessProbe --recursive
k explain pod.spec.securityContext --recursive
k explain cronjob.spec --recursive
k explain networkpolicy | head -3        # recordar l'apiVersion correcte
k api-resources | grep -i policy

És més ràpid que obrir el navegador per a dubtes del tipus "era failureThreshold o failureCount?".

6.4 kubectl run i create imperatius: els casos que cauen

# Pods d'un sol ús, per comprovar connectivitat o DNS
k run test --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- api-svc:80
k run dns  --image=busybox:1.36 --rm -it --restart=Never -- nslookup api-svc

# Modificar objectes existents sense tocar YAML
k scale deployment api-reserves --replicas=5
k autoscale deployment api-reserves --min=2 --max=10 --cpu-percent=70
k set image deployment/api-reserves api=rutas-norte/api:2.4
k set resources deployment/api-reserves -c=api --limits=cpu=500m,memory=512Mi
k set serviceaccount deployment/api-reserves suport
k set env deployment/api-reserves NIVELL_LOG=debug
k set env deployment/api-reserves --from=configmap/config-api
k label pod botiga-web entorn=pro --overwrite
k annotate deployment api-reserves kubernetes.io/change-cause="pujada a 2.4"
k cp rutas-norte-pro/api-reserves-abc:/var/log/app.log ./app.log

6.5 Filtres i sortides que estalvien minuts

k get events -n rutas-norte-pro --sort-by=.lastTimestamp     # el més útil en depurar
k get pods -A --sort-by=.status.containerStatuses[0].restartCount
k get pods -o name
k get pod api-reserves-abc -o jsonpath='{.status.podIP}'
k get pods -o custom-columns=NOM:.metadata.name,NODE:.spec.nodeName,ESTAT:.status.phase
k get pods --field-selector status.phase=Running
k get pods -l 'entorn in (pre,pro)'
k get pods -l '!app'                                  # pods SENSE l'etiqueta app
k logs -l app=api-reserves --tail=50 --all-containers=true
k logs api-reserves-abc -c sidecar --previous          # el contenidor anterior al crash

6.6 Edició ràpida amb vim

cat <<'EOF' > ~/.vimrc
set number
set expandtab
set tabstop=2
set shiftwidth=2
set softtabstop=2
set autoindent
set paste
EOF
Ajust Per què importa
expandtab YAML prohibeix tabuladors; sense això el teu manifest no valida
tabstop/shiftwidth/softtabstop a 2 La indentació estàndard dels manifests
autoindent Manté el nivell en prémer Enter
set paste Crític: evita que l'autoindentat destrossi el YAML enganxat de la documentació
number Els errors de l'API donen número de línia
Acció Comanda Acció Comanda
Anar a la línia N :N Copiar / enganxar línia yy / p
Buscar / següent /text / n Desfer / refer u / Ctrl+r
Esborrar línia / N línies dd / Ndd Indentar bloc V, seleccionar, > o <
Reemplaçar al fitxer :%s/vell/nou/g Desar / sortir sense desar :wq / :q!

6.7 kubectl patch per a canvis puntuals

# Rèpliques
k patch deployment api-reserves -p '{"spec":{"replicas":5}}'

# Imatge d'un contenidor concret (strategic merge)
k patch deployment api-reserves \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"api","image":"rutas-norte/api:2.4"}]}}}}'

# Tipus d'un Service
k patch svc botiga-web-svc -p '{"spec":{"type":"NodePort"}}'

# JSON patch per reemplaçar un camp exacte
k patch pv pv-informes --type='json' \
  -p='[{"op":"replace","path":"/spec/persistentVolumeReclaimPolicy","value":"Retain"}]'

# YAML multilínia (còmode per a llistes)
k patch deployment worker-notificacions --type='strategic' -p '
spec:
  template:
    spec:
      tolerations:
      - { key: dedicat, operator: Equal, value: batch, effect: NoSchedule }'

Quan patch no n'hi ha prou, KUBE_EDITOR=vim k edit deployment api-reserves. Compte: hi ha camps immutables (el selector d'un Deployment, l'spec d'un Job creat). Si edit falla per això, la via és exportar, esborrar i recrear:

k get job migracio -o yaml > job.yaml   # editar, treure status i metadades volàtils
k delete job migracio && k apply -f job.yaml

  1. Taula mestra: el que et demanaran → la manera més ràpida de fer-ho

Enunciat típic Manera més ràpida
"Crea un pod X amb imatge Y" k run X --image=Y
"...que executi sleep 3600" k run X --image=Y --command -- sleep 3600
"...amb l'etiqueta app=z" k run X --image=Y --labels=app=z
"...que exposi el port 8080" k run X --image=Y --port=8080
"...i s'elimini en acabar" k run X --image=Y --rm -it --restart=Never -- <cmd>
"Crea un Deployment amb N rèpliques" k create deployment X --image=Y --replicas=N
"Escala'l a M rèpliques" k scale deployment X --replicas=M
"Exposa'l internament al port 80" k expose deployment X --port=80 --target-port=8080
"Exposa'l amb NodePort 30080" k create service nodeport X --tcp=80:8080 --node-port=30080
"Actualitza la imatge a la versió Z" k set image deployment/X c=img:Z
"Desfés l'últim desplegament" k rollout undo deployment/X
"Torna a la revisió 2" k rollout undo deployment/X --to-revision=2
"Comprova l'estat del desplegament" k rollout status deployment/X
"Autoescala'l entre 2 i 10 al 70 % de CPU" k autoscale deployment X --min=2 --max=10 --cpu-percent=70
"Crea un ConfigMap amb aquestes claus" k create configmap X --from-literal=a=1 --from-literal=b=2
"Crea un ConfigMap des d'un fitxer" k create configmap X --from-file=app.conf
"Crea un Secret amb usuari i contrasenya" k create secret generic X --from-literal=user=u --from-literal=pass=p
"Crea un Secret TLS" k create secret tls X --cert=a.crt --key=a.key
"Descodifica el valor del Secret" k get secret X -o jsonpath='{.data.pass}' | base64 -d
"Injecta totes les claus com a variables" k set env deployment/D --from=configmap/X
"Job de 5 execucions, 2 en paral·lel" k create job X --image=Y $do i afegir completions: 5, parallelism: 2
"CronJob cada 5 minuts" k create cronjob X --image=Y --schedule="*/5 * * * *" -- <cmd>
"Suspèn el CronJob" k patch cronjob X -p '{"spec":{"suspend":true}}'
"Llança el CronJob ara" k create job manual --from=cronjob/X
"Ingress per a l'amfitrió H i ruta /r" k create ingress X --class=nginx --rule="H/r*=svc:80"
"Crea una SA i fes-la servir al Deployment" k create sa X + k set serviceaccount deployment/D X
"Crea un Role i el seu RoleBinding" k create role R --verb=get,list --resource=pods + k create rolebinding B --role=R --serviceaccount=ns:sa
"Comprova que té permís" k auth can-i list pods --as=system:serviceaccount:ns:sa -n ns
"Crea una quota de recursos" k create quota X --hard=cpu=4,memory=8Gi,pods=20
"Afegeix tolerància o nodeSelector" Generar amb $do i editar, o k patch
"Etiqueta el node" k label node node-1 disc=ssd
"Troba el pod que més es reinicia" k get pods -A --sort-by=.status.containerStatuses[0].restartCount
"Desa els logs en un fitxer" k logs X -c c > /opt/sortida.log
"Logs del contenidor que va petar" k logs X --previous
"Ordena els pods per CPU" k top pods -n ns --sort-by=cpu
"Entra al contenidor" k exec -it X -c c -- sh
"Compta els pods amb l'etiqueta" k get pods -l app=x --no-headers | wc -l
"Escriu el nom del pod en un fitxer" k get pods -l app=x -o jsonpath='{.items[0].metadata.name}' > /opt/r.txt
"Elimina el pod immediatament" k delete pod X $now

  1. Vuit tasques tipus CKAD resoltes contrarellotge

Escenaris de Rutas Norte, cronòmetre en marxa, només kubernetes.io obert.

Tasca 1 — Pod multicontenidor amb volum compartit (~6 %, objectiu: 6 min)

rutas-norte-dev. Crea un Pod informes-ocupacio amb dos contenidors que comparteixin un emptyDir anomenat dades muntat a /dades. El contenidor generador (busybox:1.36) escriu la data a /dades/ocupacio.log cada 5 segons; el contenidor lector (mateixa imatge) mostra aquest fitxer per la seva sortida estàndard.

apiVersion: v1
kind: Pod
metadata: { name: informes-ocupacio, namespace: rutas-norte-dev }
spec:
  containers:
  - name: generador
    image: busybox:1.36
    command: ['sh', '-c', 'while true; do date >> /dades/ocupacio.log; sleep 5; done']
    volumeMounts: [{ name: dades, mountPath: /dades }]
  - name: lector
    image: busybox:1.36
    command: ['sh', '-c', 'tail -F /dades/ocupacio.log']
    volumeMounts: [{ name: dades, mountPath: /dades }]
  volumes:
  - name: dades
    emptyDir: {}
k logs informes-ocupacio -c lector -n rutas-norte-dev
Wed Aug  6 09:14:22 UTC 2026
Wed Aug  6 09:14:27 UTC 2026

La trampa: tail -F (majúscula) espera que el fitxer existeixi; tail -f falla si el generador encara no l'ha creat. I el volum cal muntar-lo als dos contenidors.

Tasca 2 — ConfigMap i Secret consumits de dues formes (~6 %, objectiu: 5 min)

rutas-norte-dev. Crea el ConfigMap config-api amb nivell_log=debug i max_connexions=50, i el Secret credencials-postgres amb usuari=reserves i password=Nort3!2026. Crea un Pod api-reserves (nginx:1.27-alpine) que rebi totes les claus del ConfigMap com a variables d'entorn i munti el Secret com a fitxers a /etc/secrets.

k create configmap config-api --from-literal=nivell_log=debug \
  --from-literal=max_connexions=50 -n rutas-norte-dev
k create secret generic credencials-postgres --from-literal=usuari=reserves \
  --from-literal=password='Nort3!2026' -n rutas-norte-dev
k run api-reserves --image=nginx:1.27-alpine $do -n rutas-norte-dev > api.yaml
spec:
  containers:
  - name: api-reserves
    image: nginx:1.27-alpine
    envFrom:
    - configMapRef: { name: config-api }
    volumeMounts:
    - { name: secrets, mountPath: /etc/secrets, readOnly: true }
  volumes:
  - name: secrets
    secret: { secretName: credencials-postgres }
k exec api-reserves -n rutas-norte-dev -- env | grep -E 'nivell_log|max_connexions'
k exec api-reserves -n rutas-norte-dev -- cat /etc/secrets/usuari
nivell_log=debug
max_connexions=50
reserves

La trampa: envFrom és germà d'env, no fill. I el password porta !: cal entrecometre-ho amb cometes simples perquè bash no ho interpreti.

Tasca 3 — CronJob amb política de concurrència (~6 %, objectiu: 5 min)

rutas-norte-pro. Crea un CronJob informes-nocturns a les 3:00 cada dia, imatge busybox:1.36, comanda echo informe generat. Sense execucions solapades, màxim 2 reintents, i només 3 execucions reeixides a l'historial.

k create cronjob informes-nocturns --image=busybox:1.36 --schedule="0 3 * * *" \
  -n rutas-norte-pro $do -- /bin/sh -c "echo informe generat" > cj.yaml
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: informes-nocturns
            image: busybox:1.36
            command: ["/bin/sh", "-c", "echo informe generat"]
k create job prova --from=cronjob/informes-nocturns -n rutas-norte-pro
k logs job/prova -n rutas-norte-pro
informe generat

La trampa: els nivells. backoffLimit a jobTemplate.spec; concurrencyPolicy i successfulJobsHistoryLimit a CronJob.spec. I llançar el Job amb --from=cronjob/... és la manera de verificar sense esperar a les 3 de la matinada.

Tasca 4 — Sondes ben triades (~7 %, objectiu: 6 min)

rutas-norte-pre. El Deployment api-reserves triga fins a 90 segons a arrencar. Configura'l perquè (a) no rebi trànsit fins a respondre GET /ready al 3000; (b) es reiniciï si GET /healthz falla 3 vegades seguides; (c) aquestes dues sondes no actuïn durant l'arrencada.

        startupProbe:
          httpGet: { path: /healthz, port: 3000 }
          periodSeconds: 10
          failureThreshold: 12        # 12 x 10s = 120 s de marge
        readinessProbe:
          httpGet: { path: /ready, port: 3000 }
          periodSeconds: 10
        livenessProbe:
          httpGet: { path: /healthz, port: 3000 }
          periodSeconds: 10
          failureThreshold: 3
k rollout status deployment/api-reserves -n rutas-norte-pre
k describe pod -l app=api-reserves -n rutas-norte-pre | grep -E 'Liveness|Readiness|Startup'

La trampa: la clau és el punt (c). La resposta és startupProbe, que suspèn liveness i readiness fins a passar. Resoldre-ho amb initialDelaySeconds: 90 no compleix l'enunciat amb precisió.

Tasca 5 — Rolling update sense tall i rollback (~7 %, objectiu: 7 min)

rutas-norte-pro. El Deployment botiga-web s'ha d'actualitzar sense que cap pod deixi d'estar disponible. Configura l'estratègia, actualitza la imatge a nginx:1.27.2-alpine anotant la causa, i després reverteix a la revisió anterior.

k patch deployment botiga-web -n rutas-norte-pro -p '
spec:
  strategy:
    rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }'

k set image deployment/botiga-web botiga-web=nginx:1.27.2-alpine -n rutas-norte-pro
k annotate deployment botiga-web -n rutas-norte-pro \
  kubernetes.io/change-cause="actualitzacio a nginx 1.27.2" --overwrite
k rollout status deployment/botiga-web -n rutas-norte-pro
k rollout history deployment/botiga-web -n rutas-norte-pro
REVISION  CHANGE-CAUSE
1         <none>
2         actualitzacio a nginx 1.27.2
k rollout undo deployment/botiga-web -n rutas-norte-pro
k get deployment botiga-web -n rutas-norte-pro \
  -o jsonpath='{.spec.template.spec.containers[0].image}'
nginx:1.27-alpine

La trampa: el nom del contenidor a set image no té per què coincidir amb el del Deployment. Comprova-ho abans amb -o jsonpath='{.spec.template.spec.containers[*].name}'.

Tasca 6 — securityContext restrictiu (~6 %, objectiu: 5 min)

rutas-norte-pro. Crea un Pod worker-notificacions (busybox:1.36, sleep 3600) que corri com a usuari 10001 i grup 3000, sense escalada de privilegis, amb l'arrel en només lectura i descartant totes les capacitats del nucli.

apiVersion: v1
kind: Pod
metadata: { name: worker-notificacions, namespace: rutas-norte-pro }
spec:
  securityContext:
    runAsUser: 10001
    runAsGroup: 3000
    runAsNonRoot: true
  containers:
  - name: worker
    image: busybox:1.36
    command: ["sleep", "3600"]
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities: { drop: ["ALL"] }
k exec worker-notificacions -n rutas-norte-pro -- id
k exec worker-notificacions -n rutas-norte-pro -- touch /prova
uid=10001 gid=3000
touch: /prova: Read-only file system

La trampa: runAsUser pot anar al pod o al contenidor; allowPrivilegeEscalation, readOnlyRootFilesystem i capabilities només al contenidor.

Tasca 7 — Service, Ingress i verificació (~7 %, objectiu: 7 min)

rutas-norte-pro. El Deployment botiga-web escolta al port 80. Exposa'l amb un Service ClusterIP botiga-web-svc i crea un Ingress botiga-ingress que encamini www.rutasnorte.es/ a aquest Service, amb la IngressClass nginx.

k expose deployment botiga-web --name=botiga-web-svc --port=80 --target-port=80 \
  -n rutas-norte-pro
k create ingress botiga-ingress -n rutas-norte-pro --class=nginx \
  --rule="www.rutasnorte.es/*=botiga-web-svc:80"

k describe ingress botiga-ingress -n rutas-norte-pro | grep -A4 Rules
k get endpoints botiga-web-svc -n rutas-norte-pro
Rules:
  Host                Path  Backends
  ----                ----  --------
  www.rutasnorte.es   /     botiga-web-svc:80 (10.244.1.9:80,10.244.2.4:80)

La trampa: el /* genera pathType: Prefix; sense l'asterisc obtindries Exact, que només casa l'arrel literal. Si els backends surten buits, el Service no està seleccionant pods.

Tasca 8 — Diagnòstic d'un pod que no arrenca (~7 %, objectiu: 6 min)

rutas-norte-dev. El pod redis-cache fa minuts que no passa a Running. Troba'n la causa, corregeix-la i deixa constància del motiu a /opt/diagnostic.txt.

k get pods -n rutas-norte-dev
NAME          READY   STATUS                       RESTARTS   AGE
redis-cache   0/1     CreateContainerConfigError   0          4m
k describe pod redis-cache -n rutas-norte-dev | tail -6
Events:
  Warning  Failed  2m (x12 over 4m)  kubelet
    Error: configmap "config-redis" not found
k create configmap config-redis --from-literal=maxmemory=256mb -n rutas-norte-dev
k get pods -n rutas-norte-dev
echo "El pod referenciava el ConfigMap config-redis, inexistent al namespace" \
  > /opt/diagnostic.txt

La trampa: distingir els estats. Aquesta taula resol el domini de depuració del CKAD gairebé sencer:

Estat Causa habitual
ImagePullBackOff / ErrImagePull Imatge inexistent, tag mal escrit o credencials de registre
CreateContainerConfigError ConfigMap/Secret absent o clau inexistent
CrashLoopBackOff El procés arrenca i mor: mirar k logs --previous
Pending Cap node no compleix requests/afinitat/taints, o PVC sense enllaçar
ContainerCreating perllongat Volum que no munta o CNI amb problemes
OOMKilled a lastState El límit de memòria és massa baix

Errors Comuns i Consells

Error Conseqüència Prevenció
Escriure el YAML a mà des de zero Se t'acaba el temps $do + editar
Enganxar YAML a vim sense set paste Indentació en escala, manifest invàlid ~/.vimrc al minut u
Oblidar el namespace de la tasca L'objecte no puntua set-context --current --namespace=
No canviar de context Zero punts Primera comanda, sempre
Confondre liveness amb readiness Mitja tasca perduda "trànsit" → readiness; "reiniciar" → liveness
restartPolicy: Always en un Job L'API el rebutja OnFailure o Never
capabilities a nivell de pod Manifest invàlid Només a nivell de contenidor
backoffLimit a CronJob.spec Camp ignorat o rebutjat Va a jobTemplate.spec
Encallar-se 15 minuts en una tasca Perds 3 tasques fàcils Límit de 8 min i saltar
No verificar el que has creat Et penses que puntues i no Un get/describe/exec de tancament
apply sobre camps immutables Error críptic Exportar, esborrar i recrear

Consells que marquen la diferència:

  1. Cronometra sempre. Practicar sense rellotge no prepara per al CKAD.
  2. Memoritza el bloc d'arrencada. Són 20 segons que et tornen 20 minuts.
  3. Llegeix l'enunciat buscant el substantiu clau: "que no rebi trànsit" → readiness; "totes les claus" → envFrom; "només aquesta clau" → items.
  4. Practica copiar i enganxar al terminal del navegador: les dreceres són diferents.
  5. Tingues localitzades les pàgines sense generador imperatiu: PV/PVC, NetworkPolicy, securityContext i sondes.
  6. Fes sempre la part que saps. La puntuació parcial és real.
  7. Els últims 10 minuts són per revisar, no per intentar una tasca nova.

Exercicis

Exercici 1 — Cinc tasques en quinze minuts

En un clúster de pràctica amb el namespace rutas-norte-dev, resol en 15 minuts cronometrats:

  1. Deployment botiga-web amb nginx:1.27-alpine, 4 rèpliques, exposat com a NodePort 30080.
  2. ConfigMap config-web amb entorn=dev i cache=on, injectat com a variables en aquest Deployment.
  3. Un Job migracio-bd amb busybox:1.36 i echo migrat, 3 completions i 2 en paral·lel.
  4. Un HPA per a botiga-web entre 2 i 8 rèpliques al 70 % de CPU.
  5. Escriure a /opt/pods.txt el nom de tots els pods del namespace ordenats per nom.

Exercici 2 — La tasca multicontenidor completa

Crea a rutas-norte-pre un Pod api-reserves-full que reuneixi tot el que és difícil del CKAD, en 10 minuts:

  • Un initContainer espera-db que bloquegi fins que respongui postgres-reserves:5432.
  • Un contenidor api amb nginx:1.27-alpine, sondes de readiness i liveness sobre / port 80, requests de 100m/128Mi i limits de 200m/256Mi.
  • Un sidecar natiu logs (initContainer amb restartPolicy: Always) que faci tail -F d'un fitxer compartit.
  • Un emptyDir compartit entre api i logs muntat a /var/log/nginx.
  • securityContext amb runAsNonRoot, allowPrivilegeEscalation: false i drop: ["ALL"].

Exercici 3 — Diagnòstic cec

Demana a una altra persona (o a un script) que trenqui tres coses a rutas-norte-dev sense dir-te quines: un Deployment amb imatge inexistent, un pod que referencia un Secret que no existeix, un Service amb el selector equivocat, un pod amb limits.memory massa baix, o un pod Pending per un nodeSelector impossible.

Troba i arregla les tres en 12 minuts, deixant a /opt/diagnostic.txt el símptoma i la causa de cadascuna.


Solucions

Solució a l'Exercici 1

# 1 (≈90 s)
k create deployment botiga-web --image=nginx:1.27-alpine --replicas=4
k create service nodeport botiga-web --tcp=80:80 --node-port=30080

# 2 (≈60 s)
k create configmap config-web --from-literal=entorn=dev --from-literal=cache=on
k set env deployment/botiga-web --from=configmap/config-web

# 3 (≈150 s): generar i afegir completions/parallelism
k create job migracio-bd --image=busybox:1.36 $do -- /bin/sh -c "echo migrat" > job.yaml
spec:
  completions: 3
  parallelism: 2
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: migracio-bd
        image: busybox:1.36
        command: ["/bin/sh", "-c", "echo migrat"]
k apply -f job.yaml
# 4 (≈30 s)
k autoscale deployment botiga-web --min=2 --max=8 --cpu-percent=70
# 5 (≈30 s)
k get pods --sort-by=.metadata.name -o name > /opt/pods.txt

El truc del punt 2 és k set env --from=configmap/..., que insereix l'envFrom sense tocar el YAML: escriure-ho a mà hauria costat dos minuts més. I compte amb el punt 1: create service nodeport botiga-web hereta el selector app=botiga-web perquè comparteix nom amb el Deployment; verifica-ho amb k get endpoints botiga-web abans de donar la tasca per bona.

Solució a l'Exercici 2

apiVersion: v1
kind: Pod
metadata: { name: api-reserves-full, namespace: rutas-norte-pre }
spec:
  securityContext: { runAsNonRoot: true, runAsUser: 10001 }
  initContainers:
  - name: espera-db
    image: busybox:1.36
    command: ['sh', '-c', 'until nc -z postgres-reserves 5432; do sleep 2; done']
    securityContext:
      allowPrivilegeEscalation: false
      capabilities: { drop: ["ALL"] }
  - name: logs
    image: busybox:1.36
    restartPolicy: Always            # sidecar natiu (1.29+)
    command: ['sh', '-c', 'tail -F /var/log/nginx/access.log']
    volumeMounts: [{ name: logs-nginx, mountPath: /var/log/nginx }]
    securityContext:
      allowPrivilegeEscalation: false
      capabilities: { drop: ["ALL"] }
  containers:
  - name: api
    image: nginx:1.27-alpine
    ports: [{ containerPort: 80 }]
    resources:
      requests: { cpu: 100m, memory: 128Mi }
      limits:   { cpu: 200m, memory: 256Mi }
    readinessProbe:
      httpGet: { path: /, port: 80 }
      initialDelaySeconds: 5
    livenessProbe:
      httpGet: { path: /, port: 80 }
      periodSeconds: 15
      failureThreshold: 3
    volumeMounts: [{ name: logs-nginx, mountPath: /var/log/nginx }]
    securityContext:
      allowPrivilegeEscalation: false
      capabilities: { drop: ["ALL"] }
  volumes:
  - name: logs-nginx
    emptyDir: {}

Avís pràctic: nginx:1.27-alpine amb runAsNonRoot i usuari 10001 no arrenca tal qual, perquè necessita escriure a /var/cache/nginx i /var/run. A l'examen això no importa (es corregeix el manifest, no l'execució), però a la vida real caldria muntar emptyDir en aquests directoris o fer servir nginxinc/nginx-unprivileged. És exactament el detall que vas veure a 08-02.

Solució a l'Exercici 3

La metodologia és el que es practica aquí:

k get pods -n rutas-norte-dev -o wide                    # 1. què no està Running
k describe pod <nom> -n rutas-norte-dev | tail -20       # 2. els esdeveniments: 80 % dels casos
k logs <nom> -n rutas-norte-dev --previous               # 3. si arrenca i mor
k get endpoints -n rutas-norte-dev                       # 4. si el problema és de Service
k get pods --show-labels -n rutas-norte-dev              #    casen amb el selector?
k get events -n rutas-norte-dev --sort-by=.lastTimestamp | tail -20
Símptoma observat Causa Arranjament
ErrImagePull, esdeveniment manifest unknown Tag inexistent k set image deployment/X c=imatge:tag-valid
CreateContainerConfigError Secret/ConfigMap absent Crear-lo amb k create secret/configmap
Service sense endpoints El selector no casa les etiquetes k patch svc X -p '{"spec":{"selector":{"app":"correcte"}}}'
OOMKilled a lastState.terminated.reason limits.memory baix k set resources deployment/X -c=c --limits=memory=256Mi
Pending amb didn't match node selector nodeSelector impossible Etiquetar el node o treure el selector
cat <<'EOF' > /opt/diagnostic.txt
1. botiga-web: ErrImagePull per tag nginx:9.9 inexistent -> corregit a 1.27-alpine
2. api-reserves: CreateContainerConfigError, faltava el Secret credencials-postgres -> creat
3. redis-cache-svc: sense endpoints, selector app=redis davant de l'etiqueta app=redis-cache -> corregit
EOF

Error clàssic d'aquest exercici: esborrar els pods trencats per "reiniciar-los". Si pertanyen a un Deployment, es recreen idèntics: cal corregir la causa a la plantilla o a l'objecte que falta.


Conclusió

El CKAD no premia qui més sap de Kubernetes: premia qui resol més tasques correctes per minut. El seu temari és una cara coneguda del curs —Deployments, ConfigMaps, Secrets, sondes, Jobs, Services, NetworkPolicies, securityContext— però amb un cronòmetre a sobre que converteix la fluïdesa amb kubectl en la competència decisiva.

L'essencial d'aquesta lliçó:

  • El CKAD certifica construir aplicacions sobre el clúster, no administrar-lo. La frontera amb el CKA és a etcd, nodes i pla de control, que aquí no hi entren.
  • El domini de més pes és entorn, configuració i seguretat (~25 %): ConfigMaps, Secrets, recursos, QoS i securityContext.
  • L'aritmètica mana: uns 6 minuts per tasca. Escriure YAML a mà és incompatible amb aprovar.
  • La caixa d'eines és innegociable: alias k, $do, autocompletat, .vimrc amb set paste, kubectl explain --recursive i kubectl patch.
  • Els vuit temes de l'apartat 5 —multicontenidor, Jobs, sondes, securityContext, les quatre formes de consumir configuració, resources, rollouts i NetworkPolicies— concentren la majoria de les fallades.
  • La taula de l'apartat 7 és la teva xuleta d'entrenament: enunciat típic → comanda més ràpida.
  • Consulta sempre el temari oficial vigent al web de la Linux Foundation / CNCF abans de matricular-te.

Amb el CKA cobrint l'administració i el CKAD el desenvolupament, queda el tercer vèrtex: la seguretat. A la propera lliçó abordem el CKS, la certificació més exigent de les tres, l'única que exigeix tenir el CKA vigent per presentar-s'hi, i la que et demanarà manejar amb soltesa eines que ja vas veure al mòdul 8: kube-bench, Trivy, Falco, AppArmor, seccomp, Pod Security Admission i les polítiques d'admissió.

Curs de Kubernetes

Mòdul 1: Introducció a Kubernetes

Mòdul 2: Components Principals de Kubernetes

Mòdul 3: Gestió de Configuració i Secrets

Mòdul 4: Xarxes a Kubernetes

Mòdul 5: Emmagatzematge a Kubernetes

Mòdul 6: Conceptes Avançats de Kubernetes

Mòdul 7: Monitoratge i Registre

Mòdul 8: Seguretat a Kubernetes

Mòdul 9: Escalat i Rendiment

Mòdul 10: Ecosistema i Eines de Kubernetes

Mòdul 11: Estudis de Cas i Aplicacions del Món Real

Mòdul 12: Preparació per a la Certificació de Kubernetes

© Copyright 2026. Tots els drets reservats