Les dues lliçons anteriors han treballat sempre sobre una màquina: meteo-01, amb el seu RAID 1, el seu /dev/md0, el seu meteora.conf en mode 600 i el seu sistema de fitxers ext4. Hi hem virtualitzat a dins, hi hem contingut processos a dins, però continuava havent-hi una màquina física en un armari, que algú va instal·lar, que té un número de sèrie i que si s'avaria deixa el servei caigut fins que algú la vagi a arreglar.

Aquesta lliçó treu aquest supòsit. Al núvol, la màquina deixa de ser un objecte i passa a ser una crida a una API: demanes una instància i en quaranta segons la tens; l'esborres i desapareix; es mor sola a les tres de la matinada perquè l'amfitrió físic ha fallat i ningú no t'avisa. Aquest canvi no és només administratiu: canvia el que significa administrar un sistema operatiu, canvia on han de viure les dades, canvia què es pot depurar i canvia com es dissenya un servei perquè sobrevisqui.

Veuràs què canvia exactament i per què, on queda la frontera de responsabilitat del sistema operatiu en cada model de servei, com arrenca de debò una instància i com s'aprovisiona amb cloud-init, què són la infraestructura immutable i els sistemes operatius mínims, com es comporta l'emmagatzematge en xarxa davant de l'SSD local amb xifres concretes, i com un orquestrador de contenidors és literalment un sistema operatiu del clúster on reapareixen els cgroups de la lliçó anterior i el planificador de 02-02. Acabarem migrant Meteora, decisió a decisió, incloent-hi el que no convé migrar. Els sistemes mòbils i de temps real tanquen el mòdul a 06-04, i les eines de monitoratge ja són el mòdul 7.

Contingut

  1. Què canvia quan la màquina deixa de ser física
  2. Bestiar i no mascotes
  3. Models de servei i la frontera del sistema operatiu
  4. L'arrencada d'una instància, pas a pas
  5. cloud-init: aprovisionar meteo-01 al núvol
  6. Metadades d'instància i el seu risc de seguretat
  7. Infraestructura immutable davant de mutable
  8. Sistemes operatius mínims i immutables
  9. Emmagatzematge al núvol: blocs, efímer i objectes
  10. Xarxes definides per programari
  11. Orquestració: el sistema operatiu del clúster
  12. Funcions sense servidor, microVM i sandboxes
  13. Escalat automàtic i l'arrencada en fred
  14. Observabilitat quan la màquina desapareix
  15. Cost i densitat com a criteri de disseny
  16. Cas final: migrar Meteora

Què canvia quan la màquina deixa de ser física

Enumerem els canvis sense adorns, perquè cadascun té conseqüències tècniques concretes:

Propietat Servidor físic (meteo-01) Instància al núvol
Aprovisionament Setmanes: compra, muntatge, instal·lació Segons, per API
Vida esperada 3-5 anys Hores o dies; pot desaparèixer sense avís
Identitat Nom propi, número de sèrie, inventari Un identificador generat, llencívol
Fallada del maquinari Incident: algú hi va i ho arregla Rutina: se substitueix la instància
Disc Físic, dins de la caixa Un volum de xarxa, lligat i deslligat per API
Xarxa Cables, VLAN, commutador físic Definida per programari, creada per API
Escala Fixa Elàstica, amb cost per hora o per segon
Cost Inversió inicial (CAPEX) Despesa corrent (OPEX), proporcional a l'ús

El que és veritablement disruptiu és la fila de la vida esperada. Un servidor físic es dissenya per durar; una instància de núvol es dissenya assumint que es morirà. Els proveïdors ho diuen obertament: el compromís de disponibilitat d'una instància individual sol ser del 99,5 % mensual —unes 3,6 hores de caiguda al mes— i només s'eleven les garanties quan reparteixes la càrrega entre diverses zones.

D'aquí surt la primera regla de disseny d'aquesta lliçó, que contradiu tot el que un aprèn administrant un servidor: no intentis que la màquina no falli; dissenya perquè la seva fallada no importi.

Bestiar i no mascotes

La metàfora és del 2012 i continua sent la millor explicació del canvi cultural:

  • Una mascota té nom propi (meteo-01), s'instal·la a mà, se li fa un seguiment afectuós, se li apliquen pedaços amb cura i, quan emmalalteix, se la cura. És única i irreemplaçable.
  • El bestiar es numera (meteo-api-7f3c), s'aprovisiona automàticament i és intercanviable. Quan un emmalalteix, no es cura: se substitueix.

Les conseqüències pràctiques són incòmodes al principi i alliberadores després:

  • Res de canvis manuals. Un apt install a mà en una instància crea un estat que cap altra no té i que es perdrà en substituir-la. Si el canvi importa, va a la imatge o a l'automatització.
  • Cap dada important no viu al disc de la instància. Tot el que ha de sobreviure surt d'allà: a un volum persistent, a una base de dades gestionada, a emmagatzematge d'objectes.
  • L'accés per SSH deixa de ser l'eina principal. No pas perquè estigui prohibit, sinó perquè si el necessites per operar, alguna cosa falta a l'automatització.
  • La reinstal·lació és la resposta per defecte. El que a 05-04 era el desenllaç d'un incident greu —reconstruir des de zero— aquí és l'operació de dimarts al matí.

Un matís honest, perquè el dogma s'exagera: no tot pot ser bestiar. Una base de dades amb estat, un controlador de domini o un sistema amb llicències lligades al maquinari continuen sent mascotes, i pretendre el contrari provoca desastres. La regla útil és que l'estat es concentri en les mínimes peces possibles, i que tota la resta sigui intercanviable.

Models de servei i la frontera del sistema operatiu

La pregunta rellevant per a aquest curs no és "què és el núvol", sinó qui administra el sistema operatiu en cada model:

Model Tu gestiones El proveïdor gestiona Veus el SO? Exemple
On-premise Tot Res Sí, sencer meteo-01 a l'armari
IaaS SO, pedaços, temps d'execució, aplicació Maquinari, hipervisor, xarxa física Sí, sencer Una instància de còmput
CaaS Imatge del contenidor i la seva configuració A més, el SO dels nodes Parcialment (la imatge) Un servei de contenidors gestionat
PaaS Només el codi i la configuració A més, el temps d'execució No Una plataforma d'aplicacions
FaaS Només la funció Tota la resta, inclòs l'escalat No Funcions sense servidor

L'important és entendre què es guanya i què es perd en pujar per aquesta escala. Es guanya operació: menys pedaços per aplicar, menys configuració per mantenir, menys guàrdies. Es perd control i, molt en concret, es perden les eines d'aquest curs: en un PaaS no pots mirar /proc, no pots ajustar swappiness, no pots posar una regla d'nftables, no pots executar strace. Quan alguna cosa va malament i el proveïdor no et dona la mètrica que necessites, estàs cec.

I un advertiment que no se sol dir: a IaaS continues sent responsable de tot el sistema operatiu. És el model de responsabilitat compartida. El proveïdor garanteix que el maquinari i l'hipervisor funcionen; els pedaços del nucli, l'enfortiment de 05-03, el tallafocs, els usuaris i els registres continuen sent teus. Una instància acabada de crear amb la imatge oficial d'una distribució és exactament igual d'insegura que un servidor acabat d'instal·lar.

L'arrencada d'una instància, pas a pas

Convé veure la seqüència completa, perquè explica on encaixa cada peça:

sequenceDiagram
    participant U as Tu (API/terraform)
    participant P as Pla de control
    participant H as Amfitrió físic
    participant I as Instància
    U->>P: crear_instancia(imatge, tipus, xarxa, user-data)
    P->>P: triar amfitrió amb forat (planificació)
    P->>H: crear VM, adjuntar volum des de la imatge
    H->>I: arrencar (microprogramari → nucli → init)
    I->>I: cloud-init: consulta al servei de metadades
    I->>I: aplica user-data: usuaris, claus, paquets, fitxers
    I->>P: instància a punt (~30-60 s)

Les peces, una a una:

  1. La imatge de màquina. Una plantilla de disc amb un sistema ja instal·lat —la mateixa idea de les plantilles de 06-01—, identificada per un ID. Pot ser oficial del proveïdor, de la distribució, o pròpia, construïda per tu amb tot el que necessites ja a dins. Aquesta darrera opció és la base de la infraestructura immutable.
  2. El tipus d'instància. Determina vCPU, memòria, xarxa i de vegades disc local. Aquí recuperes tot 06-01: aquestes vCPU són fils d'un procés en un amfitrió compartit, i el seu steal time és real.
  3. El volum arrel. Es crea copiant (o enllaçant) la imatge. En acabar, normalment s'esborra amb la instància, llevat que es marqui el contrari.
  4. La xarxa: una interfície virtual a la xarxa definida per programari, amb IP privada, i un grup de seguretat associat.
  5. L'arrencada: microprogramari, gestor d'arrencada, nucli, init. Exactament el que passa en qualsevol màquina; ho veuràs en detall a Serveis, Arrencada i systemd.
  6. cloud-init: la peça que converteix una imatge genèrica en el teu servidor.

cloud-init: aprovisionar meteo-01 al núvol

cloud-init és un conjunt de serveis que s'executen al primer arrencada, consulten el servei de metadades i apliquen la configuració que li has passat, anomenada user-data. És un estàndard de facto: funciona igual en gairebé tots els proveïdors i també a KVM local, cosa que el fa l'eina natural per a la rèplica de proves que vam crear a 06-01.

#cloud-config
hostname: meteo-01
fqdn: meteo-01.meteora.internal
timezone: Europe/Madrid

# --- Usuaris: compte d'operació amb clau pública, sense contrasenya ---
users:
  - name: operador
    groups: [sudo]
    shell: /bin/bash
    sudo: ["ALL=(ALL) NOPASSWD:/usr/bin/systemctl restart meteo-api"]
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... operador@meteora
  # Compte de servei: MATEIX UID que al servidor físic
  - name: meteora
    uid: 990
    gid: 990
    system: true
    shell: /usr/sbin/nologin
    lock_passwd: true

# --- Paquets ---
package_update: true
package_upgrade: true
packages: [nftables, chrony, python3-venv, unattended-upgrades]

# --- Disc de dades: formatar i muntar amb les opcions del curs ---
disk_setup:
  /dev/nvme1n1: {table_type: gpt, layout: true, overwrite: false}
fs_setup:
  - device: /dev/nvme1n1
    partition: 1
    filesystem: ext4
    label: meteora-dades
mounts:
  - [LABEL=meteora-dades, /var/lib/meteora, ext4,
     "defaults,noatime,nosuid,nodev,data=ordered", "0", "2"]

# --- Fitxers: configuració SENSE secrets ---
write_files:
  - path: /etc/meteora/meteora.conf
    owner: meteora:meteora
    permissions: '0600'
    content: |
      [general]
      dades = /var/lib/meteora/lectures
      log   = /var/log/meteora/meteo-api.log
      # Els secrets NO van aquí: es llegeixen del gestor de secrets en arrencar
  - path: /etc/nftables.conf
    permissions: '0644'
    content: |
      table inet filtre {
        chain entrada {
          type filter hook input priority 0; policy drop;
          ct state established,related accept
          iif lo accept
          tcp dport 443 accept
        }
      }

# --- Ordres finals, en ordre ---
runcmd:
  - [install, -d, -o, meteora, -g, meteora, -m, '0750', /var/lib/meteora/lectures]
  - [install, -d, -o, meteora, -g, meteora, -m, '0750', /var/log/meteora]
  - [systemctl, enable, --now, nftables]
  - [systemctl, enable, --now, meteo-api]

final_message: "meteo-01 a punt després de $UPTIME segons"

Què fa i per què cada decisió:

  • #cloud-config a la primera línia és obligatori: és la signatura que diu a cloud-init que interpreti el YAML. Sense ella el fitxer s'ignora en silenci, i és l'error número u.
  • Usuaris amb clau pública i sense contrasenya. lock_passwd: true per al compte de servei i ssh_authorized_keys per al d'operació apliquen directament 05-02. El sudo està acotat a una ordre concreta, no pas a ALL.
  • uid: 990 explícit, per la mateixa raó que al Dockerfile de 06-02: els volums es comparteixen per número, no per nom. Si cloud-init assigna l'UID 999 i el volum té fitxers del 990, obtens permisos trencats.
  • mounts amb noatime,nosuid,nodev: les opcions d'enfortiment de 04-03 i 05-03, declarades a l'aprovisionament en lloc d'editades a mà a /etc/fstab. Aquí hi ha el canvi cultural complet: la configuració es declara, no s'aplica.
  • Configuració sense secrets. Aquest és el punt crític: el user-data és llegible des de dins de la instància per qualsevol procés, com veurem ara mateix. Ficar-hi una contrasenya equival a publicar-la. Els secrets es llegeixen a l'arrencada d'un gestor de secrets, autenticant-se amb la identitat de la instància.
  • runcmd al final, en ordre, per al que no cobreixen els mòduls declaratius.

Metadades d'instància i el seu risc de seguretat

Tota instància pot consultar informació sobre ella mateixa en una adreça local especial, típicament 169.254.169.254:

curl http://169.254.169.254/latest/meta-data/instance-id
curl http://169.254.169.254/latest/meta-data/local-ipv4
curl http://169.254.169.254/latest/user-data          # el teu cloud-config sencer!
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/rol-meteo
# {"AccessKeyId":"...","SecretAccessKey":"...","Token":"...","Expiration":"..."}

Què acabes de veure, i per què és el risc de seguretat més gran específic del núvol. Aquest endpoint retorna, sense cap autenticació, les credencials temporals del rol assignat a la instància. Qualsevol procés de la màquina les pot demanar i actuar al núvol amb aquests permisos. I aquí hi ha el problema: si la teva aplicació té una vulnerabilitat de falsificació de peticions del costat del servidor (SSRF) —una cosa tan ximple com un paràmetre ?url= que l'aplicació descarrega—, un atacant pot demanar a la teva pròpia aplicació que consulti 169.254.169.254 i li retorni les credencials. No necessita executar codi: n'hi ha prou que el teu servidor faci una petició HTTP per ell.

És exactament el patró de la bretxa de Capital One del 2019, que va exposar dades de més de cent milions de persones. Les mitigacions són concretes i totes necessàries:

  • Exigir la versió amb token del servei de metadades (IMDSv2 i equivalents), que obliga a un PUT previ per obtenir un token de sessió amb TTL baix: un SSRF simple, que només sap fer GET, deixa de funcionar.
  • Bloquejar l'accés a l'endpoint des dels processos que no el necessiten, amb nftables o el mateix contenidor.
  • Rol amb permisos mínims: si meteo-api només ha d'escriure en un bucket concret, que el rol no permeti res més. És el mínim privilegi de 05-01 aplicat al pla del núvol.
  • Mai posar secrets al user-data, perquè és llegible des d'allà.

Infraestructura immutable davant de mutable

Dues filosofies oposades de gestionar el cicle de vida d'un servidor:

Mutable Immutable
Actualitzar Es modifica el servidor existent (apt upgrade) Es construeix una imatge nova i es reemplacen les instàncies
Eines Gestió de configuració contínua Construcció d'imatges + desplegament
Reversió Difícil: cal desfer canvis Trivial: redesplegar la imatge anterior
Deriva de configuració Inevitable amb el temps Impossible per construcció
Reproductibilitat Depèn de l'historial de cada màquina Total: la imatge és la mateixa per a tothom
Depuració Hi entres i mires Complicat: la instància dolenta ja no hi és
Temps de desplegament Minuts Més llarg (construir + arrencar)

La deriva de configuració és l'argument decisiu. En un parc de vint servidors mutables gestionats durant dos anys, cap màquina no és idèntica a una altra: un apt install d'urgència aquí, un fitxer editat a mà allà, un paquet que va fallar en actualitzar-se en dues d'elles. El resultat és que una fallada apareix només en tres servidors i ningú no sap per què, i que la frase "al meu funciona" es converteix en la resposta habitual.

Amb infraestructura immutable, totes les instàncies de la versió 1.4.0 són bit a bit idèntiques, de manera que una fallada o és a totes o no és a cap. I la reversió és redesplegar la 1.3.0, cosa que converteix un desplegament arriscat en una operació avorrida.

El que es perd, i és real: depurar es complica. Si una instància es comporta malament i la política és substituir-la, el problema desapareix amb ella i no n'aprens res. La pràctica correcta és treure la instància malalta del balancejador però no destruir-la, deixant-la en quarantena per investigar-la —cosa que enllaça directament amb la preservació d'evidències de 05-04—, i substituir-la per una altra per restablir el servei. Contenir i analitzar no són incompatibles: són fases diferents.

Sistemes operatius mínims i immutables

Si la instància és bestiar i només executa contenidors, per què vol un sistema operatiu complet amb compilador, gestor de paquets, servidor de correu i cinquanta utilitats?

Sistema Idea Actualització Gestor de paquets
Flatcar / Container Linux SO mínim només per a contenidors Dues particions arrel A/B, atòmica No n'hi ha
Bottlerocket SO de contenidors, arrel de només lectura, verificada Imatge A/B amb reversió No n'hi ha
Talos Linux Només Kubernetes: sense shell ni SSH, API gRPC Declarativa per API No n'hi ha
Fedora CoreOS Sistema base atòmic amb rpm-ostree Transaccional, reversible Limitat
"Distroless" (imatges) Només el temps d'execució i l'aplicació Reconstruir la imatge No n'hi ha

El que es guanya és mesurable i substancial:

  • Superfície d'atac diminuta. Sense shell no hi ha reverse shell; sense curl ni wget no es descarrega la segona fase d'un atac; sense compilador no es compila un exploit local. És el principi de superfície mínima de 05-01 portat a l'extrem, i elimina classes senceres d'atac, no les dificulta.
  • Menys pedaços. Un sistema amb 80 paquets té menys CVE al mes que un amb 500. Menys soroll, menys guàrdies.
  • Actualitzacions atòmiques i reversibles. L'esquema A/B escriu la imatge nova a la partició inactiva i només canvia l'arrencada al final: o s'actualitza sencera o no s'actualitza, i si no arrenca bé, torna sola a l'anterior. És el contrari de l'apt upgrade que es queda a mitges.
  • Arrencada ràpida, perquè hi ha poquíssims serveis per iniciar.

El que es perd és igual de real: depurar és incòmode. Sense strace, sense tcpdump, sense ss i de vegades sense shell, les tècniques del mòdul 7 no es poden aplicar directament. La resposta de l'ecosistema són els contenidors de depuració efímers: es llança un contenidor amb les eines dins dels namespaces del procés problemàtic —el nsenter de 06-02, industrialitzat—, s'investiga i es descarta. Funciona bé, però cal saber-ho fer abans que passi l'incident, no durant.

Emmagatzematge al núvol: blocs, efímer i objectes

Aquí és on el mòdul 2 es torna molt visible, i on més errors de disseny es cometen.

Tipus Què és Latència típica Persistència Semàntica
Efímer d'instància NVMe físic de l'amfitrió 20-100 µs Es perd en aturar la instància Sistema de fitxers normal
Volum de blocs en xarxa Disc virtual accedit per xarxa 0,3-1 ms Sobreviu a la instància Sistema de fitxers normal
Emmagatzematge d'objectes Magatzem clau-valor per HTTP 20-100 ms Molt alta (11 nous) No és un sistema de fitxers

Volums de blocs en xarxa

Es comporten com un disc: es particionen, es formaten amb ext4 i es munten. Però són a l'altre costat d'una xarxa, i això es nota exactament on prediu 02-05:

Mètrica SSD NVMe local Volum de blocs en xarxa
Latència de lectura 4 KB 20-100 µs 300-1.000 µs (5-10× pitjor)
IOPS 500.000+ 3.000-64.000 (segons el que paguis)
Throughput 3-7 GB/s 125-1.000 MB/s
Persistència No
Instantànies No Sí, integrades

La diferència clau no és només el número: és que les IOPS s'aprovisionen i es paguen. Un volum bàsic dona sovint 3 IOPS per GB, de manera que un volum de 100 GB rendeix 300 IOPS —menys que un disc dur giratori del 2005— per molt que el maquinari subjacent sigui flaix. És la font de sorpresa més habitual en migrar: la mateixa aplicació, en la mateixa versió, va quatre vegades més lenta, i el motiu no és la CPU sinó un sostre d'IOPS contractat.

Per a Meteora, l'ingestor escriu 8.000 lectures per segon. Si les escriu d'una en una, són 8.000 IOPS i el volum bàsic s'ofega; si les agrupa en lots de 512 lectures (12.288 bytes), baixen a unes 16 escriptures per segon. L'agrupació, que al servidor físic era una optimització menor, aquí és la diferència entre funcionar i no funcionar.

Emmagatzematge efímer

És un NVMe físic de l'amfitrió: rapidíssim i, sovint, inclòs al preu de la instància. Però es perd en aturar la instància o si l'amfitrió falla. El seu ús correcte és tot allò que es pot reconstruir: memòries cau, fitxers temporals, índexs derivats, dades intermèdies d'un procés per lots. Mai la font de la veritat.

Emmagatzematge d'objectes

És la peça més incompresa, perquè no és un sistema de fitxers encara que la seva interfície ho sembli:

Aspecte Sistema de fitxers Emmagatzematge d'objectes
Unitat Fitxer amb inode (04-01) Objecte complet, amb una clau
Modificació parcial Sí, pwrite en un desplaçament No: se substitueix l'objecte sencer
Jerarquia Directoris reals Plana; la / de la clau és decorativa
Reanomenar Canvi d'entrada de directori, instantani Copiar i esborrar: proporcional a la mida
Llistar readdir sobre un directori Consulta paginada sobre un prefix, lenta
Bloqueig, fsync, permisos POSIX No
Latència Microsegons Desenes de mil·lisegons

Per això muntar-lo com si fos un disc és gairebé sempre mala idea: les eines que tradueixen POSIX a objectes han d'emular el que no existeix, i un mv d'un fitxer gran es converteix a copiar tot l'objecte i esborrar-lo, un readdir es converteix en diverses peticions HTTP paginades, i no hi ha bloqueig ni escriptura parcial. Funciona en demostracions i es trenca en producció amb dades reals.

Aplicat a Meteora, la decisió és neta:

Dada On ha de viure Per què
Fitxer del dia en curs (2026-08-31.dat) Volum de blocs S'escriu contínuament, amb append i fsync
Fitxers de dies tancats Objectes (comprimits) Només es llegeixen sencers, i surten molt més barats
Índexs i agregats horaris Volum de blocs, o base de dades gestionada Accés aleatori i consultes
Memòria cau /dev/shm/meteora-cache Memòria de la instància Reconstruïble
Còpies de seguretat Objectes, amb versionat i immutabilitat És la còpia 3-2-1 de 05-03, ara amb retenció forçada pel proveïdor

Un detall que arrodoneix la decisió: l'emmagatzematge d'objectes ofereix classes d'accés amb preus molt diferents. Un fitxer diari de 17,3 MB comprimit queda en uns 4 MB; al llarg de cinc anys són uns 7,3 GB, amb un cost d'emmagatzematge fred ridícul. Mantenir aquestes mateixes dades en volums de blocs costa un ordre de magnitud més i no aporta res, perquè ningú no consulta el 14 de març del 2023 amb latència de mil·lisegons.

Xarxes definides per programari

La xarxa del núvol és una simulació construïda sobre la xarxa física del proveïdor, i les seves peces es corresponen una a una amb el que ja coneixes:

  • Xarxa virtual privada: un espai d'adreces propi (per exemple 10.0.0.0/16) aïllat del d'altres clients, subdividit en subxarxes per zona de disponibilitat. Conceptualment és el pont de 06-01, a escala de centre de dades.
  • Subxarxes públiques i privades: la diferència real és si la seva taula de rutes té sortida a una passarel·la d'internet. Les bases de dades i els serveis interns van en privades; només el balancejador viu a la pública.
  • Grups de seguretat: un tallafocs amb estat, aplicat per interfície de xarxa, no per màquina. És la diferència amb nftables: la regla viatja amb la instància, s'avalua abans que el paquet arribi al sistema operatiu, i per defecte denega tot el que entra.
  • Adreces: la IP privada és estable durant la vida de la instància; la pública, si no es reserva, canvia a cada arrencada, motiu pel qual mai no s'ha de configurar res per IP pública.

Dos consells que eviten la majoria dels incidents de xarxa al núvol:

  • El grup de seguretat no substitueix el tallafocs del sistema. És defensa en profunditat, com a 05-03: si algú s'equivoca a la regla del grup, nftables amb policy drop continua allà.
  • Referencia grups, no rangs d'IP. La regla correcta per a la base de dades no és "permetre 10.0.1.0/24", sinó "permetre el trànsit que vingui del grup de seguretat de meteo-api". Així la regla continua sent correcta quan l'autoescalat crea instàncies amb IP noves.

Orquestració: el sistema operatiu del clúster

Aquí hi ha la idea que fa que aquesta lliçó pertanyi de ple dret a un curs de sistemes operatius. Un orquestrador de contenidors fa amb un clúster exactament el que un sistema operatiu fa amb una màquina:

Funció Sistema operatiu (mòdul 2) Orquestrador
Unitat d'execució Procés Pod (un o diversos contenidors)
Recurs a repartir CPU, RAM de la màquina CPU, RAM de tots els nodes
Planificador CFS-EEVDF tria quin procés s'executa Tria a quin node es col·loca el pod
Aïllament Namespaces i cgroups Els mateixos, més polítiques de xarxa
Noms /proc, sockets, fitxers Serveis i DNS intern
Reinici davant d'una fallada Restart= de systemd Controlador de rèpliques
Emmagatzematge VFS i muntatges (04-03) Volums persistents

El component que tanca el cercle amb la lliçó anterior és el kubelet, l'agent que corre a cada node. La seva feina és senzilla de descriure: pregunta al pla de control quins pods li toquen, parla amb el runtime de contenidors (containerd o CRI-O, via la interfície CRI) per arrencar-los, munta els seus volums, executa les seves sondes de salut i reporta l'estat. És, literalment, l'init del node per a les càrregues del clúster.

Peticions i límits són cgroups, literalment

Quan escrius això en un manifest:

resources:
  requests:            # el que el planificador RESERVA per col·locar-te
    cpu: "500m"        # 0,5 nuclis
    memory: "256Mi"
  limits:              # el SOSTRE que no pots passar
    cpu: "1500m"       # 1,5 nuclis
    memory: "512Mi"

El kubelet ho tradueix exactament als fitxers que vam escriure a mà a 06-02:

Camp del manifest Fitxer de cgroup v2 Valor
requests.cpu: 500m cpu.weight ~51 (pes proporcional)
limits.cpu: 1500m cpu.max 150000 100000
requests.memory: 256Mi (només planificació) No s'escriu: es fa servir per triar node
limits.memory: 512Mi memory.max 536870912

I d'aquí es dedueixen, sense cap màgia, els dos comportaments que més desconcerten qui comença:

  • Superar el límit de CPU no mata: estrangula. És el throttling de cpu.max que vam diagnosticar a 06-02 amb nr_throttled. L'aplicació no falla, només va a batzegades i amb un p99 horrible.
  • Superar el límit de memòria mata a l'instant. És l'OOM killer del cgroup: el contenidor mor amb codi 137 i el pod apareix com a OOMKilled. Exactament el cas de l'agregador de l'exercici anterior.

El planificador del clúster és l'equivalent del planificador de CPU de 02-02, un nivell més amunt: filtra els nodes que no serveixen (sense prou recursos lliures segons les requests, sense les etiquetes demanades, amb restriccions actives) i puntua els que queden per repartir la càrrega. I aquí una regla operativa que s'aprèn cara: el planificador col·loca segons les peticions, no segons l'ús real. Si demanes 2 CPU i n'uses 0,1, el clúster te'n reserva 2 i no col·loca ningú més en aquell forat. Peticions inflades són la causa número u de clústers caríssims amb nodes al 15 % d'ús.

Funcions sense servidor, microVM i sandboxes

Les funcions sense servidor porten l'escala a l'extrem: puges una funció, el proveïdor l'executa quan arriba un esdeveniment i et cobra per mil·lisegon. No hi ha servidor per administrar, no hi ha sistema operatiu per pedaçar.

Però per sota sí que hi ha alguna cosa, i aquesta cosa és interessant per a aquest curs, perquè planteja un problema difícil: cal executar codi de milers de clients diferents al mateix maquinari, amb aïllament de màquina virtual però arrencada de contenidor. Les màquines virtuals de 06-01 aïllen bé però triguen desenes de segons; els contenidors de 06-02 arrenquen en mil·lisegons però comparteixen nucli. Dues tecnologies ocupen aquest espai intermedi:

  • Firecracker és un VMM minimalista escrit en Rust sobre KVM. Renuncia a gairebé tot el que QEMU emula: sense BIOS heretada, sense USB, sense VGA, sense PCI complet; només virtio per a disc, xarxa i consola. El resultat és una microVM que arrenca en ~125 ms, consumeix uns 5 MB de sobrecàrrega i exposa a l'hipervisor una superfície diminuta. És l'hipervisor real darrere de les plataformes de funcions sense servidor, i és la demostració pràctica de la lliçó de VENOM a 06-01: treure dispositius emulats millora alhora la seguretat i el rendiment.
  • gVisor ataca el problema des de l'altre costat: és un nucli en espai d'usuari que intercepta les crides al sistema del contenidor i les implementa ell mateix, en Go, en lloc de deixar-les passar al nucli amfitrió. Redueix dràsticament la superfície —el contenidor ja no parla amb les 350 syscalls de Linux, sinó amb gVisor—, a canvi d'una penalització de rendiment notable en càrregues amb molta E/S.
Tecnologia Aïllament Arrencada en fred Sobrecàrrega de memòria Compatibilitat
Contenidor (runc) Nucli compartit 20-200 ms 1-10 MB Total
gVisor Nucli interceptat en espai d'usuari 100-300 ms 15-50 MB Alta, amb forats
Firecracker (microVM) Nucli propi, VM real ~125 ms ~5 MB + el convidat Total
VM completa (QEMU) Nucli propi, VM real 10-60 s 200-500 MB Total

La conclusió que convé retenir: la frontera entre VM i contenidor ha deixat de ser binària. Firecracker aconsegueix aïllament de VM amb arrencada gairebé de contenidor retallant tot el que no cal, i és la raó tècnica que les funcions sense servidor puguin ser alhora multiinquilí i barates.

Escalat automàtic i l'arrencada en fred

L'escalat automàtic ajusta el nombre d'instàncies o de pods segons una mètrica: ús de CPU, longitud d'una cua, peticions per segon. Sona trivial i té dos problemes reals.

El primer és l'arrencada en fred. Escalar no és instantani, i la latència s'acumula per capes:

Capa Temps típic fins a servir trànsit
Pod nou, imatge ja al node 1-5 s
Pod nou, imatge per descarregar 10-60 s
Instància nova (IaaS) + arrencada + cloud-init 40-120 s
Funció sense servidor (microVM) en fred 100-500 ms
Funció en calent 1-10 ms

Si la teva càrrega es duplica en 30 segons i una instància nova en triga 90 a estar a punt, l'escalat automàtic arriba tard i els usuaris veuen errors. Les respostes són escalar per una mètrica avançada (la longitud de la cua de l'ingestor creix abans que la CPU se saturi), mantenir un coixí de capacitat ociosa, precarregar imatges als nodes, i escalar de manera agressiva en pujar i conservadora en baixar per no oscil·lar.

El segon problema és l'oscil·lació: si la mètrica puja i baixa al voltant del llindar, el sistema crea i destrueix instàncies sense parar, amb un cost alt i cap estabilitat. Es resol amb histèresi —llindars diferents per pujar i baixar— i amb períodes de refredament.

I un matís important per a Meteora: l'ingestor no escala igual que meteo-api. Les consultes HTTP són sense estat i es reparteixen a voluntat; la ingesta d'estacions concretes té afinitat i ordre. Escalar horitzontalment alguna cosa amb estat sense pensar-hi porta a lectures duplicades o perdudes.

Observabilitat quan la màquina desapareix

Aquí es veu amb cruesa per què 05-04 insistia tant a treure els registres de la màquina. Al núvol, aquesta recomanació passa de bona pràctica a requisit absolut, per una raó nova: la instància que va generar el registre pot no existir quan el vagis a llegir.

  • Un pod que mor per OOM es reinicia i els seus registres anteriors desapareixen llevat que s'hagin enviat fora.
  • Una instància substituïda per l'autoescalat s'endú el seu journalctl amb ella.
  • El disc efímer, amb el que hi hagués a /var/log, s'esborra en aturar la instància.

D'aquí les regles pràctiques:

  • L'aplicació escriu a la sortida estàndard, no pas a un fitxer. Un agent al node recull, etiqueta i envia. És el contrari de /var/log/meteora/meteo-api.log, i en un contenidor té tot el sentit: qui s'ha de preocupar d'on acaba el registre no és l'aplicació.
  • Registre estructurat amb context del núvol: al request_id de 05-04 s'hi afegeixen ara identificador d'instància, zona, versió de la imatge i nom del pod. Sense això, un error que passa en una sola instància de vint és indistingible del soroll.
  • Traces distribuïdes, perquè una petició travessa balancejador, diversos serveis i una base de dades: un registre per servei ja no reconstrueix la història.
  • Mètriques per agregat, no per màquina. "La CPU de meteo-01" deixa de significar res quan hi ha quinze instàncies efímeres; el que importa és el percentil i la distribució.

Què es perd en depurar? Força, i convé dir-ho: no pots entrar a mirar /proc en una instància que ja no existeix; no pots llançar strace sobre un procés que va morir fa deu minuts; no pots reproduir l'estat exacte d'un disc efímer esborrat. La compensació és que pots conservar la instància malalta en quarantena en lloc de matar-la, que és la tècnica que cal tenir escrita al procediment abans de necessitar-la.

Cost i densitat com a criteri de disseny

En un servidor físic, el cost és una inversió feta fa dos anys i no influeix en el disseny diari. Al núvol, cada decisió tècnica té una factura mensual, i això converteix el cost en un criteri d'arquitectura de ple dret.

Algunes relacions que convé interioritzar:

  • Sobredimensionar surt car i és invisible. Peticions inflades en un manifest no produeixen cap error: produeixen nodes al 15 % d'ús i una factura triple. Mesurar l'ús real i ajustar és una tasca d'enginyeria, no de comptabilitat.
  • La transferència de dades es paga, i sobretot la de sortida. Moure els fitxers diaris entre zones o cap a internet pot costar més que emmagatzemar-los. Col·locar el còmput al costat de les dades deixa de ser una qüestió de latència i passa a ser de diners.
  • Les classes d'emmagatzematge importen molt. Ja ho vam veure: els dies tancats en emmagatzematge fred costen una fracció del que costen en volums de blocs.
  • Les instàncies interrompibles (spot) costen entre un 60 % i un 90 % menys a canvi que te les puguin prendre amb un avís de dos minuts. Per a l'agregador, que és una tasca per lots reprenible, són ideals. Per a meteo-api, no.
  • La densitat és estalvi. Cada contenidor que hi cap de més en un node és un node menys per pagar; aquí és on el cost base d'1-10 MB d'un contenidor davant dels 200-500 MB d'una VM es converteix en euros.

Cas final: migrar Meteora

Decisió a decisió, amb la justificació explícita:

Peça Decisió Per què
meteo-api Contenidor en un servei gestionat, 3 rèpliques en 2 zones, autoescalat per peticions/s, darrere d'un balancejador amb TLS Sense estat, és el cas ideal de "bestiar"; amb 3 rèpliques se sobreviu a la pèrdua d'una zona
ingestor Contenidor amb estat, rèpliques fixes amb afinitat per estació, cua gestionada al davant Té ordre i afinitat: escalar-lo a cegues duplicaria o perdria lectures. La cua absorbeix els pics i desacobla
agregador Tasca programada en instàncies interrompibles, amb reintent Per lots, reprenible i tolerant a interrupció: estalvi del 70-90 % sense risc
Fitxer del dia Volum de blocs amb IOPS aprovisionades, escriptura en lots de 512 lectures Redueix de 8.000 a ~16 escriptures/s: la diferència entre funcionar i ofegar-se
Dies tancats Objectes comprimits, amb transició automàtica a classe freda als 30 dies Només es llegeixen sencers; el cost baixa un ordre de magnitud
Còpies de seguretat Objectes en una altra regió, amb versionat i immutabilitat És la 3-2-1 de 05-03; la retenció forçada pel proveïdor és l'única defensa real contra el segrest de dades
meteora.conf Gestor de secrets, llegit en arrencar amb la identitat de la instància Mai a la imatge ni al user-data, pel que hem vist a l'apartat 6
SO dels nodes Distribució mínima i immutable, actualització A/B Superfície mínima i actualitzacions reversibles; ningú no entra per SSH a un node
Xarxa Subxarxa privada per a tot; només el balancejador a la pública; grups de seguretat que es referencien entre si Mínima exposició, i regles que sobreviuen a l'autoescalat
Registres i mètriques Sortida estàndard → agent → col·lector extern, amb context de núvol La instància pot no existir quan la vagis a llegir
Base de dades d'agregats Servei gestionat amb rèplica en una altra zona És la mascota del sistema: millor que la cuidi qui té guàrdies 24×7

I el que no convé migrar, que és igual d'important:

  • L'emmagatzematge històric complet, tal com està. Moure cinc anys de fitxers diaris a volums de blocs seria caríssim i absurd: van a objectes, comprimits i en classe freda. Migrar "igual que estava" és l'error clàssic.
  • La memòria cau a /dev/shm/meteora-cache com a peça compartida. En una màquina tenia sentit; amb rèpliques en diverses zones, una memòria cau local per instància és incoherent. O s'accepta que cada rèplica tingui la seva (si és reconstruïble i tolerant a estar desfasada), o es fa servir una memòria cau en xarxa.
  • El cron i els scripts de manteniment fets a mà. No sobreviuen a instàncies efímeres: passen a tasques programades de l'orquestrador.
  • La FIFO /run/meteora/lectures.fifo entre serveis. És IPC local (03-03) i només funciona dins d'una màquina; entre instàncies cal una cua de debò, amb persistència i reintents.
  • Les dades regulades fora de la seva jurisdicció. Si hi ha obligacions de residència de dades, la regió no és una decisió tècnica sinó legal, amb les implicacions de 05-04.
  • I potser res, si el cas no ho justifica. Un servei estable, amb càrrega previsible i sense necessitat d'elasticitat pot sortir més car i més fràgil al núvol. El núvol es paga amb diners i amb complexitat; tots dos s'han de justificar.

Errors Habituals i Consells

Tractar les instàncies com a mascotes. Instal·lar a mà, configurar per SSH i confiar que la màquina continuï allà. La primera substitució automàtica s'endú per davant tots aquests canvis i ningú no els sap reconstruir.

Ficar secrets al user-data. És llegible des de dins de la instància per qualsevol procés a través del servei de metadades. Un secret allà és un secret publicat.

No protegir el servei de metadades. Un SSRF a l'aplicació es converteix en robatori de les credencials del rol de la instància; és el patró exacte d'una de les bretxes més grans conegudes. Exigeix la versió amb token, bloqueja l'accés des d'on no calgui i redueix els permisos del rol.

Suposar que el volum de blocs rendeix com un SSD local. És 5-10 vegades pitjor en latència i té un sostre d'IOPS que has contractat. Dissenya l'aplicació per agrupar escriptures abans de migrar, no després.

Muntar emmagatzematge d'objectes com si fos un disc. No hi ha escriptura parcial, ni bloqueig, ni fsync, ni reanomenament barat, i la latència és mil vegades més gran. Fes-lo servir amb la seva API.

Confondre peticions amb límits. Les peticions decideixen on et col·loquen i què se't reserva; els límits decideixen quan t'estrangulen o et maten. Peticions inflades donen clústers caríssims al 15 % d'ús; límits de memòria massa baixos donen reinicis amb codi 137 sense cap traça.

Oblidar que l'escalat automàtic triga. Si el teu pic arriba en 30 segons i la instància en triga 90 a arrencar, has escalat tard. Escala per una mètrica avançada i mantén coixí.

Dependre només dels registres locals. En un entorn efímer, el registre que no ha sortit de la màquina és un registre que s'ha perdut.

Consell: comença per l'estat. Abans de migrar res, fes l'inventari d'on viu cada dada i de quina és la seva font de la veritat. Tot el que no té estat migra fàcil; tota la resta és on són les decisions difícils.

Consell: prova de matar una instància expressament. En un entorn de proves, destrueix una rèplica de meteo-api en horari laboral i observa què passa. Si el servei no es recupera sol en menys d'un minut, el teu disseny encara tracta aquella instància com una mascota. És l'única manera de saber-ho abans que passi de debò.

Consell: posa una alarma de cost el primer dia. Un bucle d'escalat mal configurat, un volum oblidat o una transferència entre regions poden multiplicar la factura sense generar ni un sol error.

Exercicis

Exercici 1: escriure el cloud-config d'una rèplica de l'agregador

Escriu un fitxer #cloud-config complet que aprovisioni una instància per executar l'agregador, complint: (a) usuari de servei meteora amb UID 990, sense shell i amb la contrasenya blocada; (b) un compte d'operació amb clau pública i sudo limitat a reiniciar el servei; (c) un disc addicional formatat en ext4 i muntat a /var/lib/meteora amb les opcions del curs; (d) tallafocs amb política de denegació i només SSH des de la subxarxa privada; (e) el fitxer /etc/meteora/meteora.conf en mode 600 sense secrets, explicant d'on surten; (f) actualitzacions de seguretat automàtiques. Justifica cada bloc i assenyala tres coses que deliberadament no hi poses i per què.

Exercici 2: dimensionar recursos i diagnosticar un pod

meteo-api es desplega amb aquest manifest en nodes de 4 vCPU i 8 GiB:

resources:
  requests: {cpu: "2000m", memory: "4Gi"}
  limits:   {cpu: "2000m", memory: "4Gi"}

Mesurat en producció, cada rèplica fa servir de manera sostinguda 0,2 vCPU i 300 MiB, amb pics de 0,8 vCPU i 450 MiB.

(a) Quantes rèpliques caben per node amb aquest manifest, i quantes n'hi cabrien amb peticions ajustades? Calcula el malbaratament i el seu impacte en el nombre de nodes necessaris per a 30 rèpliques. (b) Proposa valors de requests i limits justificats. (c) Després del canvi, alguns pods comencen a reiniciar-se amb codi 137 i altres mostren latència p99 molt alta sense reiniciar-se: explica què li passa a cada grup, quin fitxer de cgroup ho confirma i com ho corregeixes. (d) Explica per què posar requests igual a limits en memòria és bona idea però en CPU sol no ser-ho.

Exercici 3: decidir l'emmagatzematge de Meteora al núvol

Meteora genera un fitxer diari de 17.280.000 bytes (720.000 lectures × 24 B). Les consultes són: el 92 % sobre les últimes 48 hores, el 7 % sobre l'últim mes i l'1 % sobre l'històric de cinc anys. L'ingestor rep 8.000 lectures per segon. El volum de blocs bàsic ofereix 3 IOPS per GB.

(a) Calcula el volum anual de dades crues i comprimides (factor 4:1), i dissenya la ubicació de cada conjunt de dades amb la seva classe d'emmagatzematge. (b) Calcula les IOPS que necessita l'ingestor escrivint lectura a lectura i agrupant en lots de 512, i digues quina mida de volum bàsic caldria en cada cas només per assolir aquestes IOPS. (c) Justifica per què el fitxer del dia en curs no pot estar en emmagatzematge d'objectes, amb almenys tres raons tècniques concretes. (d) Dissenya la política de còpies de seguretat complint la regla 3-2-1 de 05-03 i explica què afegeix la immutabilitat davant d'un segrest de dades.

Solucions

Solució 1

#cloud-config
hostname: agregador-01
timezone: Europe/Madrid

users:
  - name: meteora                      # (a) compte de servei
    uid: 990
    gid: 990
    system: true
    shell: /usr/sbin/nologin
    lock_passwd: true
  - name: operador                     # (b) compte d'operació
    groups: [sudo]
    shell: /bin/bash
    lock_passwd: true
    sudo: ["ALL=(ALL) NOPASSWD:/usr/bin/systemctl restart agregador"]
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... operador@meteora

package_update: true
packages: [nftables, chrony, unattended-upgrades]

disk_setup:                            # (c) disc de dades
  /dev/nvme1n1: {table_type: gpt, layout: true, overwrite: false}
fs_setup:
  - {device: /dev/nvme1n1, partition: 1, filesystem: ext4, label: meteora-dades}
mounts:
  - [LABEL=meteora-dades, /var/lib/meteora, ext4,
     "defaults,noatime,nosuid,nodev,data=ordered", "0", "2"]

write_files:
  - path: /etc/nftables.conf           # (d) tallafocs
    permissions: '0644'
    content: |
      table inet filtre {
        chain entrada {
          type filter hook input priority 0; policy drop;
          ct state established,related accept
          iif lo accept
          ip saddr 10.0.0.0/16 tcp dport 22 accept
        }
      }
  - path: /etc/meteora/meteora.conf    # (e) configuració SENSE secrets
    owner: meteora:meteora
    permissions: '0600'
    content: |
      [general]
      dades = /var/lib/meteora/lectures
      # secrets: es llegeixen del gestor en arrencar, amb la identitat de la instància
  - path: /etc/apt/apt.conf.d/20auto-upgrades   # (f)
    content: |
      APT::Periodic::Update-Package-Lists "1";
      APT::Periodic::Unattended-Upgrade "1";

runcmd:
  - [install, -d, -o, meteora, -g, meteora, -m, '0750', /var/lib/meteora/lectures]
  - [systemctl, enable, --now, nftables]
  - [systemctl, enable, --now, unattended-upgrades]

Justificació. L'UID 990 explícit és imprescindible perquè el volum es comparteix per número, no per nom (mateix motiu que al Dockerfile de 06-02). lock_passwd: true en tots dos comptes implanta el "només clau pública" de 05-02, i el sudo acotat a una ordre concreta aplica el mínim privilegi de 05-01 —recordant-ne el límit: acota el que s'executa, no el que es pot aconseguir—. Les opcions de muntatge noatime,nosuid,nodev són l'enfortiment de 04-03 declarat, no editat a mà. El tallafocs amb policy drop i SSH restringit a la subxarxa privada és defensa en profunditat a més del grup de seguretat. I les actualitzacions automàtiques tanquen la primera mesura de 05-03.

Tres coses que deliberadament no hi van:

  1. Cap secret, perquè el user-data és llegible per qualsevol procés de la instància via 169.254.169.254/latest/user-data: posar-l'hi és publicar-lo. Es llegeix del gestor de secrets en arrencar, autenticant-se amb el rol de la instància.
  2. Cap clau privada SSH ni certificat TLS, pel mateix motiu. Només la clau pública de l'operador.
  3. Cap instal·lació pesada d'aplicació (compilar, descarregar artefactes grans, configurar a mà). Això va a la imatge, construïda i versionada, no a l'arrencada: si va aquí, cada instància triga minuts a estar a punt, depèn que els repositoris responguin i deixa de ser reproduïble —és infraestructura mutable disfressada—.

Solució 2

(a) Càlcul de densitat. Amb requests: 2000m / 4Gi en nodes de 4 vCPU i 8 GiB: per CPU hi caben 2 pods, per memòria n'hi caben 2 (deixant a més marge per al sistema del node, en realitat 1). Prenguem 2 pods per node en el millor dels casos: per a 30 rèpliques calen 15 nodes.

Amb peticions ajustades a l'ús real (per exemple 300m / 512Mi): per CPU hi caben 13 pods, per memòria 16; el límit és la CPU, uns 12 pods per node deixant marge per al sistema. Per a 30 rèpliques n'hi ha prou amb 3 nodes.

El malbaratament és de 5 vegades: es reserven 2000m per fer-ne servir 200m, és a dir s'aprofita el 10 % del que s'ha reservat. I com que el planificador col·loca segons les peticions i no segons l'ús, el clúster apareixerà amb nodes al 5-10 % de CPU real i tot i així "ple", incapaç d'acceptar més pods. És la causa número u de factures desproporcionades.

(b) Valors proposats:

resources:
  requests: {cpu: "300m", memory: "512Mi"}
  limits:   {cpu: "1000m", memory: "512Mi"}

Justificació: la petició de CPU se situa una mica per damunt de l'ús sostingut (0,2 vCPU) perquè el planificador reservi el necessari sense inflar; el límit de CPU es posa folgat (1 vCPU) per absorbir els pics de 0,8 sense estrangular. En memòria, petició i límit iguals a 512 MiB, amb marge sobre el pic de 450 MiB, perquè la memòria no és comprimible.

(c) Els dos símptomes i la seva causa:

  • Els pods que reinicien amb codi 137 estan sent morts per l'OOM killer del cgroup: el seu consum real supera memory.max. El 137 és 128 + 9 (SIGKILL) i l'estat del pod serà OOMKilled. Es confirma amb memory.events (camp oom_kill diferent de zero) o amb l'esdeveniment del pod. Correcció: apujar el límit de memòria per damunt del pic real mesurat —potser el pic de 450 MiB no cobria tots els casos— i afegir un memory.high equivalent per tenir pressió abans que mort.
  • Els pods amb p99 alta que no reinicien estan estrangulats per CPU: han esgotat la seva quota cpu.max dins del període i queden aturats fins al següent. Es confirma amb nr_throttled davant de nr_periods a cpu.stat, exactament com a 06-02. Correcció: apujar el límit de CPU o ajustar el nombre de fils de l'aplicació a la quota; si l'únic que es buscava era prioritat, treure el límit i confiar en el pes derivat de la petició.

(d) Per què requests == limits és bona idea en memòria i dolenta en CPU. La memòria no és comprimible: no es pot "anar més a poc a poc" en memòria, o la tens o et maten. Igualar petició i límit garanteix que el que el planificador et va reservar és exactament el que pots fer servir, evita l'OOM per sobrecompromís del node i dona la classe de servei més estable. La CPU, en canvi, sí que és comprimible: es pot repartir en el temps. Igualar petició i límit en CPU malbarata la capacitat ociosa del node —el pod no pot aprofitar pics encara que hi hagi nuclis lliures— i provoca estrangulament evitable. L'habitual és petició ajustada a l'ús sostingut i límit generós o fins i tot absent en càrregues de confiança.

Solució 3

(a) Volum i ubicació. Diari: 17.280.000 B ≈ 16,5 MiB. Anual: 17.280.000 × 365 ≈ 6,3 GB crus, i amb compressió 4:1 uns 1,6 GB/any. En cinc anys, 7,9 GB comprimits: una quantitat ridícula, cosa que reforça que el criteri no ha de ser l'espai sinó la latència i el patró d'accés.

Conjunt Ubicació Classe Per què
Dia en curs + 48 h (92 % de consultes) Volum de blocs amb IOPS aprovisionades Estàndard Escriptura contínua i lectura de baixa latència
Últim mes (7 %) Objectes, sense comprimir o amb compressió lleugera Estàndard/accés infreqüent Consultes ocasionals, tolera desenes de ms
Històric de 5 anys (1 %) Objectes comprimits Fred / arxiu Es consulta molt poc; cost mínim
Agregats horaris Base de dades gestionada Accés aleatori i consultes analítiques

(b) IOPS.

  • Lectura a lectura: 8.000 escriptures/s → 8.000 IOPS. Amb 3 IOPS/GB caldria un volum de 2.667 GB (uns 2,7 TB) per emmagatzemar 16,5 MiB al dia. És absurd: s'estaria pagant un volum enorme només per comprar IOPS.
  • En lots de 512 lectures (512 × 24 = 12.288 B per escriptura): 8.000 / 512 ≈ 15,6 IOPS. Amb 3 IOPS/GB n'hi ha prou amb ~6 GB, i qualsevol volum raonable (per exemple 100 GB, amb 300 IOPS) hi sobra amb un marge de 20 vegades.

La conclusió és la de la lliçó: agrupar escriptures no és una microoptimització, és un requisit de disseny al núvol. El cost de l'agrupació és una finestra de pèrdua —les lectures del lot en curs si la instància mor—, que s'acota amb un fsync periòdic per temps a més de per mida, o posant una cua persistent al davant.

(c) Per què el dia en curs no pot anar en objectes, amb tres raons tècniques:

  1. No hi ha escriptura parcial ni append. Afegir una lectura al final exigiria reescriure l'objecte sencer (16,5 MiB) cada vegada: 8.000 vegades per segon és impossible i costaria una fortuna en peticions.
  2. La latència és mil vegades pitjor. 20-100 ms per operació davant dels microsegons d'un write local; amb 8.000 escriptures/s, ni de bon tros.
  3. No hi ha fsync, ni bloqueig, ni semàntica POSIX. No es pot garantir la durabilitat d'un registre concret com a 04-05, ni coordinar escriptors concurrents, ni fer servir les eines del sistema de fitxers.

I una quarta raó de cost: l'emmagatzematge d'objectes es factura per petició, així que 8.000 peticions per segon serien més cares que tota la resta junta.

(d) Política 3-2-1 amb immutabilitat.

  • 3 còpies: la del volum de blocs (producció), la d'objectes a la regió principal i la d'objectes en una altra regió.
  • 2 mitjans diferents: volum de blocs i emmagatzematge d'objectes, que són sistemes independents amb modes de fallada diferents.
  • 1 fora de l'emplaçament: la còpia en una altra regió cobreix la pèrdua d'una regió sencera.
  • Verificació: suma de verificació en escriure i comprovació periòdica; i sobretot, prova de restauració trimestral amb el temps mesurat, perquè una còpia no provada no és una còpia (05-03).

Què afegeix la immutabilitat. Un segrest de dades amb les credencials de la instància pot xifrar el volum i també esborrar o sobreescriure els objectes, perquè té els mateixos permisos que l'aplicació. Amb versionat més bloqueig d'objectes en mode de compliment, el proveïdor rebutja qualsevol esborrament o modificació fins que expiri la retenció, fins i tot si la petició arriba amb credencials vàlides i d'administrador. Això treu la còpia de l'abast de l'atacant, que és exactament el mateix raonament que a 05-04 portava a enviar els registres a un col·lector extern amb credencials de només enviament. La mesura es completa amb credencials separades per a les còpies, diferents de les de l'aplicació, i amb alertes davant de qualsevol intent d'esborrament.

Conclusió

Quan la màquina deixa de ser un objecte físic i passa a ser una crida a una API, el que canvia no és l'administració sinó el model mental: la instància s'aprovisiona en segons, viu hores o dies, i pot desaparèixer sense avís, amb un compromís de disponibilitat individual que ronda el 99,5 %. D'aquí la regla que governa tota la lliçó —no intentis que la màquina no falli; dissenya perquè la seva fallada no importi— i la metàfora del bestiar davant de les mascotes, amb les seves conseqüències incòmodes: res de canvis manuals, cap dada important al disc de la instància, SSH com a excepció i no com a eina, i la reinstal·lació com a resposta normal. Amb el matís honest que no tot pot ser bestiar: el que té estat continua sent una mascota, i l'objectiu és concentrar-lo en les mínimes peces possibles.

Els models de servei dibuixen on queda la frontera del sistema operatiu: a IaaS és teu sencer —i amb ell els pedaços, l'enfortiment de 05-03, el tallafocs i els registres: una instància acabada de crear és tan insegura com un servidor acabat d'instal·lar—, i a mesura que es puja a CaaS, PaaS i FaaS es guanya operació i es perden, molt literalment, les eines d'aquest curs. L'arrencada d'una instància encadena imatge, tipus, volum arrel, xarxa i cloud-init, i aquest darrer és la peça que converteix una plantilla genèrica en el teu servidor: usuaris amb clau pública, UID 990 explícit perquè els volums es comparteixen per número, muntatges amb noatime,nosuid,nodev declarats en comptes d'editats, i mai un secret, perquè el user-data és llegible des de dins a través del servei de metadades —el mateix endpoint que lliura les credencials del rol i que converteix un SSRF qualsevol en un robatori de credencials, com a la bretxa de Capital One—.

D'aquí surt la infraestructura immutable: en lloc de modificar servidors es construeixen imatges noves, cosa que elimina per construcció la deriva de configuració i converteix la reversió en una cosa avorrida, a canvi de complicar la depuració —i d'aquí la tècnica de treure del balancejador sense destruir, que enllaça amb la preservació d'evidències de 05-04—. Els sistemes operatius mínims i immutables porten la idea a l'extrem: sense shell no hi ha reverse shell, sense curl no hi ha segona fase, sense compilador no hi ha exploit local; s'eliminen classes senceres d'atac a canvi d'haver de depurar amb contenidors efímers que entren als namespaces del procés, el nsenter de 06-02 industrialitzat.

En emmagatzematge, els números manen: el volum de blocs en xarxa és 5-10 vegades pitjor en latència que un NVMe local i té un sostre d'IOPS que es contracta, cosa que converteix l'agrupació d'escriptures en un requisit de disseny —de 8.000 IOPS a 16 amb lots de 512 lectures—; l'efímer és rapidíssim i desapareix, així que només val per al que és reconstruïble; i l'emmagatzematge d'objectes no és un sistema de fitxers: sense escriptura parcial, sense fsync, sense bloqueig, amb reanomenament que copia i latències de desenes de mil·lisegons, per la qual cosa muntar-lo com a disc és gairebé sempre un error. Aplicat a Meteora: dia en curs en blocs, dies tancats en objectes comprimits amb transició a classe freda, i còpies en una altra regió amb versionat i immutabilitat, que és l'única defensa real contra un segrest de dades que té les teves credencials.

I la peça que tanca el cercle del curs: l'orquestració és un sistema operatiu del clúster. El pod és el procés, el planificador del clúster és el planificador de 02-02 un nivell més amunt, el kubelet és l'init del node, i les requests i limits d'un manifest es tradueixen literalment a cpu.weight, cpu.max i memory.max a /sys/fs/cgroup —d'aquí que passar-se de CPU estranguli i passar-se de memòria mati amb un 137—. El planificador col·loca segons les peticions i no segons l'ús, cosa que fa de les peticions inflades la causa número u de clústers caríssims al 15 %. Al voltant, Firecracker i gVisor demostren que la frontera entre VM i contenidor ha deixat de ser binària (una microVM que arrenca en 125 ms retallant tot el que QEMU emulava de més), l'escalat automàtic ensopega sempre amb l'arrencada en fred i l'oscil·lació, l'observabilitat exigeix treure els registres de la instància perquè la instància pot no existir quan els vagis a llegir, i el cost es converteix en criteri d'arquitectura de ple dret.

Ens queda una família sencera de sistemes operatius que va justament en la direcció contrària a tot això. Aquí hem parlat de màquines que sobren, que es creen per API i que es llencen quan fan nosa; d'escalar cap enfora quan falta capacitat; de latències mesurades en mil·lisegons i de fallades que es toleren reintentant. Però les estacions meteorològiques de Meteora —aquells dispositius encastats que a 01-03 vam decidir que portarien un RTOS— no poden fer res d'això: tenen 64 KB de RAM, funcionen amb una bateria que ha de durar mesos, no poden demanar una altra unitat quan els falta memòria, i si el mostreig del sensor arriba 10 mil·lisegons tard, la dada està malament, no pas "arriba a poc a poc". I a l'altre extrem del mateix problema, el telèfon des del qual un usuari consulta l'API de Meteora executa un Linux que ha hagut de reinventar el seu IPC, la seva gestió de memòria i el seu model de permisos perquè la bateria i l'absència de swap ho canvien tot.

Dues famílies amb la mateixa arrel que hem estudiat i amb restriccions oposades a les del servidor. És Sistemes Operatius Mòbils i de Temps Real, i tanca el mòdul.

Fonaments de Sistemes Operatius

Mòdul 1: Introducció als Sistemes Operatius

Mòdul 2: Gestió de Recursos

Mòdul 3: Concurrència

Mòdul 4: Estructures de Fitxers

Mòdul 5: Protecció i Seguretat del Sistema

Mòdul 6: Virtualització i Contenidors

Mòdul 7: Administració i Diagnòstic a la Pràctica

© Copyright 2026. Tots els drets reservats