A la lliçó anterior vam fer servir kubectl apply com si fos una ordre òbvia i vam veure manifests YAML de passada, sense desmenuçar-los. Toca saldar aquest deute, perquè el manifest és la unitat de treball real de Kubernetes: tot el que desplegis durant els onze mòduls que queden serà un fitxer YAML que descriu un objecte. Aquesta lliçó t'ensenya l'anatomia exacta de qualsevol objecte de l'API —els quatre camps que hi són sempre, facis el que facis—, com descobrir quina apiVersion correspon a cada tipus, el subconjunt de YAML que has de dominar, la diferència real entre apply, create i replace i per què importa, com validar un manifest abans d'aplicar-lo, i com organitzar els fitxers del projecte Rutas Norte al directori k8s/ perquè el repositori sigui la font de veritat.
Contingut
- Anatomia d'un objecte de Kubernetes
- Grups, versions i nivells d'estabilitat de l'API
- El YAML que cal saber
- Estat desitjat i estat observat
applyenfront decreateireplace- Server-side apply i la propietat de camps
- Validar abans d'aplicar
- Organització dels manifests del projecte
- Anatomia d'un objecte de Kubernetes
Absolutament tots els objectes de Kubernetes —un pod, un secret, un permís RBAC, un CRD d'un operador— comparteixen la mateixa estructura de quatre camps de primer nivell. Aprendre aquesta estructura una vegada serveix per sempre.
# k8s/base/api-reserves-pod.yaml
apiVersion: v1 # 1. QUINA versió de l'API interpreta això
kind: Pod # 2. QUIN tipus d'objecte és
metadata: # 3. QUI és: identitat i metadades
name: api-reserves
namespace: rutas-norte-dev
labels:
app: api-reserves
app.kubernetes.io/part-of: rutas-norte
entorn: dev
annotations:
rutasnorte.example/responsable: [email protected]
spec: # 4. COM ha de ser: l'estat desitjat
containers:
- name: api
image: registry.rutasnorte.example/api-reserves:2.4.0
ports:
- containerPort: 3000
env:
- name: DB_HOST
value: postgres-reserves1.1. apiVersion
Indica el grup i la versió de l'API que ha d'interpretar l'objecte. Determina quins camps són vàlids i com es comporten. Exemples: v1 (grup core), apps/v1, networking.k8s.io/v1, batch/v1. És el camp que més errors provoca quan es copien manifests antics d'internet.
1.2. kind
El tipus concret: Pod, Deployment, Service, Ingress, ConfigMap. S'escriu sempre en CamelCase, mentre que el recurs a la URL de l'API va en minúscules i plural (pods, deployments). La parella apiVersion + kind identifica unívocament l'esquema de l'objecte.
1.3. metadata
La identitat de l'objecte. Els seus camps més rellevants:
| Camp | Qui l'escriu | Per a què serveix |
|---|---|---|
name |
Tu | Nom únic dins del namespace i del tipus. Ha de ser un nom DNS vàlid: minúscules, números i guions |
namespace |
Tu (o el context) | Namespace on viu. Si s'omet, es fa servir el del context de kubectl |
labels |
Tu | Metadades consultables: són la base de selectors, Services i controladors |
annotations |
Tu i les eines | Metadades no consultables: configuració de tercers, traçabilitat |
uid |
El sistema | Identificador únic i irrepetible a tot el clúster |
resourceVersion |
El sistema | Versió interna de l'objecte; serveix per detectar escriptures concurrents |
creationTimestamp |
El sistema | Data de creació |
ownerReferences |
El sistema | Qui "posseeix" l'objecte. Un pod creat per un ReplicaSet l'apunta aquí, i per això en esborrar el ReplicaSet s'esborren els seus pods (recol·lecció en cascada) |
finalizers |
El sistema o les eines | Tasques que s'han de completar abans que l'objecte es pugui esborrar |
Un detall que explica un comportament desconcertant: si un namespace o un PVC es queda "penjat" en estat Terminating, gairebé sempre és per un finalizer el responsable del qual no ha acabat la seva feina.
1.4. spec
L'estat desitjat. És el que tu descrius i el seu contingut depèn completament del kind: l'spec d'un Pod té containers, el d'un Service té selector i ports, el d'un PVC té resources.requests.storage. Tot el curs consisteix, en el fons, a aprendre a escriure spec de tipus diferents.
1.5. status
No apareix al manifest de dalt perquè no l'escrius tu: l'escriu el clúster. És l'estat observat. Si consultes l'objecte ja creat el veuràs:
status:
phase: Running
podIP: 10.244.0.17
hostIP: 192.168.49.2
startTime: "2026-08-05T09:14:22Z"
conditions:
- type: Initialized
status: "True"
- type: Ready
status: "True"
- type: ContainersReady
status: "True"
containerStatuses:
- name: api
ready: true
restartCount: 0
image: registry.rutasnorte.example/api-reserves:2.4.0
imageID: registry.rutasnorte.example/api-reserves@sha256:9f3a1c2...Fixa't en dues coses. Primera: conditions és el format estàndard amb què Kubernetes expressa "com va" un objecte, i és el que consulten kubectl wait i les eines de desplegament. Segona: imageID conté el digest real de la imatge que s'està executant, mentre que spec.image conté l'etiqueta que tu vas demanar. Quan algú reutilitza una etiqueta, aquests dos valors deixen de correspondre's; hi tornarem en parlar d'etiquetes immutables a la lliçó següent.
- Grups, versions i nivells d'estabilitat de l'API
L'API de Kubernetes està dividida en grups per poder evolucionar per parts i permetre extensions.
| Grup | apiVersion |
Tipus que conté |
|---|---|---|
| core (o legacy) | v1 |
Pod, Service, ConfigMap, Secret, Namespace, PersistentVolume, PersistentVolumeClaim, ServiceAccount, Node |
| apps | apps/v1 |
Deployment, ReplicaSet, StatefulSet, DaemonSet |
| batch | batch/v1 |
Job, CronJob |
| networking.k8s.io | networking.k8s.io/v1 |
Ingress, NetworkPolicy, IngressClass |
| rbac.authorization.k8s.io | rbac.authorization.k8s.io/v1 |
Role, RoleBinding, ClusterRole, ClusterRoleBinding |
| autoscaling | autoscaling/v2 |
HorizontalPodAutoscaler |
| storage.k8s.io | storage.k8s.io/v1 |
StorageClass, VolumeAttachment |
| policy | policy/v1 |
PodDisruptionBudget |
El grup core és l'històric i per això la seva apiVersion no porta prefix: és v1, no core/v1. Tots els altres segueixen el format grup/versió.
Com descobrir-ho al teu propi clúster
No cal memoritzar aquesta taula: es consulta.
# Tots els grups i versions que admet aquest clúster
kubectl api-versions
# Tots els recursos, amb la seva apiVersion, abreviatura i àmbit
kubectl api-resources
# Filtrar per grup
kubectl api-resources --api-group=networking.k8s.ioNAME SHORTNAMES APIVERSION NAMESPACED KIND
ingressclasses networking.k8s.io/v1 false IngressClass
ingresses ing networking.k8s.io/v1 true Ingress
networkpolicies netpol networking.k8s.io/v1 true NetworkPolicyAquest és el reflex que cal adquirir: abans de copiar una apiVersion d'un blog, comprova-la amb kubectl api-resources. La versió correcta és sempre la que diu el teu clúster.
Alpha, beta i estable
| Nivell | Exemple | Què significa | Recomanació |
|---|---|---|---|
| Alpha | v1alpha1 |
Pot desaparèixer o canviar sense avís. Desactivada per defecte. Hi pot haver pèrdua de dades | Mai en producció |
| Beta | v2beta2 |
Ben provada, però els camps encara poden canviar. Des de la 1.24 les APIs beta noves vénen desactivades per defecte | Només amb pla de migració |
| Estable | v1, v2 |
Compatibilitat garantida durant moltes versions | La que has de fer servir |
Kubernetes retira versions antigues amb cada actualització, i aquest és el motiu que manifests copiats de tutorials vells fallin:
error: unable to recognize "deploy.yaml": no matches for kind "Deployment"
in version "extensions/v1beta1"El grup extensions/v1beta1 va desaparèixer fa moltes versions. Avui un Deployment és apps/v1 i un Ingress és networking.k8s.io/v1, sense excepcions. Abans d'actualitzar un clúster, eines com kubent (kube-no-trouble) revisen els teus manifests a la recerca d'APIs a punt de retirar-se.
- El YAML que cal saber
YAML és simple però té paranys. Aquest és el subconjunt que necessites.
3.1. Mapes, llistes i indentació
# Un mapa (clau: valor)
metadata:
name: api-reserves # indentació amb ESPAIS, mai tabuladors
namespace: rutas-norte-dev
# Una llista de cadenes
args:
- "--port=3000"
- "--mode=produccio"
# Una llista de mapes: el guionet marca l'inici de cada element
containers:
- name: api # aquest 'name' pertany al primer element
image: nginx:1.27
ports:
- containerPort: 3000
- name: sidecar-metriques # segon element
image: prom/statsd-exporter:v0.26.0Regles innegociables:
- Espais, mai tabuladors. Un tabulador és un error de sintaxi en YAML. Configura el teu editor per expandir tabuladors a 2 espais en fitxers
.yaml. - La indentació defineix la jerarquia. Dos espais per nivell és la convenció a Kubernetes.
- El guionet
-indica element de llista i el seu contingut s'indenta al mateix nivell que el guionet o més.
3.2. Tipus i cometes: el parany clàssic
data:
repliques_text: "3" # cadena
repliques_numero: 3 # enter
actiu: true # booleà
actiu_text: "true" # cadena
versio: "1.30" # cadena; sense cometes seria el número 1.30
port_noruega: "NO" # IMPRESCINDIBLE! sense cometes, YAML 1.1 ho llegeix com a falseEl cas NO és cèlebre (es coneix com the Norway problem): YAML 1.1 interpreta yes, no, on, off, y, n com a booleans. Regla pràctica: en un ConfigMap o Secret, posa sempre els valors entre cometes, perquè aquests camps exigeixen cadenes i un valor mal tipat produeix un error tan clar com aquest:
error: error validating data: ValidationError(ConfigMap.data.port_noruega):
invalid type for io.k8s.api.core.v1.ConfigMap.data: got "boolean", expected "string"3.3. Cadenes multilínia
Fonamentals per incrustar fitxers de configuració en un ConfigMap:
data:
# | conserva els salts de línia (literal). És el que voldràs gairebé sempre
nginx.conf: |
server {
listen 80;
root /usr/share/nginx/html;
location /api/ {
proxy_pass http://api-reserves:80/;
}
}
# > plega els salts en espais (folded): útil per a textos llargs
descripcio: >
Configuracio de la botiga web de Rutas Norte
per a l'entorn de desenvolupament.| Indicador | Efecte | Ús típic |
|---|---|---|
| |
Conserva salts de línia; elimina l'últim | Fitxers de configuració, scripts |
|- |
Conserva salts; elimina el salt final | Quan la línia final sobra |
> |
Converteix salts en espais | Textos descriptius llargs |
3.4. Diversos documents en un fitxer
El separador --- permet posar diversos objectes en un mateix fitxer, i kubectl apply els processa en ordre:
# k8s/base/redis-cache.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cache-config
namespace: rutas-norte-dev
data:
maxmemory: "256mb"
---
apiVersion: v1
kind: Pod
metadata:
name: redis-cache
namespace: rutas-norte-dev
labels:
app: redis-cache
spec:
containers:
- name: redis
image: redis:7.2-alpineCriteri recomanat: agrupa en un fitxer els objectes que formen una unitat desplegable (un component amb el seu ConfigMap i el seu Service) i separa en fitxers diferents els components diferents. Un únic fitxer gegant amb tota la plataforma és difícil de revisar i d'aplicar parcialment.
- Estat desitjat i estat observat
Aquí és on el model declaratiu es torna concret. Ja coneixes el bucle de reconciliació de la lliçó Arquitectura de Kubernetes; vegem-lo des del punt de vista del fitxer.
flowchart LR
Y["Manifest YAML<br/>a k8s/"] -->|kubectl apply| API["kube-apiserver"]
API -->|persisteix spec| E[("etcd")]
C["Controlador"] -->|llegeix spec| API
C -->|observa| R["Món real<br/>pods, nodes"]
C -->|escriu status| API
R -->|diferència| C
Quatre conseqüències pràctiques que convé tenir molt clares:
- Tu escrius
spec; el sistema escriustatus. Editar unstatusa mà no serveix de res: el controlador el sobreescriurà en el seu cicle següent. - Aplicar no és executar.
kubectl applyacaba quan l'objecte està desat, no quan l'aplicació està llesta. Per esperar de debò:
kubectl apply -f k8s/base/api-reserves.yaml
kubectl wait --for=condition=Ready pod/api-reserves -n rutas-norte-dev --timeout=90s- És idempotent. Aplicar deu vegades el mateix fitxer dona el mateix resultat que aplicar-lo una. Això és el que fa possible que una canalització de CI/CD executi
kubectl applya cada desplegament sense comprovar abans què existeix.
- El fitxer és la font de veritat, no el clúster. Si algú canvia alguna cosa amb
kubectl edit, el següentapplydes del repositori ho reverteix. Aquesta és exactament la propietat sobre la qual es construeix GitOps (mòdul 10).
apply enfront de create i replace
apply enfront de create i replaceTres ordres que semblen intercanviables i no ho són gens.
| Ordre | Si l'objecte NO existeix | Si l'objecte JA existeix | Conserva canvis d'altres |
|---|---|---|---|
kubectl create -f |
El crea | Error: AlreadyExists |
— |
kubectl replace -f |
Error: NotFound |
El substitueix sencer | No: esborra el que no sigui al fitxer |
kubectl apply -f |
El crea | El fusiona amb l'existent | Sí, si són d'un altre gestor de camps |
La conclusió operativa és simple: fes servir apply sempre. create serveix per a ordres imperatives ràpides (kubectl create namespace) i replace per a casos molt concrets de substitució total.
Per què apply sap què esborrar: l'anotació last-applied
Imagina que apliques un pod amb dues etiquetes, després edites el fitxer i en deixes només una, i tornes a aplicar. Com sap kubectl que ha d'eliminar la segona etiqueta, en lloc de deixar-la perquè "no és al fitxer però tampoc he demanat esborrar-la"?
La resposta clàssica és que apply desa una còpia de l'últim que vas aplicar en una anotació:
kubectl get pod api-reserves -n rutas-norte-dev \
-o jsonpath='{.metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration}'Amb aquesta informació, kubectl fa una fusió de tres vies: compara (a) el que vas aplicar l'última vegada, (b) el que apliques ara i (c) el que hi ha al clúster. El que era a (a) i desapareix a (b) s'esborra; el que només és a (c) —perquè ho va posar un controlador o un webhook— es respecta.
Això explica un error freqüent: si vas crear un objecte amb kubectl create i després el modifiques amb apply, no existeix l'anotació de referència i kubectl avisa que la fusió pot ser incorrecta. Per coherència, fes servir apply des del principi de la vida de cada objecte.
- Server-side apply i la propietat de camps
Des de Kubernetes 1.22, la fusió la pot fer el servidor en lloc del client, i és el mecanisme cap al qual està migrant tot l'ecosistema.
La diferència és conceptual: en lloc d'una anotació amb l'últim estat, el servidor registra quin gestor és propietari de cada camp a metadata.managedFields.
metadata:
managedFields:
- manager: kubectl
operation: Apply
apiVersion: v1
fieldsV1:
f:spec:
f:containers:
k:{"name":"api"}:
f:image: {}
- manager: kubelet
operation: Update
subresource: statusAvantatges respecte a la fusió en client:
- Detecta conflictes. Si intentes modificar un camp que pertany a un altre gestor (un HPA que controla
replicas, per exemple), la petició falla amb un avís clar en lloc de provocar una baralla silenciosa entre eines:
Si de debò vols apropiar-te del camp, es força explícitament:
- No depèn d'una anotació que en objectes grans pot ser enorme.
- Diversos gestors poden coexistir sobre el mateix objecte de manera ordenada: la teva canalització, un operador i un autoescalador, cadascun amo dels seus camps.
Per al curs continuarem fent servir kubectl apply a seques, que és l'habitual, però convé que reconeguis managedFields quan aparegui en un -o yaml i que sàpigues què significa un error de conflicte.
- Validar abans d'aplicar
Aplicar sense comprovar és la recepta de l'incident. Kubernetes ofereix quatre nivells de verificació, del més barat al més complet.
7.1. --dry-run=client: validació local
kubectl construeix l'objecte i valida l'esquema sense enviar res. Detecta errors de sintaxi YAML i camps inexistents. No detecta problemes de permisos, quotes ni webhooks. És també la bandera que vam fer servir a la lliçó anterior per generar manifests:
kubectl create configmap botiga-web-config \
--from-literal=api_url=http://api-reserves \
-n rutas-norte-dev --dry-run=client -o yaml > k8s/base/botiga-web-configmap.yaml7.2. --dry-run=server: validació real sense persistir
La petició sí que arriba a l'apiserver i recorre tot el camí de la lliçó 01-02 —autenticació, autorització RBAC, admissió, validació— però no s'escriu a etcd. Detecta el que el client no pot veure: manca de permisos, quotes superades, polítiques de seguretat de pods, webhooks d'admissió propis. És la validació que ha d'executar la teva canalització de CI abans de desplegar.
7.3. kubectl diff: veure el canvi abans de fer-lo
diff -u -N /tmp/LIVE-1234/v1.Pod.rutas-norte-dev.api-reserves /tmp/MERGED-5678/...
--- LIVE
+++ MERGED
@@ -18,7 +18,7 @@
containers:
- name: api
- image: registry.rutasnorte.example/api-reserves:2.3.1
+ image: registry.rutasnorte.example/api-reserves:2.4.0Aquesta és l'ordre que converteix un desplegament en una cosa revisable: et diu exactament què canviarà al clúster abans de canviar-ho. Hauria de ser un pas obligatori en qualsevol desplegament manual a producció. El seu codi de sortida és diferent de zero quan hi ha diferències, la qual cosa permet fer-lo servir en scripts.
7.4. kubectl explain: consultar l'esquema
kubectl explain pod.spec.containers.livenessProbe
kubectl explain deployment.spec.strategy.rollingUpdate
kubectl explain ingress.spec.rules --recursiveJa el vam veure a la lliçó anterior; aquí convé subratllar que és la resposta correcta a "existeix aquest camp i quin tipus té?" quan estàs escrivint un manifest.
Flux de treball recomanat
# 1. El YAML és vàlid i els camps existeixen?
kubectl apply -f k8s/base/ --dry-run=client
# 2. L'acceptaria el clúster amb els meus permisos i les seves polítiques?
kubectl apply -f k8s/base/ --dry-run=server
# 3. Què canviarà exactament?
kubectl diff -f k8s/base/
# 4. Aplicar
kubectl apply -f k8s/base/
# 5. Esperar que l'estat real assoleixi el desitjat
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/part-of=rutas-norte \
-n rutas-norte-dev --timeout=120s
- Organització dels manifests del projecte
Els manifests de Rutas Norte viuen al directori k8s/ del repositori del projecte, versionats a Git. Aquesta és l'estructura que farem servir durant tot el curs:
rutas-norte/
├── src/
├── Dockerfile
└── k8s/
├── base/ # definició comuna a tots els entorns
│ ├── namespace.yaml
│ ├── botiga-web.yaml
│ ├── api-reserves.yaml
│ ├── postgres-reserves.yaml
│ ├── redis-cache.yaml
│ ├── worker-notificacions.yaml
│ └── informes-ocupacio.yaml
├── entorns/
│ ├── dev/ # allò específic de rutas-norte-dev
│ ├── pre/
│ └── pro/
├── entorn-local/
│ └── kind-rutas-norte.yaml # de la lliçó 01-04
└── README.mdConvencions que seguirem:
| Convenció | Regla |
|---|---|
| Un fitxer per component | api-reserves.yaml conté el Deployment, el seu Service i el seu ConfigMap, separats per --- |
| Nom del fitxer = nom del component | Trobar el manifest d'alguna cosa és immediat |
| Namespace explícit | Cada objecte declara el seu metadata.namespace, per no dependre del context actiu |
| Etiquetes comunes a tots els objectes | app, app.kubernetes.io/part-of: rutas-norte, entorn |
| Mai secrets reals a Git | Els Secrets van amb valors d'exemple o xifrats (mòduls 3 i 8) |
| Ordre d'aplicació | Namespaces i ConfigMaps abans que les càrregues que els consumeixen |
Sobre l'ordre: aplicar un directori complet funciona perquè el model és reconciliador —un pod que no troba el seu ConfigMap es queda esperant i arrenca quan apareix—, però per evitar errors transitoris convé aplicar primer el namespace:
Un avís important per no envair mòduls posteriors: aquesta estructura de base/ i entorns/ és deliberadament YAML pla, sense plantilles. Quan la duplicació entre entorns es torni molesta, existeixen dues solucions estàndard —Helm (plantilles i empaquetat) i Kustomize (superposicions sense plantilles)— que s'estudien a les lliçons Helm i Kustomize. Aprendre primer el YAML pla no és temps perdut: és el que et permetrà entendre què generen aquestes eines.
Errors Comuns i Consells
- Fer servir tabuladors en YAML. Error de sintaxi pur. Configura l'editor:
expandtab, 2 espais, i una extensió de YAML que validi en desar. - Copiar
apiVersionde tutorials antics.extensions/v1beta1iapps/v1beta2no existeixen. Verifica-ho sempre ambkubectl api-resources. - Confondre el nom del
kindamb el del recurs.kind: Deployment(CamelCase, singular) al YAML;kubectl get deployments(minúscules, plural) a la CLI. - Valors sense cometes en ConfigMaps i Secrets.
"3","true","NO". Aquests camps exigeixen cadenes i YAML converteix tipus alegrement. - Editar el
statusa mà. No té cap efecte: l'escriu el controlador. - Barrejar
createiapplysobre el mateix objecte. Sense l'anotaciólast-applied, la fusió es pot comportar de manera inesperada. Comença sempre ambapply. - Aplicar sense
diffen producció.kubectl diffcosta dos segons i evita desplegaments sorpresa. - Desar un
-o yamlcom a manifest. La sortida del clúster incloustatus,uid,resourceVersion,managedFieldsicreationTimestamp, que no s'han de versionar. Neteja-la (a mà o amb el pluginkubectl neat) abans de desar-la ak8s/. - Consell: tracta el directori
k8s/com a codi. Revisió per petició de canvi, validació a CI amb--dry-run=server, i la regla d'or: si un canvi no és a Git, no existeix.
Exercicis
Exercici 1: Anatomia i descobriment de l'API
- Escriu el manifest d'un ConfigMap anomenat
botiga-web-configal namespacerutas-norte-dev, amb les etiquetes del projecte i dues claus:api_urlamb valorhttp://api-reservesimode_mantenimentamb valor"NO". Explica per què el segon valor necessita cometes. - Esbrina amb kubectl, sense buscar a internet, a quina
apiVersionpertanyenCronJob,IngressiHorizontalPodAutoscaler, i quins d'ells són d'àmbit de namespace. - Comprova amb
kubectl explainsi el campspec.containers.imagePullPolicyexisteix en un Pod i quins valors admet.
Exercici 2: Validació i idempotència
Partint del manifest del pod api-reserves de la secció 1:
- Valida'l en local sense tocar el clúster.
- Aplica'l, i aplica'l una altra vegada sense canviar res. Què diu kubectl la segona vegada i per què?
- Canvia la imatge a la versió
2.5.0al fitxer i, abans d'aplicar, mostra exactament què canviaria al clúster. - Mostra el
status.phasei la IP del pod fent servirjsonpath. - Explica quina diferència hi hauria si al pas 2 haguessis fet servir
kubectl createen lloc d'apply.
Exercici 3: Fitxer multidocument i organització
Crea el fitxer k8s/base/redis-cache.yaml que contingui, en un sol fitxer i en l'ordre correcte, tres objectes:
- Un ConfigMap
redis-cache-configamb una clauredis.confque contingui diverses línies de configuració (fes servir l'indicador literal). - Un Pod
redis-cacheamb la imatgeredis:7.2-alpine. - Tot això a
rutas-norte-dev, amb les etiquetes del projecte.
Després, indica quina ordre aplicaria el directori complet i per què convé aplicar abans namespace.yaml.
Solucions
Solució 1
# k8s/base/botiga-web-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: botiga-web-config
namespace: rutas-norte-dev
labels:
app: botiga-web
app.kubernetes.io/part-of: rutas-norte
entorn: dev
data:
api_url: "http://api-reserves"
mode_manteniment: "NO""NO" necessita cometes perquè YAML 1.1 interpreta NO (igual que no, yes, on, off) com el booleà false. El camp data d'un ConfigMap només admet cadenes, així que sense cometes l'apiserver rebutjaria l'objecte amb un error de tipus.
cronjobs cj batch/v1 true CronJob
horizontalpodautoscalers hpa autoscaling/v2 true HorizontalPodAutoscaler
ingresses ing networking.k8s.io/v1 true IngressTots tres són d'àmbit de namespace (NAMESPACED = true).
FIELD: imagePullPolicy <string>
DESCRIPTION:
Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always
if :latest tag is specified, or IfNotPresent otherwise.Solució 2
# 1
kubectl apply -f k8s/base/api-reserves-pod.yaml --dry-run=client
# 2
kubectl apply -f k8s/base/api-reserves-pod.yaml # pod/api-reserves created
kubectl apply -f k8s/base/api-reserves-pod.yaml # pod/api-reserves unchanged
# 3
kubectl diff -f k8s/base/api-reserves-pod.yaml
# 4
kubectl get pod api-reserves -n rutas-norte-dev \
-o jsonpath='{.status.phase}{"\t"}{.status.podIP}{"\n"}'Al pas 2, la segona execució diu unchanged perquè apply és idempotent: kubectl compara l'anotació last-applied-configuration, el fitxer actual i l'objecte viu, no hi troba diferències i no envia cap modificació. Aquesta propietat és la que permet executar apply a cada desplegament d'una canalització sense efectes secundaris.
Amb kubectl create al pas 2, la primera vegada hauria funcionat i la segona hauria fallat amb Error from server (AlreadyExists), perquè create no fusiona: només sap crear objectes nous.
Solució 3
# k8s/base/redis-cache.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cache-config
namespace: rutas-norte-dev
labels:
app: redis-cache
app.kubernetes.io/part-of: rutas-norte
entorn: dev
data:
redis.conf: |
maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly no
save ""
---
apiVersion: v1
kind: Pod
metadata:
name: redis-cache
namespace: rutas-norte-dev
labels:
app: redis-cache
app.kubernetes.io/part-of: rutas-norte
entorn: dev
spec:
containers:
- name: redis
image: redis:7.2-alpine
ports:
- containerPort: 6379El ConfigMap va primer perquè el pod el consumirà i així s'evita un error transitori. Convé aplicar namespace.yaml per separat i abans perquè tots els altres objectes declaren namespace: rutas-norte-dev i, si aquest namespace encara no existeix, les seves creacions fallen amb namespaces "rutas-norte-dev" not found. El namespace és l'única dependència dura del conjunt.
Conclusió
Ja domines la unitat de treball de Kubernetes. Tot objecte té la mateixa anatomia —apiVersion, kind, metadata, spec que escrius tu i status que escriu el clúster—, pertany a un grup d'API la versió del qual has de verificar al teu propi clúster amb kubectl api-resources, i s'expressa en un YAML amb regles estrictes sobre indentació, tipus i cadenes multilínia. Has vist per què apply és superior a create i replace, com funciona la fusió de tres vies i la seva anotació last-applied, cap a on evoluciona el model amb server-side apply i la propietat de camps, i quina seqüència de validació —--dry-run=client, --dry-run=server, kubectl diff, kubectl explain— converteix un desplegament en una cosa predictible. I tens l'estructura del directori k8s/ que sostindrà el projecte durant la resta del curs.
Tens clúster, tens CLI i tens el llenguatge dels manifests. Només falta el pacient. A la lliçó següent, El Projecte del Curs: la Plataforma Rutas Norte, coneixeràs en detall l'empresa, cadascun dels sis components de la plataforma, l'arquitectura objectiu i les convencions del projecte; i acabaràs desplegant de debò el teu primer tros de Rutas Norte al clúster, veient-lo funcionar al teu navegador.
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
