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
- Què certifica el CKAD i en què es diferencia del CKA
- El format de l'examen i per què el temps és l'enemic
- Els dominis del temari i el seu pes orientatiu
- Mapa complet: cada objectiu del CKAD i la seva lliçó en aquest curs
- Els temes on sol fallar la gent
- La caixa d'eines de velocitat
- Taula mestra: el que et demanaran → la manera més ràpida de fer-ho
- Vuit tasques tipus CKAD resoltes contrarellotge
- 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 | Sí | No |
| Crear un PersistentVolume | Sí | Poques vegades (sí el PVC) |
| Crear un Deployment amb sondes i recursos | Sí | Sí, amb més profunditat |
| Triar entre initContainer i sidecar | Marginal | Sí, central |
| Consumir un ConfigMap de les quatre formes possibles | Sí | Sí, exhaustivament |
securityContext a nivell de contenidor |
Marginal | Sí |
| RBAC del clúster | Sí, central | Només el bàsic de ServiceAccounts |
| NetworkPolicies i Ingress | Sí | Sí |
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ó.
- 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 tascaSis 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-devResoldre 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.
- 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.
- 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 |
- 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.4Error 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: 3Errors 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í | Sí (guanya el del contenidor) |
fsGroup |
Sí | No |
seccompProfile |
Sí | Sí |
allowPrivilegeEscalation |
No | Sí |
readOnlyRootFilesystem |
No | Sí |
capabilities |
No | Sí |
privileged |
No | Sí |
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.key5.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-reservesspec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # pods extra permesos per damunt de replicas
maxUnavailable: 0 # cap no pot faltar → desplegament sense tall5.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 } }
- 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 kAmb 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.yamlEl 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.log6.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 crash6.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
- 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 |
- 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 Podinformes-ocupacioamb dos contenidors que comparteixin unemptyDiranomenatdadesmuntat a/dades. El contenidorgenerador(busybox:1.36) escriu la data a/dades/ocupacio.logcada 5 segons; el contenidorlector(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: {}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 ConfigMapconfig-apiambnivell_log=debugimax_connexions=50, i el Secretcredencials-postgresambusuari=reservesipassword=Nort3!2026. Crea un Podapi-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.yamlspec:
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/usuariLa 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 CronJobinformes-nocturnsa les 3:00 cada dia, imatgebusybox:1.36, comandaecho 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.yamlspec:
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-proLa 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 Deploymentapi-reservestriga fins a 90 segons a arrencar. Configura'l perquè (a) no rebi trànsit fins a respondreGET /readyal 3000; (b) es reiniciï siGET /healthzfalla 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: 3k 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 Deploymentbotiga-webs'ha d'actualitzar sense que cap pod deixi d'estar disponible. Configura l'estratègia, actualitza la imatge anginx:1.27.2-alpineanotant 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-prok 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}'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 Podworker-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 /provaLa 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 Deploymentbotiga-webescolta al port 80. Exposa'l amb un Service ClusterIPbotiga-web-svci crea un Ingressbotiga-ingressque encaminiwww.rutasnorte.es/a aquest Service, amb la IngressClassnginx.
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-proRules:
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 podredis-cachefa minuts que no passa aRunning. Troba'n la causa, corregeix-la i deixa constància del motiu a/opt/diagnostic.txt.
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.txtLa 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:
- Cronometra sempre. Practicar sense rellotge no prepara per al CKAD.
- Memoritza el bloc d'arrencada. Són 20 segons que et tornen 20 minuts.
- Llegeix l'enunciat buscant el substantiu clau: "que no rebi trànsit" → readiness; "totes les claus" →
envFrom; "només aquesta clau" →items. - Practica copiar i enganxar al terminal del navegador: les dreceres són diferents.
- Tingues localitzades les pàgines sense generador imperatiu: PV/PVC, NetworkPolicy, securityContext i sondes.
- Fes sempre la part que saps. La puntuació parcial és real.
- 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:
- Deployment
botiga-webambnginx:1.27-alpine, 4 rèpliques, exposat com a NodePort 30080. - ConfigMap
config-webambentorn=devicache=on, injectat com a variables en aquest Deployment. - Un Job
migracio-bdambbusybox:1.36iecho migrat, 3 completions i 2 en paral·lel. - Un HPA per a
botiga-webentre 2 i 8 rèpliques al 70 % de CPU. - Escriure a
/opt/pods.txtel 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-dbque bloquegi fins que responguipostgres-reserves:5432. - Un contenidor
apiambnginx:1.27-alpine, sondes de readiness i liveness sobre/port 80,requestsde 100m/128Mi ilimitsde 200m/256Mi. - Un sidecar natiu
logs(initContainer ambrestartPolicy: Always) que facitail -Fd'un fitxer compartit. - Un
emptyDircompartit entreapiilogsmuntat a/var/log/nginx. securityContextambrunAsNonRoot,allowPrivilegeEscalation: falseidrop: ["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.yamlspec:
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.txtEl 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
EOFError 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,.vimrcambset paste,kubectl explain --recursiveikubectl 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
- Què és Kubernetes?
- Arquitectura de Kubernetes
- Conceptes i Terminologia Clau
- Configuració d'un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objectes, Manifests YAML i el Model Declaratiu
- El Projecte del Curs: la Plataforma Rutas Norte
Mòdul 2: Components Principals de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualitzacions, Rollbacks i Estratègies de Desplegament
- Serveis
- Namespaces
- Etiquetes, Selectors i Anotacions
Mòdul 3: Gestió de Configuració i Secrets
- ConfigMaps
- Secrets
- Variables d'Entorn
- Quotes i Límits de Recursos
- LimitRanges i Classes de Qualitat de Servei (QoS)
- ServiceAccounts i Accés a l'API des dels Pods
Mòdul 4: Xarxes a Kubernetes
- Xarxes de Clúster
- Tipus de Serveis
- DNS Intern i Descobriment de Serveis
- Controladors d'Ingress
- TLS i Gestió de Certificats amb cert-manager
- Polítiques de Xarxa
Mòdul 5: Emmagatzematge a Kubernetes
- Volums
- Volums Persistents
- Reclamacions de Volums Persistents
- Classes d'Emmagatzematge
- Aprovisionament Dinàmic, Expansió i Snapshots
- Còpies de Seguretat i Restauració de Dades
Mòdul 6: Conceptes Avançats de Kubernetes
- StatefulSets
- DaemonSets
- Treballs i CronJobs
- Init Containers, Sidecars i Patrons Multicontenidor
- Planificació: Afinitat, Taints i Toleracions
- Definicions de Recursos Personalitzats (CRDs)
- Operadors i el Patró Controlador
Mòdul 7: Monitoratge i Registre
- Verificacions de Salut i Sondes
- Servidor de Mètriques i kubectl top
- Monitoratge amb Prometheus
- Visualització i Alertes amb Grafana i Alertmanager
- Registre Centralitzat amb Elasticsearch, Fluentd i Kibana (EFK)
- Depuració d'Aplicacions i Esdeveniments del Clúster
Mòdul 8: Seguretat a Kubernetes
- Control d'Accés Basat en Rols (RBAC)
- Contextos de Seguretat i Enduriment del Contenidor
- Polítiques de Seguretat de Pods i Pod Security Standards
- Seguretat de Xarxa
- Seguretat d'Imatges
- Auditoria, Escaneig i Gestió de Vulnerabilitats
Mòdul 9: Escalat i Rendiment
- Autoescalat Horitzontal de Pods
- Autoescalat Vertical de Pods
- Autoescalat de Clúster
- Escalat per Esdeveniments i Mètriques Personalitzades amb KEDA
- Alta Disponibilitat: PodDisruptionBudgets i Topologia
- Ajust de Rendiment
Mòdul 10: Ecosistema i Eines de Kubernetes
- Minikube i Entorns Locals amb kind
- Kubeadm
- Helm
- Kustomize
- GitOps amb Argo CD i Flux
- Kubernetes Gestionat: EKS, AKS i GKE
Mòdul 11: Estudis de Cas i Aplicacions del Món Real
- Desplegament d'una Aplicació Web
- Execució d'Aplicacions amb Estat
- CI/CD amb Kubernetes
- Estratègies de Desplegament: Blue-Green i Canary
- Gestió Multi-Clúster
- Operació en Producció: Incidències, Runbooks i Costos
