Tanquem el mòdul amb la lliçó que converteix Rutas Norte en una plataforma en què es pot confiar. Ja tenim les dades fora del contenidor, en un volum que sobreviu al pod, expandible i amb snapshots. Però la lliçó anterior va acabar amb un advertiment que ho relativitza tot: un snapshot viu al mateix sistema d'emmagatzematge que l'original, així que no sobreviu a la pèrdua d'una zona, a l'esborrat d'un compte ni a una corrupció descoberta tres setmanes tard. Una còpia de seguretat de veritat és una altra cosa: surt del clúster, va a un sistema independent, inclou també els objectes de l'API, es coordina amb l'aplicació per ser consistent, té retenció definida i —això és l'únic innegociable— es prova restaurant-la. En aquesta lliçó construiràs les dues peces que Rutas Norte necessita: el bolcat lògic programat de postgres-reserves i la còpia de la plataforma completa amb Velero, i faràs el simulacre de desastre esborrant un entorn sencer per recuperar-lo amb cronòmetre.

Contingut

  1. Què cal salvar realment en un clúster
  2. Snapshot no és còpia de seguretat: la regla 3-2-1
  3. RPO i RTO aplicats a Rutas Norte amb números
  4. Còpia lògica: pg_dump des d'un Job programat
  5. Compressió, xifratge i restauració del bolcat
  6. Velero: què fa i com està construït
  7. Instal·lació i primera còpia d'un namespace
  8. Hooks pre i post: la còpia consistent
  9. Còpies programades amb retenció
  10. Restaurar en un altre namespace
  11. El simulacre de desastre
  12. Què no es restaura sol i el runbook mínim
  13. Retenció, cost i dades personals

  1. Què cal salvar realment en un clúster

La primera pregunta no és "com faig còpies" sinó "de què". En un clúster de Kubernetes hi ha tres coses diferents, i cadascuna necessita una tècnica diferent:

Què On viu Tècnica Ja ho tenim?
Els manifests (Deployments, Services, Ingress, NetworkPolicies…) A Git, com a font de veritat Control de versions : k8s/base i k8s/entorns/...
L'estat de l'API (objectes reals, inclosos els creats fora de Git: Secrets, PVC, certificats de cert-manager, anotacions posades per controladors) A etcd Còpia d'objectes de l'API (Velero) o còpia d'etcd No
Les dades dels volums Als PV Bolcat lògic i/o snapshot exportat No

Tres observacions que orienten tota la resta. Git no basta: els manifests permeten recrear la forma de la plataforma, però no contenen els Secrets (que vam deixar deliberadament fora de Git a 03-02), ni els certificats emesos per cert-manager (04-05), ni per descomptat les dades. La còpia d'etcd és cosa de l'administrador del clúster, no de l'equip d'aplicació, i en un clúster gestionat ni tan sols tens accés a etcd: per això l'enfocament pràctic per a Rutas Norte és copiar objectes de l'API per namespace, que a més és portable entre clústers. I el que és veritablement irreemplaçable són les dades: un Deployment es recrea en segons; una reserva venuda i cobrada, no.

  1. Snapshot no és còpia de seguretat: la regla 3-2-1

Reprenem l'advertiment de 05-05 i li donem forma operativa amb la regla clàssica, que continua sent el millor resum. 3-2-1: almenys 3 còpies de les dades, en 2 suports o sistemes diferents, amb 1 d'elles fora de l'emplaçament principal. Aplicada a Rutas Norte:

Element Compta com Compleix
El volum de postgres-reserves en producció La còpia 1 (l'original)
Snapshots CSI diaris i previs a canvis La còpia 2, mateix sistema 3 parcial, 2 no, 1 no
Bolcats pg_dump en un PVC del clúster La còpia 3, mateix clúster 3 sí, 2 parcial, 1 no
Còpies Velero en un magatzem d'objectes en una altra regió 1 sí

La quarta fila és la que converteix el conjunt en una estratègia: sense ella, tota la resta desapareix juntament amb la infraestructura que protegeix. Dos reforços que avui es consideren obligatoris: la immutabilitat —el magatzem d'objectes en mode bloqueig (Object Lock / WORM), perquè ni tan sols unes credencials robades puguin esborrar o xifrar les còpies— i la separació de credencials: el compte que escriu les còpies no ha de poder esborrar-les, i el destí ha d'estar en un altre compte o subscripció.

  1. RPO i RTO aplicats a Rutas Norte amb números

Dues sigles que cal separar bé perquè es confonen constantment:

RPO (Recovery Point Objective) RTO (Recovery Time Objective)
Pregunta Quantes dades podem perdre? Quant temps podem estar caiguts?
Ho determina La freqüència de les còpies La velocitat de la restauració

I ara els números, que és el que converteix la conversa en una cosa útil. Rutas Norte ven unes 60 reserves per hora en dia laborable normal, i unes 400 en pont o inici de vacances, amb pics de 700.

Freqüència de còpia (RPO) Perdudes en dia normal Perdudes en pont Cost operatiu
24 h (còpia nocturna) fins a 1.440 fins a 9.600 Mínim
6 h fins a 360 fins a 2.400 Baix
1 h fins a 60 fins a 400 Mitjà
5 min (WAL arxivat continu) ~5 ~33 Alt

Cada fila implica una conversa de negoci, no tècnica: quant costa perdre 9.600 reserves? No és només l'import; és reconstruir a mà, atendre reclamacions, clients que es presenten a l'estació sense bitllet i dany reputacional en plena temporada alta.

Decisió de Rutas Norte:

Entorn RPO objectiu RTO objectiu Com s'aconsegueix
rutas-norte-pro 1 hora (15 min en temporada alta) 2 hores Bolcat lògic horari + Velero amb snapshot + WAL arxivat (pendent)
rutas-norte-pre 24 h 8 h Velero nocturn
rutas-norte-dev Sense objectiu Millor esforç Es recrea des de Git

L'RTO mesurat a l'exercici de 05-05 —de l'ordre de dos minuts en local— era el de restaurar un volum. L'RTO real d'un desastre inclou detectar, decidir, restaurar, verificar i reobrir el servei. Per això cal mesurar-lo amb un simulacre (apartat 11), no estimar-lo.

  1. Còpia lògica: pg_dump des d'un Job programat

La còpia lògica és un fitxer amb les instruccions necessàries per reconstruir la base de dades. Els seus avantatges davant d'un snapshot de blocs són decisius:

Bolcat lògic (pg_dump) Snapshot de blocs
Consistència Garantida: s'executa en una transacció Crash-consistent llevat de coordinació
Verificable Sí: si es restaura, les dades són coherents No demostra res per si mateix
Portable a una altra versió, un altre clúster, un altre proveïdor No
Permet restaurar una sola taula No
Mida Petita (comprimeix molt bé) La del volum
Velocitat en bases grans Lenta Instantània

No competeixen: es complementen. El snapshot és ràpid per desfer; el bolcat és la còpia que de veritat demostra que les dades estan bé.

Primer, el volum on s'escriuen els bolcats: k8s/base/copies-postgres-pvc.yaml, un PVC anomenat copies-postgres de 50 GiB, ReadWriteOnce, amb les etiquetes habituals més app.kubernetes.io/component: copies-de-seguretat. Va a la classe rutasnorte-rapida (amb Retain) perquè si es perden les còpies, la xarxa de seguretat desapareix.

I ara el CronJob. Nota: l'objecte CronJob s'estudia a fons a 06-03; aquí el fem servir com a eina, quedant-nos amb l'imprescindible per llegir-lo.

# k8s/entorns/pro/cronjob-copia-postgres.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: copia-postgres-reserves
  namespace: rutas-norte-pro
  labels:
    app: copia-postgres-reserves
    app.kubernetes.io/component: copies-de-seguretat
    app.kubernetes.io/part-of: rutas-norte
    entorn: pro
spec:
  schedule: "0 * * * *"              # cada hora en punt (RPO d'1 h)
  timeZone: "Europe/Madrid"
  concurrencyPolicy: Forbid          # mai dos bolcats alhora
  successfulJobsHistoryLimit: 3      # i failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      backoffLimit: 2
      activeDeadlineSeconds: 3600    # si triga mes d'1 h, alguna cosa va malament
      template:
        metadata:
          labels: { app: copia-postgres-reserves, entorn: pro }
        spec:
          restartPolicy: Never
          serviceAccountName: copies-postgres    # SA propia (03-06)
          securityContext: { runAsNonRoot: true, runAsUser: 999, fsGroup: 999 }
          containers:
            - name: pg-dump
              image: postgres:16.4
              env:
                - { name: PGHOST, value: postgres-reserves }   # el Service (02-05)
                - name: PGUSER
                  valueFrom:
                    secretKeyRef: { name: postgres-reserves-credencials, key: username }
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef: { name: postgres-reserves-credencials, key: password }
                - name: PGDATABASE
                  valueFrom:
                    secretKeyRef: { name: postgres-reserves-credencials, key: database }
              command:
                - /bin/bash
                - -c
                - |
                  set -euo pipefail
                  DESTI="/copies/reserves-$(date +%Y%m%d-%H%M%S).dump"
                  # -Fc: format comprimit de PostgreSQL; permet restaurar
                  #      taules soltes. -Z6: nivell de compressio (0-9).
                  pg_dump -Fc -Z6 --no-owner --no-privileges -f "${DESTI}"
                  # Verificacio basica: el bolcat es pot LLEGIR.
                  pg_restore --list "${DESTI}" > /dev/null
                  echo "[$(date -Is)] bolcat correcte: $(du -h ${DESTI} | cut -f1)"
                  # Retencio local: 72 h al PVC. La llarga va al magatzem
                  # d'objectes amb Velero (apartat 9).
                  find /copies -name 'reserves-*.dump' -mmin +4320 -print -delete
              volumeMounts:
                - { name: copies, mountPath: /copies }
              resources:
                requests: { cpu: 200m, memory: 256Mi }
                limits:   { cpu: "1",  memory: 1Gi }
          volumes:
            - name: copies
              persistentVolumeClaim: { claimName: copies-postgres }

Les decisions que hi ha a dins, una a una:

  • concurrencyPolicy: Forbid: si un bolcat triga més d'una hora, el següent no arrenca. Dos pg_dump simultanis duplicarien la càrrega sobre la base de dades just quan ja va lenta.
  • set -euo pipefail i la verificació amb pg_restore --list: sense el primer, un pg_dump fallit pot deixar un fitxer truncat i el Job acabar en Completed, donant-te còpies que no valen; el segon comprova que el bolcat és almenys llegible. La verificació de veritat és restaurar-lo (apartat 11).
  • --no-owner --no-privileges: el bolcat no arrossega propietaris ni permisos, cosa que permet restaurar-lo en un altre clúster amb un altre usuari. I amb la ServiceAccount pròpia (03-06) el pod només té el que necessita, mentre que la retenció local de 72 h converteix el PVC en la còpia calenta per restaurar ràpid; la retenció llarga viu fora del clúster.

I cal recordar les NetworkPolicies de 04-06: el pod del CronJob és nou al namespace i el deny-all el bloquejarà. Cal autoritzar explícitament la seva conversa:

# k8s/entorns/pro/np-06-copies-postgres.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: copies-postgres-egress
  namespace: rutas-norte-pro
spec:
  podSelector:
    matchLabels: { app: copia-postgres-reserves }
  policyTypes: [Egress]
  egress:
    - to:
        - podSelector: { matchLabels: { app: postgres-reserves } }
      ports: [{ protocol: TCP, port: 5432 }]
    - to:                                    # DNS, o res no resol
        - namespaceSelector:
            matchLabels: { kubernetes.io/metadata.name: kube-system }
          podSelector: { matchLabels: { k8s-app: kube-dns } }
      ports:
        - { protocol: UDP, port: 53 }
        - { protocol: TCP, port: 53 }

Verificació:

kubectl apply -f k8s/base/copies-postgres-pvc.yaml
kubectl apply -f k8s/entorns/pro/cronjob-copia-postgres.yaml
kubectl apply -f k8s/entorns/pro/np-06-copies-postgres.yaml
# Llancar una execucio manual sense esperar a l'hora en punt
kubectl create job --from=cronjob/copia-postgres-reserves \
  copia-manual-$(date +%s) -n rutas-norte-pro
kubectl logs -n rutas-norte-pro -l app=copia-postgres-reserves --tail=20
# [2026-08-05T12:00:41+02:00] bolcat correcte: 412M

  1. Compressió, xifratge i restauració del bolcat

Xifratge

El format -Fc comprimeix però no xifra. I aquest fitxer conté el nom, el DNI, el telèfon i el correu de tots els clients de Rutas Norte: és exactament el tipus d'artefacte que no pot quedar en clar en cap suport. La solució pràctica és xifrar amb una clau pública la clau privada de la qual no és al clúster:

# Al contenidor del CronJob, substituint el pg_dump -f per una canonada:
pg_dump -Fc -Z6 --no-owner --no-privileges \
  | age -r "$CLAU_PUBLICA_COPIES" -o "${DESTI}.age"

La variable CLAU_PUBLICA_COPIES arriba per secretKeyRef des d'un Secret copies-clau-publica. I aquí hi ha el punt important: al clúster només hi viu la clau pública, així que qui comprometi el clúster pot escriure còpies però no pot llegir-les. La clau privada es custodia fora (gestor de secrets corporatiu, caixa forta), i el seu procediment d'accés ha d'estar documentat, perquè una còpia que ningú no pot desxifrar el dia del desastre no és una còpia.

Restauració

Es llança un pod auxiliar amb la imatge postgres:16.4 que munti el PVC copies-postgres a /copies (amb kubectl run ... --overrides o un Job d'un sol ús), i des de dins:

# 1. Localitzar el bolcat
ls -lh /copies/

# 2. Restaurar sobre una base de dades NOVA (mai sobre la de produccio)
export PGHOST=postgres-reserves PGUSER=rutasnorte PGPASSWORD=...
createdb reserves_restaurada
pg_restore -d reserves_restaurada --no-owner --jobs=4 \
  /copies/reserves-20260805-120003.dump

# 3. Verificar ABANS de tocar res real
psql -d reserves_restaurada -c "SELECT count(*), max(data) FROM reserves;"
#  count  |    max
# --------+------------
#  184392 | 2026-12-28

Restaura sempre a una base de dades nova i verifica abans de substituir. Restaurar directament sobre la base de producció amb --clean és la forma més ràpida de convertir un incident recuperable en un d'irreversible.

Opcions de pg_restore que estalvien temps el dia dolent:

--jobs=4 restaura en paral·lel i redueix molt l'RTO en bases grans; --table=reserves restaura una sola taula, que és el cas més comú (algú ha esborrat una taula); --schema-only i --data-only separen estructura i dades; i --list amb --use-list permeten inspeccionar el contingut i restaurar només una part.

  1. Velero: què fa i com està construït

El bolcat lògic protegeix les dades de la base. No protegeix els Secrets, ni els PVC, ni els Ingress, ni els certificats, ni la resta d'objectes del namespace. Per a això hi ha Velero, l'eina estàndard de còpia i migració de recursos de Kubernetes.

Velero fa tres coses. Copia els objectes de l'API d'un namespace (o del clúster sencer), filtrats per etiquetes o per tipus, a un magatzem d'objectes (S3, Azure Blob, GCS, MinIO). Copia les dades dels volums per dues vies: demanant snapshots al driver CSI, o copiant fitxer a fitxer amb Kopia/Restic al mateix magatzem d'objectes, que és el que de veritat treu les dades del sistema d'emmagatzematge. I restaura tot això, al mateix clúster o en un altre, amb reassignació de namespaces i filtres.

flowchart TB
    subgraph CLUSTER["Cluster de Kubernetes"]
        API["kube-apiserver"]
        SRV["Deployment velero (controlador)<br/>+ plugins d'objectes i CSI"]
        NA["DaemonSet node-agent<br/>(copia de fitxers amb Kopia)"]
        PVCS[("PVC de rutas-norte-pro")]
    end
    OBJ[("Magatzem d'objectes<br/>s3://rutasnorte-copies<br/>ALTRA REGIO, immutable")]
    SNAP[("Snapshots CSI<br/>mateix sistema d'emmagatzematge")]

    SRV -->|"llegeix objectes"| API
    SRV -->|"manifests + metadades"| OBJ
    SRV -->|"VolumeSnapshot"| SNAP
    NA -->|"dades fitxer a fitxer"| OBJ
    PVCS --- NA

La distinció que cal tenir clara des del principi:

Mètode de volum On acaben les dades Sobreviu a perdre la regió? Velocitat
Snapshots CSI (--snapshot-volumes) Al mateix sistema d'emmagatzematge No Molt ràpida
Fitxers (--default-volumes-to-fs-backup) Al magatzem d'objectes Lenta

Per a Rutas Norte: snapshots CSI per al dia a dia (ràpid, per desfer) i còpia al magatzem d'objectes per a la còpia setmanal de retenció llarga, que és la que compleix l'"1" de la regla 3-2-1.

  1. Instal·lació i primera còpia d'un namespace

Per practicar a minikube farem servir MinIO com a magatzem d'objectes compatible amb S3, desplegat al mateix clúster. En producció seria un bucket real en una altra regió i un altre compte.

# 1. CLI de Velero (descarrega la release i mou el binari a /usr/local/bin)
velero version --client-only
# 2. Credencials del magatzem (en produccio, d'un compte que NO pugui esborrar)
printf '[default]\naws_access_key_id=minio\naws_secret_access_key=minio123\n' \
  > credencials-velero
# 3. Instal·lar el servidor al cluster
velero install --provider aws \
  --plugins velero/velero-plugin-for-aws:v1.11.0,velero/velero-plugin-for-csi:v0.7.0 \
  --bucket rutasnorte-copies --secret-file ./credencials-velero \
  --use-node-agent --features=EnableCSI \
  --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio.velero.svc:9000 \
  --snapshot-location-config region=minio

kubectl get pods -n velero
velero backup-location get      # PHASE ha de dir Available

La primera còpia, del namespace de preproducció:

velero backup create pre-completa-$(date +%Y%m%d) \
  --include-namespaces rutas-norte-pre --default-volumes-to-fs-backup \
  --ttl 168h0m0s --labels entorn=pre,tipus=manual
velero backup describe pre-completa-20260805 --details
Name: pre-completa-20260805    Phase: Completed    TTL: 168h0m0s
Resource List:
  apps/v1/Deployment: 5      v1/Service: 5         v1/ConfigMap: 7
  v1/Secret: 4               v1/ServiceAccount: 6  v1/PersistentVolumeClaim: 2
  networking.k8s.io/v1/Ingress: 2    networking.k8s.io/v1/NetworkPolicy: 6
Backup Volumes (Pod Volume Backups - kopia):
  postgres-reserves-.../dades: Completed (18.4GB)

Fixa't en el que ha capturat: els Secrets, que no són a Git; els PVC; les NetworkPolicies; els ServiceAccounts. I les dades del volum, 18,4 GB copiats fitxer a fitxer al magatzem d'objectes.

Les dues variants de volums són --snapshot-volumes=false (només objectes de l'API: rapidíssim, però sense dades) i --snapshot-volumes=true (snapshots CSI: ràpid, però les dades es queden al mateix sistema d'emmagatzematge).

  1. Hooks pre i post: la còpia consistent

Aquí resolem el problema de consistència que vam deixar obert a 05-05. Els hooks de Velero executen una ordre dins del contenidor abans i després de copiar el volum, cosa que permet deixar la base de dades en un estat coherent.

Es declaren com a anotacions al pod, o al mateix backup. La forma declarativa, al podTemplate del Deployment:

    metadata:
      labels: { app: postgres-reserves, entorn: pro }
      annotations:
        # ABANS de copiar el volum: mode copia de PostgreSQL
        pre.hook.backup.velero.io/container: postgres
        pre.hook.backup.velero.io/command: >-
          ["/bin/bash","-c",
           "psql -U rutasnorte -d reserves -c \"SELECT pg_backup_start('velero', true);\" &&
            psql -U rutasnorte -d reserves -c 'CHECKPOINT;'"]
        pre.hook.backup.velero.io/timeout: 3m
        # DESPRES: sortir del mode copia SEMPRE, hagi anat be o malament
        post.hook.backup.velero.io/container: postgres
        post.hook.backup.velero.io/command: >-
          ["/bin/bash","-c",
           "psql -U rutasnorte -d reserves -c 'SELECT pg_backup_stop();'"]
        post.hook.backup.velero.io/timeout: 3m

Què fa cadascun. pg_backup_start (nom a partir de PostgreSQL 15; abans era pg_start_backup) posa el servidor en mode còpia: força un checkpoint i garanteix que els fitxers del directori de dades formin un conjunt restaurable, encara que continuïn arribant escriptures. I pg_backup_stop tanca aquest mode: és imprescindible que s'executi sempre, perquè una base de dades que es queda en mode còpia acumula WAL sense alliberar i acaba omplint el disc; per això el hook post s'executa encara que la còpia falli.

Alternativa més simple i sovint preferible per a bases mitjanes: el hook pre fa directament un pg_dump a un emptyDir que Velero també copia. Sacrifica una mica de temps a canvi d'una còpia lògica verificable.

Nivell Com Consistència Cost
Sense hooks Còpia directa del volum Crash-consistent Nul
Hooks pg_backup_start/stop Mode còpia de PostgreSQL Consistent a nivell d'aplicació Baix
Hook amb pg_dump Bolcat lògic dins de la còpia Consistent i verificable Mitjà
velero backup create pro-consistent-$(date +%Y%m%d) \
  --include-namespaces rutas-norte-pro --default-volumes-to-fs-backup
velero backup logs pro-consistent-20260805 | grep -i hook
# level=info msg="Running exec hook" hookPhase=pre  pod=.../postgres-reserves-...
# level=info msg="Running exec hook" hookPhase=post pod=.../postgres-reserves-...

  1. Còpies programades amb retenció

Una còpia manual serveix per aprendre; el que protegeix és una programació amb retenció automàtica, que Velero resol amb Schedule i el TTL, que esborra la còpia i les seves dades en expirar.

# Diaria de produccio amb snapshots: rapida, retencio curta (7 dies)
velero schedule create pro-diaria --schedule="0 2 * * *" \
  --include-namespaces rutas-norte-pro --snapshot-volumes=true \
  --ttl 168h0m0s --labels entorn=pro,tipus=diaria

# Setmanal al magatzem d'objectes: lenta, retencio llarga (90 dies)
velero schedule create pro-setmanal --schedule="0 3 * * 0" \
  --include-namespaces rutas-norte-pro --default-volumes-to-fs-backup \
  --ttl 2160h0m0s --labels entorn=pro,tipus=setmanal

# Preproduccio (14 dies)
velero schedule create pre-diaria --schedule="0 4 * * *" \
  --include-namespaces rutas-norte-pre --default-volumes-to-fs-backup \
  --ttl 336h0m0s
velero schedule get      # les tres, en estat Enabled

El calendari resultant de Rutas Norte:

Còpia Freqüència Mètode Retenció Protegeix de
Bolcat pg_dump (CronJob) Cada hora Lògic, a PVC 72 h Error humà a les dades, recent
Velero pro-diaria Diària Snapshots CSI 7 dies Fallada de desplegament, esborrat d'objectes
Velero pro-setmanal Setmanal Fitxers al magatzem 90 dies Pèrdua de zona, compte, corrupció antiga
Snapshot manual Abans de cada canvi amb risc CSI 72 h Migració fallida

Només la fila pro-setmanal compleix l'"1" de la regla 3-2-1. Les altres són comoditat i velocitat.

Vigilància obligatòria: una còpia que falla en silenci és pitjor que no tenir còpies, perquè genera confiança infundada. Es detecta amb velero backup get | grep -v Completed i s'investiga amb velero backup describe <nom> --details i velero backup logs <nom> | grep -i error. Aquesta comprovació ha de ser una alerta automàtica, no una revisió manual; es munta amb les eines de 07-04.

  1. Restaurar en un altre namespace

Restaurar sobre el namespace original en producció és l'operació més delicada. La pràctica correcta és restaurar primer en un altre lloc i verificar:

velero restore create verificacio-$(date +%s) \
  --from-backup pro-setmanal-20260802030012 \
  --namespace-mappings rutas-norte-pro:rutas-norte-verificacio \
  --include-resources deployments,services,configmaps,secrets,persistentvolumeclaims \
  --wait
velero restore describe verificacio-1754392011 --details
kubectl get all,pvc -n rutas-norte-verificacio
Phase:  Completed
Warnings:
  rutas-norte-verificacio:  could not restore, Ingress "botiga-web" already exists

Opcions de restauració que es fan servir cada dia:

Opció Per a què
--namespace-mappings origen:desti Restaurar en un altre namespace: verificació, o clonar pre a partir de pro
--include-resources / --exclude-resources / --selector Restaurar només el necessari: certs tipus, o un sol component
--existing-resource-policy=update Sobreescriure el que ja existeix (per defecte no ho fa)
--restore-volumes=false Només els objectes, sense dades

I l'avís que estalvia un ensurt: per defecte Velero NO sobreescriu els objectes que ja existeixen. Si restaures sobre un namespace viu, veuràs un munt d'avisos "already exists" i creuràs que la restauració ha fallat, quan el que ha fet és protegir-te. Per a una recuperació real, el namespace destí ha d'estar buit o cal fer servir --existing-resource-policy=update conscientment.

  1. El simulacre de desastre

Una còpia no provada no és una còpia. Aquesta és la pràctica obligatòria del mòdul, i a Rutas Norte s'executa cada trimestre sobre rutas-norte-pre, amb cronòmetre i amb acta.

# --- ESTAT PREVI: documentar el que ha de tornar ---
kubectl get all,pvc,secret,ingress,networkpolicy -n rutas-norte-pre --no-headers | wc -l
kubectl exec -n rutas-norte-pre deploy/postgres-reserves -- \
  psql -U rutasnorte -d reserves -c "SELECT count(*) FROM reserves;" | tee inventari-previ.txt
curl -s -o /dev/null -w "%{http_code}\n" https://pre.rutasnorte.example/

# --- COPIA DE PARTIDA ---
velero backup create simulacre-$(date +%Y%m%d) \
  --include-namespaces rutas-norte-pre --default-volumes-to-fs-backup --wait

# --- EL DESASTRE ---
T0=$(date +%s); kubectl delete namespace rutas-norte-pre

# --- RECUPERACIO ---
velero restore create recuperacio-$(date +%s) \
  --from-backup simulacre-20260805 --wait
kubectl wait --for=condition=available --timeout=900s deployment --all -n rutas-norte-pre

# --- VERIFICACIO: no n'hi ha prou que els pods estiguin Running ---
kubectl get all,pvc -n rutas-norte-pre
kubectl exec -n rutas-norte-pre deploy/postgres-reserves -- \
  psql -U rutasnorte -d reserves -c "SELECT count(*) FROM reserves;"   # -> 184392
curl -s -o /dev/null -w "%{http_code}\n" https://pre.rutasnorte.example/   # -> 200
echo "RTO MESURAT: $(( ($(date +%s) - T0) / 60 )) minuts"   # -> 23 minuts

L'acta del simulacre ha de recollir, com a mínim: data i responsable, còpia utilitzada, RTO mesurat, RPO efectiu (el temps transcorregut entre la còpia i el desastre), què no va tornar sol, incidències i accions derivades. Al segon exercici la redactaràs sencera.

L'apartat "què no va tornar sol" és el més valuós de l'acta. És literalment impossible de conèixer sense fer el simulacre, i és el que converteix una recuperació teòrica en una recuperació real.

  1. Què no es restaura sol i el runbook mínim

Element Per què no torna Què cal fer
Certificats TLS cert-manager els reemet; si es restaura el Secret antic pot estar caducat Deixar que es reemetin; vigilar els límits de taxa de Let's Encrypt (04-05)
IP del LoadBalancer i registres DNS S'assignen IP noves en recrear el Service, i el DNS viu fora del clúster Reservar IP estàtiques i actualitzar www i api de rutasnorte.example, comptant amb la propagació
Secrets externs Tokens de la passarel·la de pagaments, del proveïdor SMTP: poden haver rotat Regenerar des del gestor de secrets corporatiu
Tallafocs, grups de seguretat i objectes d'altres namespaces Els primers són del núvol, no del clúster; els segons no entren si la còpia era d'un sol namespace Infraestructura com a codi, i copiar també ingress-nginx, cert-manager i velero
Recursos de clúster (StorageClasses, ClusterIssuers, ClusterRoles) No entren en una còpia de namespace, i sense ells els PV no s'aprovisionen --include-cluster-resources=true, i crear les StorageClasses abans de restaurar

Runbook mínim de recuperació de Rutas Norte

Un document curt, al repositori, que algú de guàrdia a les tres de la matinada pugui seguir sense pensar:

RUNBOOK: recuperacio de rutas-norte-pro
Responsable de guardia: [email protected]   |   Ultima prova: 2026-08-05

0. DECLARAR L'INCIDENT. Obrir canal, anotar hora T0, avisar atencio al
   client. NO improvisar: si hi ha dubtes sobre l'abast, anar al pas 1 igual.
1. AVALUAR l'abast
   kubectl get nodes; kubectl get all -n rutas-norte-pro; velero backup get
   -> Nomes dades corruptes? -> pas 3   |   Namespace destruit? -> pas 4
   -> Cluster perdut?        -> pas 5
2. CONTENIR. Escalar a 0 el que pugui seguir escrivint dades dolentes:
   kubectl scale deploy api-reserves worker-notificacions -n rutas-norte-pro --replicas=0
3. RESTAURACIO LOGICA (RTO ~30 min)
   Bolcat mes recent al PVC copies-postgres -> pg_restore a reserves_restaurada
   VERIFICAR recompte i data maxima -> reanomenar bases de dades -> arrencar
4. RESTAURACIO DEL NAMESPACE (RTO ~45 min)
   velero restore create --from-backup <ultima Completed>
   Comprovar: pods Ready, PVC Bound, Ingress amb IP, certificat valid
   Actualitzar DNS si la IP del LoadBalancer ha canviat
5. CLUSTER NOU (RTO ~4 h)
   Crear cluster -> addons (ingress-nginx, cert-manager, csi, velero)
   Crear StorageClasses ABANS de restaurar
   velero restore create --include-cluster-resources=true
   Regenerar secrets externs (pagaments, SMTP) i actualitzar DNS
6. VERIFICAR EL NEGOCI, no nomes els pods: recompte i data maxima de
   reserves, compra d'un bitllet de prova d'extrem a extrem, i
   https://www.rutasnorte.example i https://api.rutasnorte.example -> 200
7. TANCAR: anotar T_fi, RTO i RPO reals, i obrir el post-mortem.

  1. Retenció, cost i dades personals

Cost

Les còpies costen diners, i sense retenció automàtica la despesa creix sense fre. Amb postgres-reserves en uns 20 GiB, els 72 bolcats horaris ocupen uns 30 GiB (comprimeixen a ~410 MB cadascun), els 7 snapshots diaris uns 25 GiB per ser incrementals, i les 13 còpies setmanals uns 260 GiB al magatzem d'objectes. Palanques per ajustar-ho: fer servir classes d'emmagatzematge fredes per al que és antic, aprofitar la deduplicació de Kopia, i no copiar el que es pot regenerar (redis-cache no entra en cap còpia, pel que es va decidir a 05-03).

Dades personals

I aquí hi ha el que no pot quedar com un detall tècnic. Les còpies de seguretat de Rutas Norte contenen dades personals de clients: nom, DNI, telèfon i correu de cada persona que ha comprat un bitllet. Això significa que cada còpia és un tractament de dades personals amb els mateixos deures que el sistema original, i en alguns aspectes més exigents, perquè les còpies es multipliquen, es repliquen i s'obliden.

Els requisits mínims que Rutas Norte aplica:

Requisit Com s'implementa aquí
Xifratge en repòs i en trànsit Bolcats xifrats amb age (clau privada fora del clúster); magatzem d'objectes amb xifratge del costat del servidor i accés per HTTPS; volums amb encrypted: "true" a la StorageClass (05-04); trànsit intern segmentat per NetworkPolicies (04-06)
Control d'accés ServiceAccount dedicada i RBAC mínim (08-01); credencials del magatzem amb permisos de només escriptura
Termini d'esborrat definit TTL a cada Schedule de Velero; find -mmin +4320 -delete al CronJob. Sense retenció automàtica, les còpies són eternes i això és incompliment
Àmbit geogràfic El bucket de còpies ha d'estar en una regió prevista i declarada. Copiar a una altra regió per resistir desastres no pot treure les dades de l'àmbit legal permès
Registre d'accessos i dret de supressió Auditoria de qui descarrega o restaura una còpia (08-06); i si un client exerceix el seu dret d'esborrat, cal decidir i documentar què passa amb les còpies que el contenen
Entorns no productius Un clon de producció a pre o en un portàtil de desenvolupament són dades reals en un entorn menys protegit. Anonimitzar o pseudonimitzar

ADVERTIMENT DE COMPLIMENT NORMATIU

Tot l'anterior és arquitectura tècnica, no assessorament legal. Els terminis de retenció, la ubicació geogràfica de les còpies, el tractament del dret de supressió sobre dades ja copiades, la base legal del tractament i les obligacions davant d'una bretxa de seguretat han de ser revisats i aprovats pel responsable de compliment normatiu i de protecció de dades de l'organització abans de posar en marxa aquesta configuració en producció. Els valors d'aquest curs (72 hores, 7 dies, 90 dies) són exemples didàctics triats per il·lustrar la mecànica de la retenció, no recomanacions legals: el termini correcte depèn de la normativa aplicable, del sector i de la finalitat declarada de cada tractament, i conservar dades personals més temps del necessari és tan incompliment com no protegir-les.

Errors Comuns i Consells

Error Símptoma Solució
Confiar en snapshots com a única còpia Pèrdua total si cau la zona o el compte Còpia en magatzem d'objectes, una altra regió, un altre compte
No provar mai la restauració Es descobreix el dia del desastre que la còpia no val Simulacre trimestral amb acta
Restaurar directament sobre producció Un incident recuperable es torna irreversible Restaurar a base o namespace nous i verificar
Còpies que fallen en silenci, o sense set -euo pipefail al bolcat Fitxers truncats, Jobs en Completed i confiança infundada durant mesos Fallar sorollosament, verificar el bolcat i alertar sobre estats diferents de Completed
Bolcats sense xifrar, o copiar producció a desenvolupament sense anonimitzar Dades personals en clar en un PVC, un bucket o un entorn poc protegit Xifratge amb clau pública (privada fora del clúster); anonimitzar o pseudonimitzar
Sense retenció Cost creixent i incompliment normatiu TTL als Schedules i neteja al CronJob
Oblidar els recursos de clúster La restauració falla per StorageClasses inexistents --include-cluster-resources=true; crear classes abans
Oblidar les NetworkPolicies amb el CronJob El bolcat no connecta amb la base de dades Autoritzar el seu egress explícitament (04-06)
Hook pre sense hook post La base es queda en mode còpia i omple el disc de WAL El post s'ha d'executar sempre
Creure que l'RTO és el temps de velero restore El simulacre real dona el triple Mesurar d'extrem a extrem, inclosos DNS i verificació

Consells:

  1. Automatitza la verificació, no només la còpia. Un Job setmanal que restauri l'últim bolcat en un namespace de proves i compari el recompte de files converteix "creiem que tenim còpies" en "sabem que tenim còpies".
  2. Documenta l'RPO i l'RTO reals, no els desitjats, i revisa'ls després de cada simulacre. Són la dada que necessita el negoci per decidir quant invertir.
  3. La còpia ha de poder restaurar-se sense la persona que la va configurar. Si el procediment viu al cap d'algú, no existeix: escriu-lo al runbook, al repositori. I quan dubtis entre gastar en còpies o en qualsevol altra cosa, gasta en còpies: és l'únic component de la plataforma l'absència del qual no es nota fins que és massa tard.

Exercicis

Exercici 1: el bolcat programat

A rutas-norte-dev, munta la cadena completa de còpia lògica:

  1. Crea el PVC copies-postgres de 5 GiB a la classe rutasnorte-rapida.
  2. Adapta el CronJob de l'apartat 4 a dev, amb schedule: "*/10 * * * *" per no esperar una hora.
  3. Llança una execució manual i comprova als registres que el bolcat s'ha creat i verificat.
  4. Esborra la taula reserves i restaura-la a una base de dades nova des del bolcat, sense tocar l'original.
  5. Comprova que les dades coincideixen i explica per què és important restaurar a una base nova.

Exercici 2: simulacre de desastre a rutas-norte-pre

Executa el simulacre complet de l'apartat 11 sobre rutas-norte-pre (crea'l amb els cinc components si no el tens) i redacta l'acta amb: inventari previ, còpia usada, RTO mesurat, RPO efectiu, què no va tornar sol, incidències i accions derivades.

Compara després l'RTO mesurat amb l'objectiu de 8 hores fixat per a pre a l'apartat 3 i raona si la política és adequada o cal canviar-la.

Exercici 3: dissenyar l'estratègia de rutas-norte-pro

El comitè de direcció de Rutas Norte pregunta: "si demà perdem el centre de dades sencer, quant trigarem a tornar a vendre i quantes reserves perdem?". Prepara la resposta en forma de document breu:

  • A. Taula de què es copia, amb quin mètode, freqüència, retenció i de què protegeix cada còpia.
  • B. RPO i RTO compromesos, amb el nombre de reserves perdudes en el pitjor cas (pont, 400 reserves/hora).
  • C. Els tres elements que no es restauren sols i qui és responsable de cadascun.
  • D. Un apartat sobre dades personals que inclogui què contenen les còpies, com es protegeixen i què ha de validar el responsable de compliment normatiu.

Solucions

Exercici 1

# 1, 2 i 3. El PVC i el CronJob de l'apartat 4, amb namespace rutas-norte-dev,
#           5Gi i schedule "*/10 * * * *". Despres, execucio manual:
kubectl apply -f k8s/entorns/dev/copies-postgres-pvc.yaml
kubectl create job --from=cronjob/copia-postgres-reserves \
  copia-prova -n rutas-norte-dev
kubectl wait --for=condition=complete job/copia-prova -n rutas-norte-dev --timeout=300s
kubectl logs job/copia-prova -n rutas-norte-dev
# [2026-08-05T13:10:04+02:00] bolcat correcte: 12K

# 4: el desastre i la restauracio, des d'un pod amb el PVC de copies muntat
kubectl exec -n rutas-norte-dev deploy/postgres-reserves -- \
  psql -U rutasnorte -d reserves -c "DROP TABLE reserves;"

# Dins d'aquest pod auxiliar:
createdb reserves_restaurada
pg_restore -d reserves_restaurada --no-owner /copies/reserves-20260805-131002.dump
psql -d reserves_restaurada -c "SELECT count(*) FROM reserves;"    # -> 5

# 5: promoure la restauracio, ja verificada
psql -d postgres -c "ALTER DATABASE reserves RENAME TO reserves_malmesa;"
psql -d postgres -c "ALTER DATABASE reserves_restaurada RENAME TO reserves;"

Per què a una base nova. Tres raons. Primera, si el bolcat estigués corrupte o incomplet, restaurar a sobre amb --clean hauria destruït el que quedava: et quedaries sense el que està danyat i sense la còpia. Segona, la base danyada és l'evidència per al post-mortem: sense ella no sabràs què va passar ni des de quan. I tercera, permet comparar abans de tallar: recompte de files, data màxima, integritat referencial. La substitució final és un canvi de nom de dos segons, així que l'estalvi de temps de restaurar a sobre és menyspreable davant del risc.

Exercici 2

velero backup create simulacre-pre-$(date +%Y%m%d) \
  --include-namespaces rutas-norte-pre --default-volumes-to-fs-backup --wait

T0=$(date +%s)
kubectl delete namespace rutas-norte-pre
velero restore create rec-$(date +%s) --from-backup simulacre-pre-20260805 --wait
kubectl wait --for=condition=available --timeout=900s deployment --all -n rutas-norte-pre
kubectl exec -n rutas-norte-pre deploy/postgres-reserves -- \
  psql -U rutasnorte -d reserves -c "SELECT count(*), max(data) FROM reserves;"
echo "RTO: $(( ($(date +%s) - T0) / 60 )) minuts"

Acta tipus:

Camp Valor
Data / responsable 2026-08-05 / [email protected]
Còpia usada simulacre-pre-20260805, fs-backup, 18,4 GB
RTO mesurat / RPO efectiu 23 min (esborrat → primer 200 OK verificat) / 47 min
Objectes restaurats 5 Deployments, 5 Services, 2 Ingress, 6 NetworkPolicies, 4 Secrets, 2 PVC
No va tornar sol El certificat TLS (reemès en 4 min per cert-manager); la IP del Service LoadBalancer va canviar; el Secret de l'SMTP es va restaurar amb un token ja rotat
Incidències Els PVC van trigar 6 min a aprovisionar-se i muntar-se: el 40 % de l'RTO
Accions (1) IP estàtica reservada per a l'Ingress; (2) rotació del token SMTP documentada al runbook; (3) avaluar --snapshot-volumes per baixar el temps dels PVC

Valoració de la política: l'RTO objectiu de pre era de 8 hores i el mesurat és de 23 minuts, així que es compleix amb un marge enorme. Però la conclusió útil és una altra: el mateix procediment aplicat a producció no donaria 23 minuts, perquè a pro el volum és deu vegades més gran, cal actualitzar el DNS amb la seva propagació, regenerar secrets externs i verificar el negoci d'extrem a extrem. L'acció correcta és repetir el simulacre sobre una còpia de producció restaurada en un namespace a part, que és el que dona el número real de les 2 hores compromeses.

Exercici 3

A. Què es copia:

Què Mètode Freqüència Retenció Protegeix de
Manifests Git Cada canvi Il·limitada Error de configuració
Dades de postgres-reserves pg_dump -Fc xifrat a PVC Cada hora (15 min en temporada alta) 72 h Error humà recent a les dades
Objectes + volums de rutas-norte-pro Velero + snapshots CSI Diària 02:00 7 dies Esborrat d'objectes, desplegament fallit
Objectes, volums i recursos de clúster Velero + fs-backup a una altra regió, amb --include-cluster-resources Setmanal diumenge 03:00 90 dies Pèrdua de zona, de compte, corrupció antiga; reconstrucció en clúster nou
Volum abans de cada canvi amb risc Snapshot CSI Sota demanda 72 h Migració d'esquema fallida

B. Compromisos:

Escenari RPO Reserves perdudes (pont) RTO
Corrupció de dades detectada ràpid 1 h fins a 400 ~30 min
Namespace destruït 24 h (o 1 h amb el bolcat) fins a 400 ~45 min
Pèrdua del centre de dades 7 dies en el pitjor cas, 24 h en l'habitual fins a 67.200 ~4 h

L'última fila és la resposta a la pregunta del comitè, i és incòmoda a propòsit: la còpia que sobreviu a perdre el centre de dades és la setmanal, de manera que en el pitjor cas es perdrien fins a set dies de reserves. Si això no és acceptable —i no hauria de ser-ho—, hi ha dues inversions que ho arreglen: passar la còpia externa a diària (RPO de 24 h) i, sobretot, arxivar el WAL de PostgreSQL de forma contínua a un magatzem d'objectes extern, que portaria l'RPO a minuts. És la millora prioritària del pla.

C. El que no es restaura sol:

Element Responsable Acció
DNS de www.rutasnorte.example i api.rutasnorte.example Plataforma Actualitzar registres i IP; comptar amb la propagació
Secrets externs: clau de pagos.proveedorexterno.example i token SMTP Seguretat Regenerar des del gestor corporatiu i reaplicar
Infraestructura: nodes, xarxa, tallafocs, StorageClasses, ingress-nginx, cert-manager Plataforma Infraestructura com a codi; crear les StorageClasses abans de restaurar

D. Dades personals:

Les còpies contenen nom, DNI, telèfon i correu de tots els clients que han comprat un bitllet, a més de l'historial complet de reserves. Proteccions aplicades: xifratge en repòs dels bolcats amb clau pública (privada custodiada fora del clúster), xifratge del bucket, xifratge en trànsit, credencials de només escriptura per al procés de còpia, accés a la restauració limitat per RBAC i registrat, retenció automàtica per TTL i anonimització obligatòria de qualsevol clon destinat a entorns no productius.

El que ha de validar el responsable de compliment normatiu, abans de posar res d'això en producció:

  1. Que els terminis de retenció (72 h, 7 dies, 90 dies) són els legalment correctes per a aquesta finalitat, ni més ni menys.
  2. Que la regió del magatzem de còpies és dins de l'àmbit geogràfic permès i declarat.
  3. El procediment davant del dret de supressió: què es fa amb les còpies que ja contenen les dades de qui l'exerceix, i com es documenta.
  4. La base legal del tractament i el seu reflex al registre d'activitats, incloent-hi les còpies com a tractament.
  5. El protocol de notificació de bretxes si una còpia es veu compromesa.
  6. Les condicions de l'encarregat del tractament amb el proveïdor del núvol que allotja les còpies.

Aquest document és una proposta tècnica i no substitueix aquesta revisió.

Conclusió

Rutas Norte ja és una plataforma en què es pot confiar. Saps què cal salvar —els manifests, que ja són a Git; l'estat de l'API, amb els Secrets i els PVC que Git no conté; i les dades dels volums, l'única cosa veritablement irreemplaçable— i que cadascun exigeix una tècnica diferent. Tens interioritzada la regla 3-2-1 i saps que, de totes les còpies de Rutas Norte, només la setmanal al magatzem d'objectes d'una altra regió compleix l'"1"; les altres són comoditat i velocitat. I manegues RPO i RTO amb números concrets: a 400 reserves per hora en un pont, una còpia diària significa perdre fins a 9.600 reserves, i això converteix una discussió tècnica en una decisió de negoci que algú ha de prendre i signar.

Has construït les dues peces. La còpia lògica: un CronJob horari que executa pg_dump -Fc, verifica el bolcat amb pg_restore --list, el xifra amb una clau pública la privada de la qual no viu al clúster, aplica retenció local i —detall que s'oblida sempre— té la seva pròpia NetworkPolicy per travessar el deny-all de producció. I la còpia de la plataforma amb Velero: la seva arquitectura de controlador, node-agent i plugins; les dues vies per als volums, amb la diferència decisiva que només la còpia al magatzem d'objectes treu de veritat les dades del sistema d'emmagatzematge; els hooks pre i post amb pg_backup_start/pg_backup_stop que resolen la consistència que vam deixar oberta a 05-05, i l'advertiment que el post s'ha d'executar sempre so pena d'omplir el disc de WAL; els Schedule amb TTL que fan la retenció automàtica; i la restauració amb --namespace-mappings per verificar abans de tocar res real.

I has fet el que separa una còpia d'una còpia útil: el simulacre. Esborrar rutas-norte-pre sencer i recuperar-lo amb cronòmetre, mesurant un RTO real i descobrint la llista del que no torna sol —el certificat, la IP del LoadBalancer, el DNS, els secrets externs, els recursos de clúster—, que és literalment impossible de conèixer sense executar-lo. D'aquí surt el runbook que algú de guàrdia pot seguir a les tres de la matinada. Al costat de tot això queda l'advertiment que no és tècnic: les còpies contenen dades personals de clients, s'han de xifrar, tenir termini d'esborrat, no sortir de l'àmbit legal previst, i la política de retenció i el tractament d'aquestes dades han de ser revisats i aprovats pel responsable de compliment normatiu abans de portar res d'això a producció.

Amb això acaba el mòdul 5. El deute que arrossegàvem des del mòdul 2 està saldat del tot: postgres-reserves guarda les seves dades en un volum que sobreviu al pod, aprovisionat dinàmicament per una StorageClass amb Retain, expandible en calent, amb snapshots abans de cada canvi arriscat i amb còpies verificades dins i fora del clúster.

Queda un sostre que hem trobat tres vegades i sempre hem ajornat amb la mateixa frase. A 05-03 vas descobrir que un Deployment té una sola plantilla, de manera que totes les seves rèpliques demanen el mateix PVC i per això postgres-reserves està condemnat a replicas: 1 amb strategy: Recreate. Una base de dades de producció necessita més: identitat estable, arrencada ordenada i un volum propi per rèplica. Això ho dona el volumeClaimTemplate dels StatefulSets, i amb ell arrenca el mòdul 6, Conceptes Avançats: StatefulSets, DaemonSets, Jobs i CronJobs —on per fi arriba informes-ocupacio, el sisè component de la plataforma—, init containers i sidecars, planificació amb afinitat i taints, recursos personalitzats i operadors. Comencem per StatefulSets.

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