Práctica · Patches y generadores
Practice · Patches and generators
Pratica · Patch e generatori
Un overlay completo, de punta a punta. Base: un Deployment api (nginx, con una anotación example.com/owner) y un Service api. Todo lo que sigue va en overlays/prod/.
A complete overlay, end to end. Base: a Deployment api (nginx, with an example.com/owner annotation) and a Service api. Everything below lives in overlays/prod/.
Un overlay completo, da capo a fondo. Base: un Deployment api (nginx, con un'annotazione example.com/owner) e un Service api. Tutto quello che segue sta in overlays/prod/.
overlays/prod/
kustomization.yaml
patch-api.yaml # patch estratégico# strategic patch# patch strategica
app.env # fuente del configMapGenerator# source for configMapGenerator# sorgente del configMapGenerator
default.conf # archivo que va como volumen# file mounted as a volume# file montato come volume
1 · El kustomization.yaml del overlay
1 · The overlay's kustomization.yaml
1 · Il kustomization.yaml dell'overlay
resources:
- ../../base
namePrefix: prod-
configMapGenerator:
- name: api-env
envs: # archivo KEY=VALUE → una clave por línea# KEY=VALUE file → one key per line# file KEY=VALUE → una chiave per riga
- app.env
- name: api-files
files: # el archivo entero → clave = nombre del archivo# the whole file → key = file name# il file intero → chiave = nome del file
- default.conf
secretGenerator:
- name: api-secret
literals: # KEY=VALUE en línea, sin comillas# inline KEY=VALUE, no quotes# KEY=VALUE in linea, senza virgolette
- DB_PASS=s3cr3t
patches:
- path: patch-api.yaml # estratégico: en archivo aparte# strategic: separate file# strategica: file a parte
- target: # JSON 6902: a QUÉ recurso# JSON 6902: WHICH resource# JSON 6902: a QUALE risorsa
kind: Service
name: api # nombre de la BASE, sin prefijo# BASE name, no prefix# nome della BASE, senza prefisso
patch: |-
- op: add
path: /spec/type
value: NodePort
- op: add
path: /spec/ports/0/nodePort # 0 = primer elemento de la lista# 0 = first element of the list# 0 = primo elemento della lista
value: 30180
- target:
kind: Deployment
name: api
patch: |-
- op: replace
path: /spec/replicas
value: 3
- op: remove # "/" dentro de la clave se escribe ~1# a "/" inside the key is written ~1# "/" dentro la chiave si scrive ~1
path: /metadata/annotations/example.com~1owner
2 · El patch estratégico: conectar ConfigMaps y Secret al pod
2 · The strategic patch: wiring ConfigMaps and the Secret into the pod
2 · La patch strategica: collegare ConfigMap e Secret al pod
# patch-api.yaml — YAML de Kubernetes normal, pero solo lo que cambia# patch-api.yaml — ordinary Kubernetes YAML, only what changes# patch-api.yaml — YAML Kubernetes normale, solo ciò che cambia
apiVersion: apps/v1
kind: Deployment # ┐ identifican QUÉ recurso# ┐ identify WHICH resource# ┐ identificano QUALE risorsa
metadata: # │
name: api # ┘ nombre de la base# ┘ base name# ┘ nome della base
spec:
template:
spec:
containers:
- name: api # identifica QUÉ contenedor de la lista# identifies WHICH container in the list# identifica QUALE container della lista
envFrom: # a) TODAS las claves del ConfigMap como env# a) ALL ConfigMap keys as env vars# a) TUTTE le chiavi del ConfigMap come env
- configMapRef:
name: api-env # nombre del GENERADOR, sin hash# GENERATOR name, no hash# nome del GENERATORE, senza hash
env: # b) UNA clave del Secret, con el nombre que elijas# b) ONE Secret key, under the name you choose# b) UNA chiave del Secret, col nome che scegli
- name: DB_PASS
valueFrom:
secretKeyRef:
name: api-secret
key: DB_PASS
volumeMounts: # c) un ConfigMap como ARCHIVOS# c) a ConfigMap as FILES# c) un ConfigMap come FILE
- name: conf
mountPath: /etc/nginx/conf.d
volumes:
- name: conf
configMap:
name: api-files
3 · Verlo antes de aplicarlo
3 · See it before applying it
3 · Vederlo prima di applicarlo
k kustomize overlays/prod | grep -E "kind:|name: prod-|nodePort|replicas"
# kind: ConfigMap name: prod-api-env-89m87m749m ← hash del contenido# kind: ConfigMap name: prod-api-env-89m87m749m ← content hash# kind: ConfigMap name: prod-api-env-89m87m749m ← hash del contenuto
# kind: Secret name: prod-api-secret-7kttbck6c5 ← data en base64, automático# kind: Secret name: prod-api-secret-7kttbck6c5 ← data in base64, automatically# kind: Secret name: prod-api-secret-7kttbck6c5 ← data in base64, automatico
# en el Deployment: configMapRef → prod-api-env-89m87m749m (reescrito solo)# in the Deployment: configMapRef → prod-api-env-89m87m749m (rewritten for you)# nel Deployment: configMapRef → prod-api-env-89m87m749m (riscritto da solo)
k apply -k overlays/prod --dry-run=server # valida contra el apiserver# validates against the apiserver# valida contro l'apiserver
k apply -k overlays/prod
★
Regla práctica: patch estratégico por defecto (es YAML que ya sabés escribir y k explain te ayuda). JSON 6902 solo si la consigna lo pide, para borrar un campo o para tocar un elemento de lista por posición. Y antes de aplicar, siempre k kustomize <dir>: es tu dry-run.
Rule of thumb: strategic patch by default (it is YAML you already know, and k explain helps). JSON 6902 only when the task asks for it, to delete a field, or to touch a list element by position. And before applying, always k kustomize <dir>: that is your dry run.
Regola pratica: patch strategica di default (è YAML che sai già scrivere e k explain aiuta). JSON 6902 solo se la consegna lo chiede, per cancellare un campo o toccare un elemento di lista per posizione. E prima di applicare, sempre k kustomize <dir>: è il tuo dry run.
⚠
Dos tropiezos clásicos: en los literals, DB_PASS="s3cr3t" puede terminar con las comillas como parte del valor, así que escribilo sin comillas. Y el name de target y del patch estratégico es el de la base (api), nunca el prefijado (prod-api).
Two classic slips: in literals, DB_PASS="s3cr3t" can end up with the quotes as part of the value, so write it without quotes. And the name in target and in the strategic patch is the base one (api), never the prefixed one (prod-api).
Due inciampi classici: nei literals, DB_PASS="s3cr3t" può finire con le virgolette dentro il valore, quindi scrivilo senza virgolette. E il name di target e della patch strategica è quello della base (api), mai quello col prefisso (prod-api).
Teoría · Tres sintaxis en un archivo
Theory · Three syntaxes in one file
Teoria · Tre sintassi in un file
Por qué Kustomize confunde
Why Kustomize is confusing
Perché Kustomize confonde
En un mismo overlay conviven tres lenguajes distintos. Separarlos es la mitad del trabajo:
Three different languages live side by side in one overlay. Telling them apart is half the job:
In uno stesso overlay convivono tre linguaggi diversi. Separarli è metà del lavoro:
| Lo que escribís | Sintaxis | ¿k explain? |
| What you write | Syntax | k explain? |
| Cosa scrivi | Sintassi | k explain? |
Campos del kustomization.yaml | La de Kustomize | No → kubernetes.io |
kustomization.yaml fields | Kustomize's own | No → kubernetes.io |
Campi del kustomization.yaml | Quella di Kustomize | No → kubernetes.io |
| Patch estratégico | YAML de Kubernetes, parcial | Sí |
| Strategic patch | Kubernetes YAML, partial | Yes |
| Patch strategica | YAML Kubernetes, parziale | Sì |
| Patch JSON 6902 | Estándar RFC 6902: lista de operaciones | No |
| JSON 6902 patch | RFC 6902 standard: a list of operations | No |
| Patch JSON 6902 | Standard RFC 6902: lista di operazioni | No |
Patch estratégico: una fusión, no un reemplazo
Strategic patch: a merge, not a replacement
Patch strategica: una fusione, non una sostituzione
Kustomize fusiona tu fragmento con el recurso: lo que no escribís queda como estaba. Para saber dónde fusionar necesita encontrar el recurso (kind + metadata.name) y, en las listas, el elemento: los contenedores se fusionan por su name. Por eso - name: api es obligatorio aunque no lo cambies: sin él, Kustomize no sabe a cuál contenedor sumarle los campos.
Kustomize merges your fragment into the resource: whatever you do not write stays as it was. To know where to merge, it must find the resource (kind + metadata.name) and, in lists, the element: containers are merged by their name. That is why - name: api is mandatory even if you do not change it: without it, Kustomize cannot tell which container gets the fields.
Kustomize fonde il tuo frammento con la risorsa: ciò che non scrivi resta com'era. Per sapere dove fondere deve trovare la risorsa (kind + metadata.name) e, nelle liste, l'elemento: i container si fondono per il loro name. Per questo - name: api è obbligatorio anche se non lo cambi: senza, Kustomize non sa a quale container aggiungere i campi.
JSON 6902: operaciones sobre un camino
JSON 6902: operations on a path
JSON 6902: operazioni su un percorso
No es de Kustomize: es un estándar genérico de JSON, el mismo de kubectl patch --type=json. Cada operación tiene tres partes: op (qué), path (dónde) y value (con qué).
It is not Kustomize's: it is a generic JSON standard, the same one behind kubectl patch --type=json. Every operation has three parts: op (what), path (where) and value (with what).
Non è di Kustomize: è uno standard JSON generico, lo stesso di kubectl patch --type=json. Ogni operazione ha tre parti: op (cosa), path (dove) e value (con cosa).
op | Qué hace |
op | What it does |
op | Cosa fa |
add | Crea el campo; si ya existe, lo pisa. En una lista, inserta en esa posición (/- = al final) |
add | Creates the field; if it exists, overwrites it. In a list, inserts at that position (/- = at the end) |
add | Crea il campo; se esiste, lo sovrascrive. In una lista, inserisce in quella posizione (/- = in fondo) |
replace | Cambia un campo que tiene que existir (si no, error) |
replace | Changes a field that must exist (error otherwise) |
replace | Cambia un campo che deve esistere (altrimenti errore) |
remove | Borra el campo. Es lo que el patch estratégico no hace fácil |
remove | Deletes the field. The thing a strategic patch does not do easily |
remove | Cancella il campo. È ciò che la patch strategica non fa facilmente |
El path recorre el YAML con barras; las listas van por índice desde 0 (/spec/template/spec/containers/0/image). Si una clave tiene una barra adentro, como las anotaciones example.com/owner, la barra se escribe ~1.
The path walks the YAML with slashes; lists go by index from 0 (/spec/template/spec/containers/0/image). If a key contains a slash, like the example.com/owner annotation, the slash is written ~1.
Il path percorre lo YAML con le barre; le liste vanno per indice da 0 (/spec/template/spec/containers/0/image). Se una chiave ha una barra dentro, come l'annotazione example.com/owner, la barra si scrive ~1.
Generadores: por qué el hash en el nombre
Generators: why the hash in the name
Generatori: perché l'hash nel nome
configMapGenerator y secretGenerator son campos de Kustomize, el equivalente de kubectl create cm/secret --from-literal / --from-file / --from-env-file (ver M4 §4.3). Le agregan al nombre un hash del contenido y reescriben solos todas las referencias en los pods. La cadena es:
configMapGenerator and secretGenerator are Kustomize fields, the equivalent of kubectl create cm/secret --from-literal / --from-file / --from-env-file (see M4 §4.3). They append a content hash to the name and rewrite every reference in the pods on their own. The chain is:
configMapGenerator e secretGenerator sono campi di Kustomize, l'equivalente di kubectl create cm/secret --from-literal / --from-file / --from-env-file (vedi M4 §4.3). Aggiungono al nome un hash del contenuto e riscrivono da soli tutti i riferimenti nei pod. La catena è:
# cambiás un valor → cambia el hash → cambia el nombre# you change a value → the hash changes → the name changes# cambi un valore → cambia l'hash → cambia il nome
# → cambia el pod template → rollout AUTOMÁTICO# → the pod template changes → AUTOMATIC rollout# → cambia il pod template → rollout AUTOMATICO
Sin generador, editar un ConfigMap no reinicia los pods que lo leen como env: hace falta rollout restart (lo que hubo que hacer con kube-proxy). Los ConfigMaps viejos quedan en el cluster; si no querés el hash, options: {disableNameSuffixHash: true}, pero perdés el rollout automático.
Without a generator, editing a ConfigMap does not restart the pods that read it as env: you need a rollout restart (what kube-proxy needed). The old ConfigMaps stay in the cluster; if you do not want the hash, options: {disableNameSuffixHash: true}, but you lose the automatic rollout.
Senza generatore, modificare un ConfigMap non riavvia i pod che lo leggono come env: serve un rollout restart (come con kube-proxy). I ConfigMap vecchi restano nel cluster; se non vuoi l'hash, options: {disableNameSuffixHash: true}, ma perdi il rollout automatico.
Las tres formas de meterlos en el pod
The three ways into the pod
I tre modi di metterli nel pod
| Querés… | Campo del contenedor |
| You want… | Container field |
| Vuoi… | Campo del container |
| Todas las claves como variables | envFrom: - configMapRef / secretRef |
| Every key as variables | envFrom: - configMapRef / secretRef |
| Tutte le chiavi come variabili | envFrom: - configMapRef / secretRef |
| Una clave, con el nombre que elijas | env: - valueFrom: configMapKeyRef / secretKeyRef |
| One key, under the name you choose | env: - valueFrom: configMapKeyRef / secretKeyRef |
| Una chiave, col nome che scegli | env: - valueFrom: configMapKeyRef / secretKeyRef |
| Como archivos en un directorio | volumes: configMap / secret + volumeMounts |
| As files in a directory | volumes: configMap / secret + volumeMounts |
| Come file in una directory | volumes: configMap / secret + volumeMounts |
★
El secretGenerator codifica en base64 por vos, pero el valor queda en texto plano en tu kustomization.yaml. En un repo real eso es una fuga: para el examen alcanza, en producción se usa un gestor de secretos.
secretGenerator base64-encodes for you, but the value sits in plain text in your kustomization.yaml. In a real repo that is a leak: fine for the exam, in production you use a secrets manager.
Il secretGenerator codifica in base64 per te, ma il valore resta in chiaro nel tuo kustomization.yaml. In un repo vero è una fuga: per l'esame va bene, in produzione si usa un gestore di segreti.