meteo-01 arrenca sol, meteo-api està active (running), el temporitzador de l'agregador es dispara puntualment i systemctl list-units --failed no retorna res. I tot i així, els usuaris diuen que l'API va lenta. Cap de les eines de la lliçó anterior no respon a la pregunta «per què?», perquè totes contesten «està funcionant?» i no «funciona ?».

Aquesta lliçó ensenya el segon, i ho fa en un ordre deliberat: primer el mètode, després les eines. La raó és que la majoria dels diagnòstics fallits no fallen per desconèixer una ordre, sinó per saltar a conclusions: es veu la càrrega alta, es culpa la CPU, s'afegeixen nuclis i el problema continua perquè en realitat era la cua del disc. Un mètode ho evita. Veuràs dues metodologies amb nom i cognoms —USE i RED—, la llista de comprovació dels primers 60 segons que aplicaràs sempre igual, i després, recurs per recurs, què mesura de debò cada número que et retornen top, free, iostat, ss i vmstat, amb els paranys d'interpretació que enganyen gairebé tothom. Acabarem pujant un esglaó: de mirar una màquina a instrumentar una flota, i amb un laboratori on provocaràs cada tipus de saturació per veure-la amb els teus propis ulls.

El cas integrat, on tot això s'aplica a un incident real de matinada, és la lliçó següent i última del curs.

Contingut

  1. Símptoma, causa i línia base
  2. Dues metodologies: USE i RED
  3. La llista de comprovació dels primers 60 segons
  4. CPU: què mesura de debò cada número
  5. Memòria: per què free -h confon tothom
  6. E/S: iostat -x camp a camp
  7. Xarxa: cues, retransmissions i latència
  8. Pressió de recursos: PSI
  9. Observació profunda i el seu cost: strace, perf, eBPF
  10. Els registres com a font de diagnòstic
  11. De l'observació puntual al monitoratge continu
  12. Alertes útils, SLI i SLO
  13. Planificació de capacitat
  14. Laboratori guiat: provocar i observar cada saturació

Símptoma, causa i línia base

Un símptoma és el que es percep: «el web va lent», «els informes arriben tard», «l'aplicació dona errors 502». Una causa és un mecanisme concret i comprovable: «l'agregador està fent lectures aleatòries de 4 KB i la cua del RAID 1 és a 18 peticions pendents amb 40 ms d'espera mitjana». Entre tots dos hi ha una cadena de troballes, i la feina de diagnòstic consisteix a recórrer-la descartant en lloc d'endevinant.

Tres regles que eviten la majoria dels errors:

  • Quantifica el símptoma abans de tocar res. «Va lenta» no és una dada. «El p99 de /v1/lectures ha passat de 80 ms a 4,2 s des de les 03:05, amb el mateix volum de peticions» sí que ho és, i a més et dona un instant d'inici amb el qual correlacionar tota la resta.
  • Una hipòtesi, una comprovació. Canvia una cosa cada vegada. Si apliques tres mitigacions alhora i millora, no sabràs quina ha funcionat ni podràs escriure un post mortem honest.
  • Anota-ho amb marca de temps. Què miraves, què has vist i què has fet. En un incident llarg, la memòria falla i les sortides de les ordres són irrepetibles.

I per damunt de tot hi ha la línia base. Un número aïllat no significa res: una càrrega mitjana de 6 pot ser normal en una màquina de 16 nuclis i catastròfica en una de 2. Que el %util del disc sigui al 60 % pot ser l'habitual al migdia. Sense saber com es comporta meteo-01 un dimarts normal, qualsevol mesura durant l'incident és soroll. Per això el millor moment per preparar un diagnòstic és quan tot va bé: si encara no tens monitoratge continu, desa almenys una foto periòdica.

# Línia base rudimentària però utilíssima: una foto cada 5 minuts
{ date -Is; uptime; vmstat 1 3 | tail -1; free -m | sed -n 2p; \
  iostat -x 1 2 /dev/md0 | tail -3; ss -s | head -2; } >> /var/log/meteora/baseline.log

Què fa. Bolca en un sol fitxer, amb marca de temps ISO, la càrrega, un mostreig de vmstat (el primer registre d'aquestes eines són mitjanes des de l'arrencada i cal descartar-lo, d'aquí el tail -1), la memòria, l'activitat del RAID i el resum de sockets. Executat des d'un temporitzador de systemd cada 5 minuts, en una setmana tens una referència amb què comparar. Costa uns pocs kilobytes al dia i ha salvat més incidents que molts taulers.

Dues metodologies: USE i RED

USE (Brendan Gregg) s'aplica recurs per recurs i respon a «quin component del sistema està limitant?». Per a cadascun se'n miren tres coses:

  • Utilització: quina fracció del temps el recurs està ocupat.
  • Saturació: quanta feina està esperant en cua perquè el recurs no dona l'abast.
  • Errors: errades comptades pel recurs.

La saturació és la mètrica clau i la que més s'ignora. Un disc al 100 % d'utilització amb cua 1 està treballant a gust; el mateix disc al 100 % amb cua 30 està ofegat. La utilització té un sostre (100 %) i per això deixa d'informar tan bon punt l'assoleix; la saturació no té sostre i continua creixent amb la gravetat del problema.

RED s'aplica al servei i respon a «ho estan passant malament els usuaris?»: Rate (peticions per segon), Errors (quantes fallen) i Duration (quant triguen, en percentils). És la vista des de fora; USE és la vista des de dins. Es fan servir juntes: RED detecta i delimita el problema, USE localitza el recurs culpable.

El flux complet, que és el que seguiràs a la lliçó següent:

graph LR
    A["Símptoma<br/>'l'API va lenta'"] --> B["RED: quantificar<br/>p99, taxa, errors"]
    B --> C["Quan va començar?<br/>finestra temporal"]
    C --> D["USE recurs a recurs<br/>60 segons"]
    D --> E["Recurs saturat<br/>(cua, no utilització)"]
    E --> F["Quin procés?<br/>pidstat, iotop, perf"]
    F --> G["Causa concreta<br/>i mitigació mesurable"]

Taula d'aplicació a meteo-01:

Recurs Utilització Saturació Errors
CPU %us+%sy a mpstat -P ALL 1 Càrrega mitjana, procs r de vmstat, /proc/pressure/cpu Poc habituals (mcelog)
Memòria MemAvailable a /proc/meminfo si/so de vmstat, /proc/pressure/memory Morts de l'OOM killer a dmesg
Disc (/dev/md0) %util d'iostat -x aqu-sz, await, /proc/pressure/io dmesg (errors d'E/S), mdadm --detail
Xarxa rxkB/s/txkB/s a sar -n DEV Cua d'escolta plena, netstat -s ip -s link (errors, dropped)
Servei meteo-api Peticions/s Cua de connexions (Recv-Q del socket d'escolta) Codis 5xx a meteo-api.log

La llista de comprovació dels primers 60 segons

Aquesta seqüència s'executa sempre igual, en el mateix ordre, sense pensar. El seu valor no és que trobi la causa, sinó que en un minut descarta el 80 % de les possibilitats i et diu cap on has d'aprofundir.

uptime                    # 1. Quanta càrrega hi ha i des de quan?
dmesg -T | tail -30       # 2. Ha cridat el nucli? (OOM, errors de disc, RAID)
vmstat 1 5                # 3. Vista global: CPU, memòria, intercanvi, E/S, context
mpstat -P ALL 1 3         # 4. La càrrega està repartida o hi ha un nucli saturat?
pidstat 1 3               # 5. Quins processos consumeixen CPU, ara, per segon?
iostat -xz 1 3            # 6. Estan saturats els discos?
free -m                   # 7. Hi ha memòria disponible de debò?
ss -s                     # 8. Quantes connexions i en quin estat?
sar -n DEV 1 3            # 9. Quant trànsit de xarxa?
cat /proc/pressure/*      # 10. Qui està causant esperes? (PSI)
systemctl list-units --failed ; journalctl -p err -b --since '-30 min'

Què es busca a cadascun, que és el que realment importa:

  1. uptime — les tres càrregues mitjanes (1, 5 i 15 minuts). El que informa no és el número, sinó la tendència: si és 24.1, 8.4, 3.2, el problema està començant ara; si és 3.2, 8.4, 24.1, ja està remetent. També confirma si la màquina s'ha reiniciat fa poc.
  2. dmesg -T — és el primer que cal mirar perquè conté les errades que cap altra eina no ensenya: una mort per OOM killer, un disc amb errors de lectura, un RAID degradat, un desbordament de la taula de connexions. Un sol missatge aquí pot acabar el diagnòstic en 10 segons.
  3. vmstat 1 5 — la panoràmica. r és la cua d'execució (processos llestos esperant CPU); b els bloquejats en E/S ininterrompible; si/so l'intercanvi; us/sy/id/wa el repartiment de CPU; cs els canvis de context de 02-01.
  4. mpstat -P ALL 1 3 — distingeix «tota la màquina saturada» de «un sol nucli al 100 % i quinze ociosos», que és el símptoma d'un programa monofil o d'una interrupció mal repartida (02-07).
  5. pidstat 1 3 — a diferència de ps, mostra consum per interval, no acumulat des de l'arrencada. És la diferència entre «aquest procés ha fet servir 40 hores de CPU en dos mesos» i «aquest procés està fent servir el 180 % ara».
  6. iostat -xz 1 3-z omet els dispositius sense activitat, i deixa només el que és interessant.
  7. free -m — amb l'advertència de la secció de memòria: mira available, no free.
  8. ss -s — resum de sockets; un salt a timewait o a connexions sincronitzades apunta a la xarxa o al patró de clients.
  9. sar -n DEV 1 3 — trànsit per interfície, per descartar saturació d'enllaç.
  10. PSI — la resposta directa a «quant temps s'està perdent esperant cada recurs?».

I sempre, al final, els registres: unitats fallides i errors recents del diari.

CPU: què mesura de debò cada número

La càrrega mitjana no és utilització de CPU. És l'error d'interpretació més estès. A la majoria dels UNIX, la càrrega compta els processos en estat R (executant-se o llestos). A Linux, i només a Linux, compta a més els que estan en estat D, és a dir, en espera ininterrompible d'E/S (02-01).

La conseqüència pràctica és enorme: a meteo-01, amb 8 nuclis, una càrrega de 24 pot significar tres coses completament diferents —24 processos barallant-se per la CPU, o 2 fent servir CPU i 22 esperant el disc, o qualsevol barreja— i la resposta correcta és oposada en cada cas. Afegir CPU al segon escenari no arregla res. Per això la càrrega serveix per saber que alguna cosa passa, mai per saber què.

uptime
#  03:12:44 up 41 days,  load average: 24.31, 9.02, 4.11
mpstat -P ALL 1 3
# CPU  %usr %nice %sys %iowait %irq %soft %steal %idle
# all   4,2   0,0  2,1    88,3  0,0   0,3    0,0    5,1
ps -eo state,pid,comm | awk '$1=="D"' | head    # qui està en espera ininterrompible?

Què revela la combinació. Càrrega 24 amb %iowait del 88 % i %usr del 4 % és concloent: la CPU està pràcticament ociosa i els processos s'acumulen esperant el disc. El ps amb filtre D anomena els culpables. Si en canvi veiessis %usr al 95 % i %iowait a 0, el problema seria genuïnament de càlcul.

top -b -n1 | head -5
# top - 03:12:44 up 41 days,  3 users,  load average: 24,31, 9,02, 4,11
# Tasques: 214 total,   1 executant, 186 dormint,  27 aturades,   0 zombis
# %Cpu(s):  4,2 us,  2,1 sy,  0,0 ni,  5,1 id, 88,3 wa,  0,0 hi,  0,3 si,  0,0 st
# MiB Mem :  16037,0 total,    412,0 free,  11204,0 used,   4421,0 buff/cache
# MiB Swap:   4095,0 total,   3967,0 free,    128,0 used.   4102,0 avail Mem

Com llegir la capçalera, que és on hi ha gairebé tota la informació. La línia de tasques ja avança el diagnòstic: 27 processos en estat «aturades» —que a la traducció de top agrupa els detinguts i els ininterrompibles— amb només un executant-se és una anomalia enorme. htop presenta el mateix de manera interactiva, amb una barra per nucli (útil per veure d'un cop d'ull si la càrrega està repartida), l'arbre de processos amb F5 i la possibilitat d'ordenar per qualsevol columna; el seu avantatge real és que fa visible en un segon el que a top s'ha de buscar.

Els camps de la línia de CPU, un a un:

Camp Què mesura Quan preocupa
%us (user) Temps en codi d'usuari Alt i sostingut: hi ha feina de càlcul real
%sy (system) Temps al nucli >20 % sostingut: excés de crides al sistema, E/S mal emmagatzemada
%ni (nice) Processos amb prioritat rebaixada Informatiu
%id (idle) Ociós
%wa (iowait) Ociós, amb E/S pendent Alt: el coll d'ampolla és a l'emmagatzematge
%hi/%si Interrupcions de maquinari i de programari %si alt: molta xarxa (02-07)
%st (steal) CPU que l'hipervisor va donar a un altre hoste >2 %: veí sorollós o sobresubscripció (06-01)

%wa es mereix un matís que gairebé ningú no explica: no és temps «gastat» en E/S, és temps ociós havent-hi E/S pendent. Si la CPU tingués una altra feina a fer, la faria i el %wa baixaria sense que l'E/S millorés gens ni mica. Per això un %wa baix no descarta un problema de disc en una màquina ocupada: cal mirar iostat.

%st és la mètrica que només existeix en màquines virtuals, i és la que explica un misteri recurrent: «el meu procés triga el doble i la CPU és al 50 %». Si st és del 15 %, l'hipervisor t'està prenent un de cada set cicles i no hi ha res que puguis canviar dins de l'hoste.

pidstat -u 1 3                     # CPU per procés, per segon
pidstat -t -p 1834 1 3             # desglossament per FIL del procés 1834
top -H -p 1834                     # el mateix, interactiu
taskset -pc 1834                   # està lligat a uns nuclis concrets?

Per què el desglossament per fil importa. Un procés al 100 % en una màquina de 8 nuclis pot ser un programa monofil saturat —sostre real, no hi ha res més a gratar sense canviar el codi— o un procés multifil tot just ocupat. pidstat -t o top -H ho distingeixen en dos segons, i aquesta dada canvia del tot la recomanació.

Memòria: per què free -h confon tothom

free -m
#               total     usada   lliure  compartida    mem/cau  disponible
# Mem:          16037     11204      412         890       4421        4102
# Swap:          4095       128     3967

Gairebé tothom llegeix «lliure: 412 MB» i entra en pànic. És la lectura equivocada. Linux fa servir tota la RAM que sobra com a memòria cau de pàgines (02-03), perquè memòria lliure és memòria malgastada: desant-hi els fitxers llegits recentment, evita anar al disc. Aquesta memòria cau és reclamable a l'instant: si un procés demana memòria, el nucli descarta pàgines netes de la memòria cau i l'hi dona.

La columna que importa és disponible (MemAvailable), que és l'estimació del nucli de quanta memòria pot aconseguir un procés nou sense provocar intercanvi. Aquí, 4.102 MB: la màquina està còmoda malgrat el «412 lliure».

Concepte Significat Reclamable?
usada Anònima de processos, més nucli No
mem/cau Memòria cau de pàgines i d'inodes , gairebé tota
lliure Mai utilitzada Sí (i és normal que sigui baixa)
disponible El que pot aconseguir un procés nou És la xifra bona
grep -E 'MemTotal|MemFree|MemAvailable|Cached|Dirty|Writeback|SwapCached' /proc/meminfo
vmstat 1 5
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  2 14  131072 421884  8912 4523112    0 2048  8192 12288 4102 8934  4  3  5 88  0

Com llegir l'intercanvi. swpd (quant n'hi ha a la swap) pot ser alt sense que passi res: són pàgines que es van expulsar fa dies i ningú no ha tornat a demanar. El que indica un problema actiu són si i so, les pàgines que entren i surten per segon. Un so sostingut de 2.048 pàgines/s són 8 MB/s d'escriptura a la swap; si a més hi ha un si alt alhora, el sistema està fent thrashing (02-04): dedica més temps a moure pàgines que a treballar, i la latència es dispara dos ordres de magnitud.

Detecció de fuites de memòria. Una fuita no es veu en una foto: es veu en una pel·lícula. Segueix el RSS —memòria física realment ocupada, davant del VSZ, que és només espai d'adreces reservat (02-04)— al llarg del temps:

while true; do
    printf '%s %s\n' "$(date -Is)" "$(awk '/VmRSS/{print $2}' /proc/1834/status)"
    sleep 60
done >> /var/log/meteora/rss-meteo-api.log

Com interpretar-ho. Un servei sa estabilitza el seu RSS després de l'escalfament; una fuita dibuixa una recta ascendent que no es doblega mai. Si el RSS de meteo-api puja 40 MB cada hora de manera constant, en 50 hores assolirà el MemoryMax=2G de la seva unitat i systemd el matarà. L'avantatge d'haver posat aquest límit a 07-02 és que la fuita mata només el servei culpable en lloc de deixar que l'OOM killer del sistema triï víctima.

El rastre de l'OOM killer, que cal saber reconèixer de memòria:

dmesg -T | grep -iE 'out of memory|killed process'
# [Mon Aug 31 03:07:52 2026] Out of memory: Killed process 1834 (meteo-api)
#   total-vm:3982104kB, anon-rss:2914208kB, file-rss:0kB, shmem-rss:8192kB, UID:990 pgtables:6284kB oom_score_adj:0
journalctl -k --since '-1 hour' | grep -i oom

Què et diu aquesta línia. Anomena el procés triat, el seu UID (990, o sigui meteora) i el seu anon-rss en el moment de morir: 2,9 GB. Recorda de 02-04 que l'OOM killer tria per oom_score, que és principalment proporcional a la memòria utilitzada, així que sol matar el procés més gran, no el culpable. Un símptoma típic i desconcertant és que mor la base de dades perquè un guió maldestre va demanar tota la RAM.

E/S: iostat -x camp a camp

iostat -x 1 3
# Device   r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  r_await  w_await  aqu-sz  rareq-sz  %util
# md0    3891,0    42,0 15564,0   672,0     0,0     0,0    41,20     2,10   18,24      4,00   99,80
# sda    1948,0    21,0  7792,0   336,0     0,0     0,0    40,85     2,05    9,12      4,00   99,60
# sdb    1943,0    21,0  7772,0   336,0     0,0     0,0    41,55     2,08    9,11      4,00   99,70
Camp Què és Què cal mirar
r/s, w/s Operacions per segon (IOPS) Compara-ho amb la capacitat del dispositiu
rkB/s, wkB/s Amplada de banda Juntament amb els IOPS dona la mida mitjana de petició
rrqm/s, wrqm/s Peticions fusionades pel planificador Alt = accés seqüencial que el nucli agrupa
r_await, w_await Latència mitjana total (cua + servei), en ms La mètrica del dolor: >20 ms en lectura ja es nota
aqu-sz Longitud mitjana de la cua La saturació d'USE
rareq-sz Mida mitjana de petició de lectura, en KB 4 KB = aleatori; 128-512 KB = seqüencial
%util Fracció de temps amb almenys una petició en vol Compte: enganya en NVMe

Diagnòstic de l'exemple. Un rareq-sz de 4,00 KB amb rrqm/s a zero diu que són lectures aleatòries d'un bloc: no hi ha res a fusionar perquè els blocs no són contigus. 3.891 lectures/s de 4 KB són només 15,5 MB/s d'amplada de banda —irrisori— però gairebé 4.000 IOPS, que per a un RAID 1 de discos mecànics (unes 150-200 IOPS per disc) és un ordre de magnitud per damunt de la seva capacitat. D'aquí l'aqu-sz de 18 (divuit peticions esperant de mitjana) i el r_await de 41 ms. La causa no és «el disc és lent»: és el patró d'accés, exactament el que es va explicar a 02-05.

Per què %util enganya en NVMe. El camp es calcula com el percentatge de temps en què hi havia almenys una petició en vol. En un disc mecànic, amb un únic capçal, això equival de debò a «ocupat». Un NVMe modern atén desenes de milers d'IOPS en paral·lel repartides en múltiples cues: pot ser al 100 % de %util fent servir el 5 % de la seva capacitat real. En SSD i NVMe, les mètriques fiables són await i aqu-sz, no %util.

pidstat -d 1 3          # E/S per procés: kB_rd/s, kB_wr/s, iodelay
iotop -oPa              # interactiu, només processos amb E/S activa
cat /proc/1834/io       # comptadors acumulats del procés
filefrag -v /var/lib/meteora/lectures/2026-08-31.dat | tail -3   # fragmentació en extents

Per a què serveix cadascun. pidstat -d atribueix l'E/S a processos concrets, que és el pas que converteix «el disc està saturat» en «l'agregador està saturant el disc»; la seva columna iodelay mesura el temps que el procés ha passat bloquejat esperant. filefrag compta els extents d'un fitxer (04-05): si un fitxer de 17 MB és en 3 extents, llegir-lo sencer és pràcticament seqüencial; si és en 4.000, fins i tot una lectura completa es comporta com a accés aleatori.

Com es veu la saturació del RAID 1 de Meteora. Fixa't que a la sortida de dalt md0 rep 3.891 lectures/s i cada disc físic n'atén unes 1.945: el RAID 1 reparteix les lectures entre les dues còpies, cosa que duplica els IOPS de lectura disponibles, però no ajuda gens en escriptura, perquè cada escriptura ha d'anar als dos discos. És l'asimetria fonamental del mirall, i explica per què un RAID 1 serveix per donar disponibilitat i una mica de lectura, però mai no resol un problema d'escriptures.

Xarxa: cues, retransmissions i latència

ss -s
# Total: 1284
# TCP:   1201 (estab 940, closed 187, orphaned 0, timewait 186)
ss -tanp state listen '( sport = :443 )'
# Recv-Q Send-Q  Local Address:Port  Process
#   129    2048        0.0.0.0:443   users:(("meteo-api",pid=1834,fd=7))
ip -s link show eth0 | sed -n '3,6p'
nstat -az | grep -E 'TcpRetransSegs|TcpExtListenOverflows|TcpExtListenDrops'
# ip -s link show eth0
#     RX: bytes  packets  errors  dropped  overrun  mcast
#     8912443021  9124883       0     1204        0     41
#     TX: bytes  packets  errors  dropped  carrier  collsns
#     4412009834  6021144       0        0        0       0

Què significa cada cosa, i aquí hi ha una subtilesa que confon molta gent. En un socket en escolta, les columnes canvien de sentit: Recv-Q és el nombre de connexions ja establertes esperant que l'aplicació faci accept() i Send-Q és la mida màxima d'aquesta cua (el Backlog=2048 de la unitat de 07-02). Un Recv-Q de 129 sostingut significa que meteo-api no accepta connexions tan de pressa com arriben: el problema no és la xarxa, és que l'aplicació està ocupada o bloquejada. Si la cua s'omple del tot, el nucli comença a descartar SYN i el comptador ListenOverflows creix; el client veu una connexió que no respon i reintenta, amplificant el problema.

Les retransmissions (TcpRetransSegs) indiquen pèrdua de paquets: un percentatge sostingut per damunt del 0,1 % del total de segments apunta a congestió o a un enllaç defectuós. I a ip -s link, les columnes errors i dropped de recepció distingeixen una errada física (cable, negociació de velocitat) d'un descart per cua plena al nucli mateix.

Latència davant d'amplada de banda, distinció que decideix molts dissenys: l'amplada de banda és quants bytes per segon hi caben; la latència és quant triga el primer a arribar. Un enllaç de 10 Gb/s amb 200 ms d'anada i tornada és magnífic per transferir un fitxer de 50 GB i pèssim per a una API que fa 30 consultes encadenades, perquè aquestes 30 anades i tornades són 6 segons irreductibles: cap millora d'amplada de banda no els baixa. Quan meteo-api respon lent, la pregunta correcta és si triga a començar a respondre (latència, dependències, bloqueigs) o a acabar (volum, amplada de banda).

Pressió de recursos: PSI

PSI (Pressure Stall Information, Linux 4.20+) és probablement la mètrica més útil apareguda a l'última dècada, i respon justament a la pregunta que les altres esquiven: quant temps s'està perdent per culpa de cada recurs.

cat /proc/pressure/io
# some avg10=87.42 avg60=71.03 avg300=44.18 total=1904821334
# full avg10=61.19 avg60=48.77 avg300=29.02 total=1233908112
cat /proc/pressure/cpu /proc/pressure/memory

Com es llegeix. some és el percentatge de temps en què almenys una tasca ha estat bloquejada esperant aquest recurs; full és el percentatge en què totes les tasques executables ho estaven, és a dir, temps en què la màquina sencera no va progressar. Els tres números són mitjanes mòbils de 10, 60 i 300 segons, cosa que dona tendència sense necessitat de mostrejar.

Un io full avg10 del 61 % significa, literalment, que en els últims 10 segons la màquina ha passat sis de cada deu segons sense poder fer res per esperar el disc. Compara-ho amb el que dirien les mètriques clàssiques del mateix moment: la CPU sembla ociosa (%id alt), la memòria sembla bé i només el %wa insinua alguna cosa. PSI ho diu sense ambigüitat i, sobretot, en unitats d'impacte —temps perdut— en lloc d'en unitats de recurs. Per això és una excel·lent base per alertar: io full avg60 > 20 % és un llindar que significa el mateix en qualsevol màquina, amb qualsevol disc.

PSI també existeix per cgroup (/sys/fs/cgroup/system.slice/meteo-api.service/io.pressure), cosa que permet respondre a «quin servei està patint?» i «quin servei està causant el patiment?» per separat.

Observació profunda i el seu cost: strace, perf, eBPF

Quan les mètriques agregades no basten, es baixa al detall. Aquestes eines responen a «què està fent exactament aquest procés?», però costen, i cal saber quant.

strace -c -p 1834 -f          # resum: quantes crides i quant temps a cadascuna
strace -T -e trace=read,pread64 -p 1834 2>&1 | head -20    # amb durada per crida

Advertència seriosa. strace funciona amb ptrace, que atura el procés a cada entrada i sortida de crida al sistema i fa dos canvis de context extra per cadascuna. La alentiment típic va de 10 a 100 vegades per a un procés intensiu en crides. Sobre meteo-api en producció, amb milers de peticions per segon, strace no és una observació: és una caiguda provocada. Fes-lo servir amb -c (que només agrega), durant pocs segons, sobre un procés poc crític, o en un entorn de proves. La regla és: strace per entendre un programa, mai per mesurar-ne un en producció.

perf top -p 1834                        # quines funcions consumeixen CPU, en viu
perf record -F 99 -g -p 1834 -- sleep 30   # mostreig a 99 Hz amb pila de crides
perf report --stdio | head -30

Per què perf sí que és viable. No intercepta res: mostreja. A 99 Hz pren 99 fotos per segon d'on és el comptador de programa i quina pila hi ha a sota; el sobrecost típic és per sota de l'1-2 %. La freqüència 99 i no 100 és un truc deliberat per no sincronitzar-se amb temporitzadors del sistema que solen anar a 100 Hz i esbiaixarien el mostreig. El resultat natural de perf record és un gràfic de flames (flame graph): un dibuix en què l'eix horitzontal és la proporció de mostres —no el temps— i el vertical la profunditat de la pila, de manera que els altiplans amples assenyalen a l'instant on se'n va la CPU. És la manera més ràpida que existeix de trobar un punt calent de codi.

# eBPF: instrumentació segura al nucli, amb sobrecost mínim
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
bpftrace -e 'tracepoint:block:block_rq_issue { @bytes = hist(args->bytes); }'
biolatency-bpfcc 10 1     # histograma de latència de bloc

Què aporta eBPF. Programes verificats que el nucli executa en punts d'instrumentació, agregant dins del nucli i retornant només el resultat, sense copiar cada esdeveniment a espai d'usuari. El primer exemple compta les openat per programa; el segon dibuixa un histograma de la mida de les peticions de bloc, que en el cas de Meteora mostraria d'un cop d'ull el pic a 4 KB que delata l'accés aleatori; biolatency dona la distribució de latències de disc, amb la qual es veuen les cues llargues que una mitjana amaga. És l'evolució natural de tot l'anterior: la potència de strace amb un cost més a prop del de perf.

Els registres com a font de diagnòstic

Les mètriques diuen què passa; els registres solen dir per què. Amb les eines de 05-04:

journalctl --since '03:00' --until '03:20' -p warning --no-pager   # tot el sistema, acotat
journalctl -u meteo-api.service --since '03:00' -o short-precise   # amb microsegons
journalctl -k --since '03:00' | grep -iE 'oom|error|reset|degraded'
awk '$9 >= 500 {c[$9]++} END {for (k in c) print c[k], k}' /var/log/meteora/meteo-api.log

La tècnica que més rendeix és la correlació temporal. Pren l'instant exacte en què va començar el símptoma (03:05, segons el percentil que vas calcular al principi) i mira tot el que va passar a la màquina en aquella finestra, sense filtrar per servei: un desplegament, una tasca programada que va arrencar, un logrotate, un disc que va reportar un error, un pic de connexions. La causa sol ser en aquella finestra, i moltes vegades en un servei diferent del que dona el símptoma. Un buit sospitós als registres —minuts sense cap entrada en un fitxer que escriu cada segon— és en si mateix una troballa de primer ordre.

# Hi ha buits en un registre que s'hauria d'escriure cada segon?
awk '{split($4, t, ":"); m = t[2]":"t[3]; if (m != prev) {print m; prev = m}}' \
    /var/log/meteora/meteo-api.log | uniq -c | awk '$1 < 10'

Què fa. Compta quantes entrades hi ha per minut i mostra els minuts amb menys de deu. En un servei que registra milers de peticions per minut, un minut amb dues entrades —o absent— significa que el servei va estar bloquejat, que el disc no acceptava escriptures… o que algú va manipular el fitxer, possibilitat que cal prendre's seriosament i que veurem a 07-04.

De l'observació puntual al monitoratge continu

Tot l'anterior serveix quan ja hi ha un incident i tu ets al davant. Això no escala: cal que els números es recullin sempre, per poder mirar enrere i perquè algú avisi sense que ningú estigui mirant.

Peça Què fa Exemples
Exportador Exposa mètriques de la màquina o del servei node_exporter, mètriques pròpies de meteo-api
Base de sèries temporals Mostreja i emmagatzema (mètrica, etiquetes, temps, valor) Prometheus, VictoriaMetrics, InfluxDB
Taulers Representen l'evolució i faciliten la correlació Grafana
Alertes Avaluen regles i notifiquen Alertmanager
Registres i traces Context i seguiment de peticions Loki, OpenTelemetry
# Un exportador és, en el fons, un servidor HTTP que retorna text pla
curl -s localhost:9100/metrics | grep -E '^node_(load1|memory_MemAvailable_bytes|pressure)'
# node_load1 24.31
# node_memory_MemAvailable_bytes 4.30178304e+09
# node_pressure_io_waiting_seconds_total 1904.821334

Què demostra. No hi ha màgia: l'exportador llegeix /proc/loadavg, /proc/meminfo i /proc/pressure/io —els mateixos fitxers que has estat consultant a mà— i els publica en un format que una base de sèries temporals recull cada 15 segons. Entendre això té una conseqüència pràctica: qualsevol número que sàpigues obtenir amb una ordre el pots convertir en una mètrica històrica i en una alerta, inclosa la sortida del guió de verificació de 07-01.

Quatre decisions pràctiques que determinen si el sistema serveix o fa nosa:

  • Interval de mostreig. Amb 60 segons no veuràs un pic de 20; amb 5 segons multipliques per dotze el volum. Per a infraestructura, 15 segons és el punt d'equilibri habitual.
  • Cardinalitat. Cada combinació diferent d'etiquetes és una sèrie temporal. Etiquetar per estacio_id amb 3.000 estacions i 20 mètriques són 60.000 sèries per instància: és la manera més comuna de tombar un sistema de monitoratge. Etiqueta per dimensions de cardinalitat acotada (servei, endpoint, codi d'estat), mai per identificadors lliures.
  • Retenció i resolució. Alta resolució poc temps (15 s durant 15 dies) i agregats a llarg termini (5 min durant 2 anys, per a tendències i capacitat).
  • Percentils, no mitjanes. Si el 99 % de les peticions triga 20 ms i l'1 % triga 10 s, la mitjana és 120 ms i no descriu l'experiència de ningú. El p99 és el número que correspon als usuaris que es queixen. Desa p50, p95 i p99, i recorda que els percentils no es promedien: la mitjana dels p99 de deu màquines no és el p99 del conjunt.

Alertes útils, SLI i SLO

La diferència entre un sistema d'alertes i una font de soroll cap en una frase: alerta sobre símptomes que percep l'usuari, no sobre utilització de recursos.

Mala alerta Per què falla Bona alerta
«CPU > 80 %» Una màquina ben aprofitada és al 80 %; i pot anar malament al 30 % «p99 de latència > 1 s durant 10 min»
«Disc al 85 %» Dispara sempre i s'ignora «El disc s'omplirà en < 4 h segons la tendència»
«Procés caigut» systemd ja el reinicia «El servei porta 5 min sense arrencar (start-limit-hit
«Hi ha swap utilitzada» Pot portar setmanes allà sense efecte «si/so > 0 durant 5 min» o «memory full avg60 > 10 %»

Una alerta ha de complir tres condicions: és real (alguna cosa va malament de debò), és accionable (hi ha alguna cosa que qui la rep pot fer) i és urgent (no pot esperar a demà). Si en falla alguna, no és una alerta: és un tauler, o un tiquet.

Formalitzar això són els SLI i els SLO. Un SLI (indicador) és una mesura de l'experiència de l'usuari: «percentatge de peticions a /v1/lectures respostes correctament en menys de 500 ms». Un SLO (objectiu) és el nivell compromès: «99,5 % en una finestra de 30 dies». La conseqüència pràctica és el pressupost d'error: el 0,5 % de 30 dies són unes 3,6 hores d'incompliment permeses al mes. Aquest pressupost converteix discussions d'opinió en decisions amb dades —si queda molt pressupost, es desplega i s'experimenta; si s'ha consumit, es congela i s'estabilitza— i defineix l'alerta que de debò importa: avisar quan el pressupost s'està esgotant massa de pressa, no quan un recurs passa d'un llindar arbitrari.

Planificació de capacitat

Diagnosticar és reaccionar; planificar capacitat és no haver de reaccionar. Amb les sèries a llarg termini es responen preguntes com quan s'omplirà /var/lib/meteora, quantes estacions més aguanta l'ingestor o si el RAID 1 donarà l'abast el proper estiu.

Un exemple amb els números de Meteora. Cada dia genera un fitxer de 17.280.000 bytes, és a dir 16,5 MiB; a l'any, uns 6,0 GiB. Si el volum té 200 GB i ja n'hi ha 120 GB d'utilitzats, en queden 80 GB, que a aquest ritme són més de tretze anys: el creixement per nombre de dies és irrellevant. Però si el negoci preveu passar de 500 a 3.000 estacions, el volum diari es multiplica per sis (99 MiB/dia, 35 GiB/any) i el marge baixa a poc més de dos anys… i, molt abans que l'espai, el problema serà l'IOPS: sis vegades més lectures concurrents sobre un RAID 1 que ja vam veure saturat a 4.000 IOPS aleatòries.

D'aquí les dues regles de la planificació de capacitat: projecta sobre l'impulsor del negoci (estacions, usuaris, peticions), no sobre el calendari; i recorda que els recursos no s'esgoten alhora —normalment el coll d'ampolla arriba per IOPS o per latència molt abans que per espai o per CPU—. I tingues present que la latència no creix de manera lineal: per teoria de cues, quan la utilització passa del 70-80 %, el temps d'espera es dispara, així que planificar per al 95 % d'utilització és planificar un incident.

Laboratori guiat: provocar i observar cada saturació

Advertència expressa: fes això en una màquina virtual de proves d'usar i llençar, mai en producció. Cada exercici provoca deliberadament una degradació; algun pot deixar la màquina sense respondre uns minuts. Tingues a mà com reiniciar-la.

sudo apt install stress-ng sysstat fio bpfcc-tools

Laboratori 1 — Saturació de CPU. En un terminal, stress-ng --cpu 8 --timeout 120s. En un altre, observa:

uptime ; mpstat -P ALL 1 3 ; vmstat 1 3 ; cat /proc/pressure/cpu

Què has de veure i per què. La càrrega puja cap al nombre de treballadors; %usr a prop del 100 % i %iowait a zero; la columna r de vmstat (processos llestos) creix per damunt del nombre de nuclis; i cpu some avg10 puja clarament mentre full es manté baix, perquè sempre hi ha algú executant-se. Contrasta això amb la signatura de l'E/S: càrrega alta amb %usr alt és CPU; càrrega alta amb %wa alt és disc. Aquesta comparació és l'objectiu de l'exercici.

Laboratori 2 — Pressió de memòria i intercanvi. stress-ng --vm 2 --vm-bytes 80% --timeout 120s:

free -m ; vmstat 1 5 ; cat /proc/pressure/memory ; dmesg -T | tail -5

Què has de veure. disponible cau; si/so comencen a moure's quan la memòria anònima supera el que hi cap; memory some puja abans que cap altre indicador. Si estrenys més (--vm-bytes 95%), arribarà l'OOM killer i veuràs a dmesg la línia Killed process amb el seu anon-rss, que és exactament el rastre que has après a reconèixer. Observa també com la memòria cau (mem/cau) es redueix automàticament per cedir memòria: la demostració pràctica que no era memòria ocupada.

Laboratori 3 — Saturació d'E/S aleatòria (la signatura del cas de Meteora):

fio --name=aleatori --rw=randread --bs=4k --size=2G --numjobs=4 \
    --iodepth=32 --runtime=120 --time_based --directory=/var/tmp
# En un altre terminal:
iostat -xz 1 ; pidstat -d 1 ; cat /proc/pressure/io ; iotop -oPa

Què has de veure i comparar. rareq-sz clavat a 4 KB, rrqm/s gairebé zero (res a fusionar), aqu-sz alt, await de desenes de mil·lisegons i %util a prop del 100 %. Ara repeteix-ho amb --rw=read --bs=1M: veuràs una amplada de banda moltíssim més gran, rareq-sz gran, rrqm/s alt i await baix, amb el mateix %util. Aquesta comparació és la lliçó central del laboratori i del mòdul sencer: %util no distingeix un disc còmode d'un d'ofegat, i el patró d'accés importa més que el dispositiu. Amb --direct=1 evites a més que la memòria cau de pàgines t'emmascari els resultats.

Acaba cada laboratori anotant la «signatura» de cada saturació: quina combinació exacta de números has vist. Aquest quadern és el que et permetrà reconèixer el problema en tres segons quan passi de debò.

Errors Habituals i Consells

Error Per què és un error Què cal fer
Interpretar la càrrega mitjana com a ús de CPU A Linux inclou els processos en estat D Creua-ho amb %wa, %usr i ps -eo state
Alarmar-se per un free baix La memòria cau és reclamable Mira disponible / MemAvailable
Refiar-se de %util en SSD/NVMe El dispositiu atén en paral·lel Fes servir await i aqu-sz
Fer servir el primer registre de vmstat/iostat És la mitjana des de l'arrencada Descarta la primera mostra
strace en producció Alentiment de 10× a 100× perf, eBPF, o strace -c uns segons
Mesurar amb mitjanes Amaguen la cua que pateixen els usuaris p50, p95, p99
Alertar sobre utilització Genera soroll i s'acaba ignorant Alerta sobre símptomes de l'usuari
Canviar diverses coses alhora No sabràs què ha funcionat Una hipòtesi, una comprovació
Diagnosticar sense línia base No saps què és anormal Desa una foto periòdica
Etiquetar mètriques per identificador lliure Explosió de cardinalitat Etiquetes de cardinalitat acotada
Mirar només el servei que dona el símptoma La causa sol ser en un altre Correlaciona tota la finestra temporal

Consells: comença sempre per dmesg, perquè un sol missatge et pot estalviar una hora; aprèn de memòria la llista dels 60 segons i executa-la sencera encara que creguis saber la resposta, perquè descartar té valor; mesura abans i després de cada mitigació, amb la mateixa ordre; i desa les sortides en un fitxer amb marca de temps (ordre | tee -a /var/tmp/incident-$(date +%s).log), perquè al post mortem aquestes captures són irrepetibles.

Exercicis

Exercici 1: llegir una foto del sistema

Davant d'una queixa de lentitud a meteo-api obtens això:

load average: 31.44, 12.07, 5.90
%usr 3,1  %sys 2,4  %iowait 91,2  %steal 0,0  %idle 3,3
free -m:  total 16037  usada 9210  lliure 388  mem/cau 6439  disponible 6120
vmstat: r=1 b=27 si=0 so=0 bi=61440 bo=1024 cs=3980
iostat md0: r/s=4102 w/s=38 rareq-sz=4,00 rrqm/s=0,0 await=52,10 aqu-sz=27,40 %util=99,9
/proc/pressure/io: full avg10=74.28

Digues quin recurs és el coll d'ampolla, què descartes amb cada línia i quina seria la teva ordre següent.

Exercici 2: distingir dos incidents de memòria

Dues màquines es comporten així. Determina en quina hi ha un problema real i què faries en cada cas.

  • A: lliure=210 MB, disponible=5.980 MB, swpd=2.100 MB, si=0, so=0, memory some avg60=0.4
  • B: lliure=1.900 MB, disponible=1.980 MB, swpd=180 MB, si=1.840, so=2.310, memory some avg60=63.7

Exercici 3: convertir alertes de soroll en alertes útils

Reescriu aquestes tres alertes perquè compleixin les condicions de real, accionable i urgent, i justifica cada canvi: (a) «CPU del servidor > 85 % durant 1 minut»; (b) «Hi ha memòria d'intercanvi en ús»; (c) «El procés meteo-api no s'està executant».

Solucions

Solució 1

Coll d'ampolla: E/S de disc, amb un patró de lectura aleatòria de 4 KB. Descarts línia a línia:

  • Càrrega 31 amb %usr 3,1 % i %idle 3,3 %: descarta la CPU com a causa. Amb 8 nuclis, una càrrega de 31 i gairebé gens de temps d'usuari només es pot explicar per processos en estat D, que a Linux compten a la càrrega.
  • %iowait 91,2 % i %steal 0: confirma espera d'E/S i descarta que sigui un problema de l'hipervisor.
  • disponible 6.120 MB, si/so a zero: descarta la memòria. El lliure baix és normal per la memòria cau.
  • vmstat amb r=1 i b=27: la prova definitiva. Només un procés llest per executar i vint-i-set bloquejats en E/S ininterrompible. És la signatura exacta de la saturació de disc.
  • rareq-sz=4,00 amb rrqm/s=0: lectures aleatòries de 4 KB; el planificador no pot fusionar res perquè els blocs no són contigus. 4.102 IOPS és un ordre de magnitud per damunt del que dona un RAID 1 mecànic.
  • aqu-sz=27,4 i await=52 ms: saturació severa, no simple utilització alta. El disc és al 99,9 % i amb 27 peticions esperant.
  • io full avg10=74,28: en els últims 10 segons, la màquina no ha progressat durant gairebé tres quartes parts del temps per esperar el disc.

Ordre següent: pidstat -d 1 5 (o iotop -oPa), per atribuir aquesta E/S a un procés concret. Després, filefrag sobre els fitxers implicats i cat /proc/<pid>/io, per entendre si el patró aleatori ve de l'accés del programa o de la fragmentació del fitxer.

Solució 2

A: no hi ha problema. Un disponible de gairebé 6 GB indica marge de sobres; el lliure baix és el comportament normal de la memòria cau de pàgines. Els 2.100 MB a la swap són històric: pàgines expulsades fa temps que ningú no ha tornat a necessitar. Ho demostren si=0 i so=0 —no hi ha trànsit d'intercanvi ara— i una pressió de memòria del 0,4 %, és a dir, soroll. Acció: cap, llevat d'anotar el valor a la línia base. Buidar la swap «per estètica» amb swapoff -a && swapon -a és contraproduent: força a portar a la RAM pàgines que ningú no fa servir.

B: problema real i greu. Un disponible de només 1.980 MB, i sobretot si=1.840 i so=2.310 pàgines per segon, o sigui de l'ordre de 7 i 9 MB/s entrant i sortint simultàniament: això és thrashing, el sistema mou pàgines en tots dos sentits perquè el conjunt de treball no cap a la RAM. La pressió del 63,7 % confirma que s'està perdent gairebé dos terços del temps. Accions, per ordre: identificar el consumidor amb ps -eo pid,rss,comm --sort=-rss | head; comprovar a dmesg si l'OOM killer ja ha actuat; mirar l'evolució del RSS per distingir una fuita d'una càrrega legítima; com a mitigació immediata, aplicar o ajustar MemoryMax a la unitat del servei culpable (07-02) per acotar el dany; i com a solució de fons, corregir la fuita o dimensionar la màquina. Abaixar vm.swappiness no arregla res aquí: el problema no és que es faci servir la swap, és que no hi ha prou memòria.

Solució 3

(a) «CPU > 85 % durant 1 minut» → «El p99 de latència de /v1/lectures supera 1 s durant 10 minuts». El problema de l'original és que mesura un recurs, no un dany: una màquina ben dimensionada ha d'estar alta de CPU, i un servei pot anar fatal amb la CPU al 20 % si el coll és al disc. A més, un minut és massa curt i dispara amb qualsevol pic legítim, com l'arrencada de l'agregador. La versió nova mesura el que pateix l'usuari i la seva finestra de 10 minuts filtra els transitoris. Com a alerta secundària i de menor prioritat, cpu some avg300 > 40 % sí que és defensable, perquè mesura temps perdut i no utilització.

(b) «Hi ha memòria d'intercanvi en ús» → «si+so > 0 de manera sostinguda durant 5 minuts», o millor «memory full avg60 > 10 %». Que hi hagi pàgines a la swap no significa res, com demostra la màquina A de l'exercici anterior: l'alerta original dispararia en màquines perfectament sanes i acabaria silenciada. El que fa mal és el trànsit d'intercanvi, o directament el temps perdut que mesura PSI, que a més és comparable entre màquines diferents.

(c) «El procés meteo-api no s'està executant» → «meteo-api.service porta 5 minuts sense assolir l'estat actiu (o ha entrat en failed)». L'original és soroll garantit: cada reinici legítim, cada desplegament i cada Restart=on-failure que funciona com cal generarien una alerta a les tres de la matinada per alguna cosa que el sistema ja ha resolt sol. La nova només dispara quan la recuperació automàtica ha fallat, que és l'únic cas en què cal un humà. És exactament l'estat estable que aconseguíem amb StartLimitBurst a 07-02, i systemctl list-units --failed és la comprovació que la implementa.

Conclusió

El missatge central d'aquesta lliçó és que el mètode val més que les eines. Saps distingir un símptoma d'una causa, quantificar abans de tocar, canviar una cosa cada vegada i recolzar-te en una línia base; i tens dos marcs amb nom: USE per recórrer els recursos amb utilització, saturació i errors —recordant que la saturació és la mètrica que no té sostre i que gairebé ningú no mira—, i RED per mesurar el servei des de fora amb peticions, errors i durada.

Sobre això has après a llegir els números de debò, que gairebé sempre signifiquen una cosa diferent del que semblen. La càrrega mitjana inclou els processos en estat D i per això no és utilització de CPU. %wa és temps ociós amb E/S pendent, així que un valor baix no descarta un problema de disc. free confon perquè la memòria cau no és memòria ocupada i la xifra bona és disponible; l'intercanvi fa mal quan hi ha si/so, no quan hi ha swpd. %util enganya en NVMe i les mètriques fiables són await i aqu-sz. En un socket d'escolta, Recv-Q són connexions esperant accept() i delata l'aplicació, no la xarxa. I PSI mesura directament el temps perdut, en unitats comparables entre màquines, que és el que converteix una alerta en una cosa amb sentit.

Has vist també el cost d'observar: strace alenteix entre 10 i 100 vegades perquè intercepta, mentre que perf mostreja a 99 Hz amb un 1-2 % i eBPF agrega dins del nucli; i has pujat de la màquina a la flota, amb exportadors, sèries temporals, cardinalitat, percentils, alertes basades en símptomes i pressupostos d'error derivats d'un SLO. Al laboratori has provocat les tres saturacions i n'has anotat la signatura, inclosa la comparació que resumeix el mòdul: lectura aleatòria de 4 KB i lectura seqüencial d'1 MB donen el mateix %util i experiències d'usuari incomparables.

Ja tens les tres peces: la interfície per actuar (07-01), el control del que corre i quan (07-02) i el mètode per saber què passa. Falta ajuntar-ho tot sota pressió, amb dades incompletes, a una hora dolenta i amb la incertesa real de no saber per endavant quina és la resposta.

És l'última lliçó del curs: Cas Pràctic Final: Diagnosticar un Servidor en Producció. Són les 03:12 i acaba d'arribar un avís.

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