Durant tot el mòdul has treballat amb una suposició implícita: que cada nom correspon a un fitxer i cada fitxer té un nom. Aquesta suposició és falsa, i entendre per què és el que separa qui fa servir Linux de qui el comprèn.
En un sistema de fitxers Unix, el nom i el contingut són coses separades. El contingut viu en una estructura anomenada inode; el nom és simplement una entrada en un directori que apunta a aquest inode. Res no impedeix que dues entrades apuntin al mateix lloc, ni que existeixi un fitxer el contingut del qual sigui la ruta d'un altre. D'aquí surten els dos tipus d'enllaços d'aquesta lliçó.
No és teoria de sistemes de fitxers per curiositat. Els enllaços són per tot arreu en un sistema Linux: /bin és un enllaç, el teu python3 és un enllaç, i el sistema amb què Debian i Ubuntu decideixen quina versió d'una eina es fa servir està construït sobre enllaços. I al final de la lliçó dissenyaràs el patró amb què Tramontana Reserves convertirà un desplegament —i una marxa enrere— en un canvi d'enllaç instantani, sense finestra de servei caigut.
Contingut
- Què és un inode
- Veure l'inode i el comptador d'enllaços
- Enllaços durs amb
ln - Les dues limitacions dels enllaços durs
- Enllaços simbòlics amb
ln -s - Absoluts enfront de relatius
- Enllaços trencats
- Quines ordres segueixen l'enllaç i quines no
- Dur enfront de simbòlic: la taula
- Enllaços en un sistema Linux real
update-alternatives- El patró de desplegament de Tramontana
- Què és un inode
Quan deses un fitxer, el sistema de fitxers crea tres coses diferents:
- Els blocs de dades: el contingut en si, repartit pel disc.
- L'inode: una estructura amb totes les metadades del fitxer —permisos, propietari, grup, mida, marques de temps, comptador d'enllaços i les adreces dels blocs de dades—. Cada inode té un número únic dins del seu sistema de fitxers.
- L'entrada de directori: un parell
(nom, número d'inode)emmagatzemat al directori.
Fixa't en el que no conté l'inode: el nom del fitxer. El nom no és una propietat del fitxer; és una propietat del directori que el conté.
flowchart LR
subgraph DIR["Directori /home/operador/dades"]
E1["'cases.txt' -> 262149"]
E2["'reserves.csv' -> 262151"]
end
subgraph INO["Taula d'inodes"]
I1["Inode 262149<br/>permisos, propietari, dates<br/>enllaços: 1<br/>punters a blocs"]
I2["Inode 262151<br/>permisos, propietari, dates<br/>enllaços: 1<br/>punters a blocs"]
end
subgraph BLQ["Blocs de dades"]
B1["mas-figueres;Mas Figueres..."]
B2["id;data;casa;hoste..."]
end
E1 --> I1 --> B1
E2 --> I2 --> B2
Un directori, vist així, no conté fitxers: conté una llista de noms amb números d'inode. És un índex.
Aquesta arquitectura explica de cop diverses coses que fins ara eren fets solts:
| Fet | Explicació per l'inode |
|---|---|
mv dins del mateix disc és instantani |
Només canvia l'entrada de directori; l'inode i els blocs no es toquen |
. té el mateix número que el seu directori |
Són dos noms per al mateix inode (ho vas comprovar a 02-03) |
El nom no és a stat |
Perquè no és a l'inode |
| Pots esborrar un fitxer que un altre procés està llegint | Es treu el nom, però l'inode sobreviu mentre algú el tingui obert |
df -i es pot esgotar amb espai lliure |
Els inodes són un recurs finit, fixat en formatar |
Aquest penúltim punt és l'explicació definitiva del misteri de la lliçó 02-03: esborres un registre de 2 GB, du deixa de veure'l, però df continua dient que l'espai està ocupat. El nom ha desaparegut; l'inode continua viu perquè el procés el té obert, i amb ell els seus blocs. Fins que el procés no tanqui el fitxer —o el reiniciïs— no s'allibera res.
- Veure l'inode i el comptador d'enllaços
Dues eines que ja coneixes, ara amb la mirada posada als enllaços.
operador@srv-tramontana:~$ ls -li dades/
total 8
262149 -rw-r----- 1 operador operador 418 Aug 17 19:40 cases.txt
262151 -rw-r--r-- 1 operador operador 1204 Aug 18 07:55 reserves.csvLa primera columna amb -i és el número d'inode. La columna que va just després dels permisos —l'1— és el comptador d'enllaços: quants noms apunten a aquest inode.
stat ho diu amb totes les lletres:
operador@srv-tramontana:~$ stat dades/cases.txt
File: dades/cases.txt
Size: 418 Blocks: 8 IO Block: 4096 regular file
Device: 8,2 Inode: 262149 Links: 1
Access: (0640/-rw-r-----) Uid: ( 1001/operador) Gid: ( 1001/operador)Inode: 262149 i Links: 1. Aquest comptador és el que governa quan s'allibera l'espai d'un fitxer, com veuràs a l'apartat següent.
I ara s'entén el comportament dels directoris que va quedar pendent a la lliçó 02-03:
operador@srv-tramontana:~$ ls -ldi /opt/tramontana/app
131074 drwxr-xr-x 4 root root 4096 Aug 18 08:30 /opt/tramontana/appComptador 4, no 1. Els quatre noms que apunten a aquest inode són:
- L'entrada
appdins de/opt/tramontana. - L'entrada
.dins de/opt/tramontana/app. - L'entrada
..dins de/opt/tramontana/app/plantilles. - L'entrada
..dins d'un altre subdirectori.
D'aquí la fórmula: enllaços d'un directori = 2 + nombre de subdirectoris. Un directori sense subdirectoris en té 2; un amb set subdirectoris en té 9.
Un número d'inode només té sentit dins del seu sistema de fitxers. Dos fitxers en discos diferents poden tenir el mateix número sense cap relació. Per això, per comparar, cal mirar també el dispositiu:
operador@srv-tramontana:~$ stat -c '%d:%i %n' dades/cases.txt /boot/vmlinuz
2049:262149 dades/cases.txt
2049:131331 /boot/vmlinuzEl parell dispositiu:inode sí que identifica un fitxer de manera única a tota la màquina.
- Enllaços durs amb
ln
lnUn enllaç dur és, senzillament, una altra entrada de directori que apunta al mateix inode. No és una còpia ni un accés directe: és un altre nom igual de legítim per al mateix fitxer.
operador@srv-tramontana:~$ ln dades/cases.txt dades/allotjaments.txt
operador@srv-tramontana:~$ ls -li dades/
total 12
262149 -rw-r----- 2 operador operador 418 Aug 17 19:40 allotjaments.txt
262149 -rw-r----- 2 operador operador 418 Aug 17 19:40 cases.txt
262151 -rw-r--r-- 1 operador operador 1204 Aug 18 07:55 reserves.csvTres coses per confirmar en aquesta sortida:
- Mateix inode (262149) a les dues primeres línies.
- El comptador ha pujat a 2 a totes dues: hi ha dos noms apuntant-hi.
- La mida es mostra dues vegades (418 i 418), però el fitxer ocupa 418 bytes en total, no 836.
lsmostra la mida de l'inode, i l'inode és el mateix.
Aquest últim punt té una conseqüència pràctica: du compta l'espai una sola vegada si troba els dos noms al mateix recorregut, però el pot comptar dues vegades si són en recorreguts diferents. És una font clàssica de descaires en calcular la mida de còpies de seguretat amb enllaços durs.
No hi ha original ni còpia. Els dos noms són exactament equivalents; el sistema no guarda quin es va crear primer. Modificar-ne un modifica l'altre, perquè són el mateix fitxer:
operador@srv-tramontana:~$ echo "el-moli;El Molí;Ripollès;5" >> dades/allotjaments.txt
operador@srv-tramontana:~$ tail -n 1 dades/cases.txt
el-moli;El Molí;Ripollès;5I el mateix amb les metadades: chmod sobre un canvia els permisos de l'altre, perquè els permisos són a l'inode.
Què passa en esborrar
Aquí hi ha el comportament que cal entendre bé:
operador@srv-tramontana:~$ rm dades/cases.txt
operador@srv-tramontana:~$ ls -li dades/
262149 -rw-r----- 1 operador operador 446 Aug 18 13:02 allotjaments.txt
262151 -rw-r--r-- 1 operador operador 1204 Aug 18 07:55 reserves.csv
operador@srv-tramontana:~$ cat dades/allotjaments.txt
mas-figueres;Mas Figueres;Girona;6
...
el-moli;El Molí;Ripollès;5El contingut continua intacte. rm no esborra fitxers: decrementa el comptador d'enllaços i elimina el nom. L'inode i els seus blocs només s'alliberen quan passen dues coses alhora:
- El comptador d'enllaços arriba a 0.
- Cap procés no té el fitxer obert.
Per això l'ordre de baix nivell es diu unlink(), no delete(). rm és literalment «desenllaçar».
Els tres desenllaços possibles d'un rm:
| Situació després de decrementar el comptador | Què passa amb les dades |
|---|---|
| Comptador > 0 | Continuen accessibles pels altres noms. No s'allibera res |
| Comptador = 0 i ningú no el té obert | S'alliberen l'inode i els blocs. El fitxer desapareix |
| Comptador = 0 però algun procés el té obert | Continuen al disc, sense nom però vius: du no els veu, df sí que els compta |
La tercera fila és la del misteri de l'espai que no s'allibera. I també és un mecanisme que es fa servir expressament: un programa que necessita un fitxer temporal el pot crear, obrir i esborrar immediatament. El fitxer continua funcionant mentre el programa el tingui obert, i desapareix automàticament quan el programa acaba, fins i tot si es tanca de manera anormal. No cal netejar res.
- Les dues limitacions dels enllaços durs
No poden creuar sistemes de fitxers
operador@srv-tramontana:~$ ln dades/allotjaments.txt /boot/prova.txt
ln: failed to create hard link '/boot/prova.txt' => 'dades/allotjaments.txt':
Invalid cross-device linkEl perquè: un enllaç dur és una entrada (nom, número d'inode). El número d'inode només té significat dins del seu sistema de fitxers: l'inode 262149 de /dev/sda2 no té res a veure amb l'inode 262149 de /dev/sda1. Una entrada en un directori de /boot que digués «262149» apuntaria a l'inode 262149 de /boot, que és un fitxer completament diferent.
No és una restricció arbitrària: és que la referència no significa res fora del seu àmbit.
Com saber si dues rutes són al mateix sistema de fitxers, abans d'intentar-ho:
operador@srv-tramontana:~$ df --output=source /home/operador /opt/tramontana /boot
Filesystem
/dev/sda2
/dev/sda2
/dev/sda1/home i /opt comparteixen /dev/sda2: s'hi pot enllaçar entre ells. /boot és a /dev/sda1: no.
No es poden fer a directoris
operador@srv-tramontana:~$ ln /opt/tramontana/app /tmp/enllac-app
ln: /opt/tramontana/app: hard link not allowed for directoryNi tan sols root pot fer-ho a Linux. El perquè és més interessant:
L'arbre de directoris d'Unix és, per disseny, un graf acíclic: es pot baixar i pujar, però no fer voltes en cercle. Si poguessis crear un enllaç dur a un directori, podries construir un bucle:
Conseqüències immediates:
find,du,tar,rsynci qualsevol recorregut recursiu entrarien en un bucle infinit, perquè no hi hauria manera de detectar que ja hi han passat sense portar registre de cada inode visitat.- El comptador d'enllaços deixaria de servir per saber quan alliberar l'espai: un cicle de directoris que es referencien mútuament tindria comptadors majors que zero encara que ningú no hi pogués arribar des de l'arrel. Serien escombraries inabastables i impossibles de recollir.
..deixaria de tenir sentit únic: un directori amb dos pares no pot tenir un sol...
Les entrades . i .. són l'excepció: són enllaços durs a directoris, creats pel nucli, i per això els comptadors dels directoris valen més d'1. Però estan controlats i la seva topologia és coneguda, així que no trenquen res.
Totes dues limitacions desapareixen amb l'altre tipus d'enllaç.
- Enllaços simbòlics amb
ln -s
ln -sUn enllaç simbòlic (o symlink, o enllaç tou) és un fitxer de ple dret, amb el seu propi inode, el contingut del qual és una ruta de text. Quan alguna cosa l'obre, el nucli llegeix aquesta ruta i redirigeix l'operació.
operador@srv-tramontana:~$ ln -s /home/operador/dades/reserves.csv ~/reserves-actual.csv
operador@srv-tramontana:~$ ls -li ~/reserves-actual.csv
262180 lrwxrwxrwx 1 operador operador 36 Aug 18 13:20 reserves-actual.csv -> /home/operador/dades/reserves.csvTot el que cal llegir-hi:
| Element | Què significa |
|---|---|
262180 |
Inode propi i diferent del destí (262151) |
l inicial |
Tipus enllaç simbòlic, la l que vas veure al Mòdul 1 |
rwxrwxrwx |
Els permisos de l'enllaç són sempre aquests i no signifiquen res: els que manen són els del destí |
1 |
Comptador d'enllaços del mateix enllaç |
36 |
La mida és la longitud de la ruta, 36 caràcters |
-> /home/... |
La ruta que conté |
Aquesta mida confirma què és un symlink: un fitxer el contingut del qual és una cadena de text.
En fer-lo servir, tot funciona com si fos el destí:
operador@srv-tramontana:~$ head -n 2 ~/reserves-actual.csv
id;data;casa;hoste;nits;import
1001;2026-07-03;mas-figueres;Nuria Prat;4;620.00I les dues limitacions de l'enllaç dur no existeixen:
operador@srv-tramontana:~$ ln -s /boot/vmlinuz ~/nucli-actual
operador@srv-tramontana:~$ ls -l ~/nucli-actual
lrwxrwxrwx 1 operador operador 13 Aug 18 13:24 nucli-actual -> /boot/vmlinuz
operador@srv-tramontana:~$ ln -s /opt/tramontana/app ~/app-produccio
operador@srv-tramontana:~$ ls -ld ~/app-produccio
lrwxrwxrwx 1 operador operador 19 Aug 18 13:25 app-produccio -> /opt/tramontana/appCreua sistemes de fitxers i apunta a directoris sense problema, perquè no referencia un inode: referencia una ruta, i una ruta és text que es resol en el moment de fer-la servir.
Un enllaç a un directori es comporta com el directori:
operador@srv-tramontana:~$ ls ~/app-produccio/
executable plantilles version.txt
operador@srv-tramontana:~$ cd ~/app-produccio
operador@srv-tramontana:~/app-produccio$ pwd
/home/operador/app-produccio
operador@srv-tramontana:~/app-produccio$ pwd -P
/opt/tramontana/appAquí tens l'explicació de pwd -P que va quedar pendent a la lliçó 02-03: pwd mostra la ruta lògica, la que vas fer servir per arribar-hi; pwd -P la física, resolent els enllaços.
Opcions de ln que convé conèixer:
| Opció | Què fa |
|---|---|
-s |
Crea un enllaç simbòlic (sense ella, dur) |
-f |
Força: reemplaça l'enllaç si ja existeix |
-n |
Tracta un enllaç a directori existent com a fitxer, no hi entra |
-r |
Crea l'enllaç amb una ruta relativa calculada automàticament |
-v |
Informa del que fa |
-T |
Tracta el destí sempre com un nom, mai com un directori |
La combinació -sfn és la del desplegament i la veuràs en acció a l'apartat 12. Sense -n, reemplaçar un enllaç que apunta a un directori crea l'enllaç nou dins d'aquest directori en comptes de substituir-lo, que és un error molt fàcil de cometre.
- Absoluts enfront de relatius
El contingut d'un enllaç simbòlic és una ruta, i aquesta ruta pot ser dels dos tipus.
operador@srv-tramontana:~$ cd /opt/tramontana
# Absolut
operador@srv-tramontana:/opt/tramontana$ sudo ln -s /opt/tramontana/releases/3.2.1 app-abs
# Relatiu
operador@srv-tramontana:/opt/tramontana$ sudo ln -s releases/3.2.1 app-rel
operador@srv-tramontana:/opt/tramontana$ ls -l app-*
lrwxrwxrwx 1 root root 30 Aug 18 13:40 app-abs -> /opt/tramontana/releases/3.2.1
lrwxrwxrwx 1 root root 16 Aug 18 13:40 app-rel -> releases/3.2.1Tots dos funcionen igual ara mateix. Es diferencien en què passa quan alguna cosa es mou.
Un enllaç relatiu es resol des del directori on és l'enllaç, no des d'on siguis tu:
Funciona des del teu home perquè releases/3.2.1 es resol relatiu a /opt/tramontana/, que és on viu l'enllaç.
Ara, la prova que els distingeix: copiar tot l'arbre a un altre lloc.
operador@srv-tramontana:~$ sudo cp -a /opt/tramontana /srv/tramontana/copia-completa
operador@srv-tramontana:~$ ls -l /srv/tramontana/copia-completa/app-*
lrwxrwxrwx 1 root root 30 Aug 18 13:40 app-abs -> /opt/tramontana/releases/3.2.1
lrwxrwxrwx 1 root root 16 Aug 18 13:40 app-rel -> releases/3.2.1app-abscontinua apuntant a/opt/tramontana/..., és a dir, a l'arbre original. La còpia no és autocontinguda: depèn de l'original. Si esborres/opt/tramontana, la còpia queda trencada.app-relapunta areleases/3.2.1dins de la còpia. L'arbre copiat és autònom i coherent.
| Criteri | Absolut | Relatiu |
|---|---|---|
| Sobreviu a moure l'enllaç | No | Sí, si es mou tot el conjunt |
| Sobreviu a moure el destí | No | No |
| Copiar l'arbre complet | Es trenca la independència | Continua coherent |
Funciona dins d'un chroot o contenidor |
No (l'arrel canvia) | Sí |
Llegibilitat en fer ls -l |
Molt clara | Cal calcular |
| Robust davant d'un muntatge en un altre punt | No | Sí |
Criteri d'ús:
- Relatius dins d'un mateix arbre d'aplicació (
/opt/tramontana/app→releases/...), perquè el conjunt es copia, se'n fa còpia de seguretat i es restaura com una unitat. - Absoluts quan el destí és en una altra part del sistema i no té relació estructural amb l'enllaç (per exemple, un enllaç al teu home cap a
/var/log/tramontana).
ln -r calcula el relatiu per tu:
operador@srv-tramontana:~$ cd /opt/tramontana
operador@srv-tramontana:/opt/tramontana$ sudo ln -sr /opt/tramontana/releases/3.2.1 app-auto
operador@srv-tramontana:/opt/tramontana$ ls -l app-auto
lrwxrwxrwx 1 root root 16 Aug 18 13:47 app-auto -> releases/3.2.1Li dones rutes absolutes, que són fàcils d'escriure amb Tab sense equivocar-se, i ell genera l'enllaç relatiu correcte. És la millor de les dues opcions.
- Enllaços trencats
Com que un symlink guarda una ruta i no una referència a l'inode, res no garanteix que el destí existeixi. Pots crear un enllaç a alguna cosa que encara no existeix, i el destí pot desaparèixer després.
operador@srv-tramontana:~$ ln -s /opt/tramontana/releases/9.9.9 ~/futur
operador@srv-tramontana:~$ ls -l ~/futur
lrwxrwxrwx 1 operador operador 33 Aug 18 13:52 futur -> /opt/tramontana/releases/9.9.9
operador@srv-tramontana:~$ cat ~/futur
cat: /home/operador/futur: No such file or directoryls -l mostra l'enllaç sense queixar-se, perquè l'enllaç existeix perfectament: és el destí el que no. Només en intentar fer-lo servir apareix l'error, i el missatge esmenta el nom de l'enllaç, cosa que confon: sembla que no existeix ~/futur quan ~/futur és allà.
Amb color activat, ls mostra els enllaços trencats en vermell parpellejant, que és difícil de passar per alt. Per detectar-los de manera fiable:
# Un enllaç concret
operador@srv-tramontana:~$ ls -lL ~/futur
ls: cannot access '/home/operador/futur': No such file or directory
# Tots els enllaços trencats d'un arbre
operador@srv-tramontana:~$ find ~ -xtype l
/home/operador/futur
# Amb més context
operador@srv-tramontana:~$ find ~ -xtype l -exec ls -l {} \;
lrwxrwxrwx 1 operador operador 33 Aug 18 13:52 /home/operador/futur -> /opt/tramontana/releases/9.9.9find -xtype l significa «elements que, en resoldre l'enllaç, són de tipus enllaç», cosa que només passa si l'enllaç no es pot resoldre. find s'estudia a fons a la lliçó 03-03; aquesta invocació ja la pots adoptar com a recepta.
I readlink és l'eina específica per inspeccionar enllaços:
operador@srv-tramontana:~$ readlink ~/futur
/opt/tramontana/releases/9.9.9
operador@srv-tramontana:~$ readlink -f ~/reserves-actual.csv
/home/operador/dades/reserves.csv
operador@srv-tramontana:~$ readlink -e ~/futur
operador@srv-tramontana:~$ echo $?
1| Opció | Què fa |
|---|---|
| (cap) | Mostra el contingut literal de l'enllaç |
-f |
Resol tota la cadena d'enllaços; no falla si l'últim no existeix |
-e |
Com -f, però falla si el destí no existeix |
readlink -e és el verificador: codi 0 si l'enllaç porta a alguna cosa real, diferent de 0 si està trencat. És exactament el que faràs servir a l'script de desplegament per comprovar que l'enllaç app apunta a un release existent abans d'arrencar el servei.
Un enllaç trencat no sempre és un error. És una situació normal quan el destí es crearà més tard, o quan apunta a un dispositiu extraïble que ara no està muntat. El que no hi ha d'haver són enllaços trencats que ningú no sap per què hi són.
- Quines ordres segueixen l'enllaç i quines no
Aquesta és la part que provoca més errors a la pràctica: cada ordre decideix pel seu compte si actua sobre l'enllaç o sobre el seu destí.
| Ordre | Comportament per defecte | Com canviar-lo |
|---|---|---|
cat, less, editors |
Segueixen l'enllaç: llegeixen i escriuen el destí | — |
ls |
Mostra l'enllaç | -L per mostrar el destí |
ls -l d'un directori enllaçat |
Llista el contingut del destí | -d per veure l'enllaç |
cp |
Segueix: copia el contingut del destí | -P o -d copia l'enllaç com a tal |
cp -a |
No segueix: copia els enllaços com a enllaços | -L per seguir-los |
mv |
Mou l'enllaç, no el destí | — |
rm |
Esborra l'enllaç, mai el destí | — |
chmod, chown |
Segueixen: canvien el destí | chown -h canvia l'enllaç |
du |
Compta l'enllaç (uns bytes) | -L per comptar el destí |
find |
No segueix | -L per seguir |
tar |
Guarda els enllaços com a enllaços | -h per guardar el contingut |
rsync -a |
Copia els enllaços com a enllaços | -L per copiar el contingut |
Els tres casos que cal tenir clars:
rm sobre un enllaç esborra l'enllaç. Sempre. És segur:
operador@srv-tramontana:~$ rm ~/reserves-actual.csv
operador@srv-tramontana:~$ ls dades/reserves.csv
dades/reserves.csvEl destí continua intacte. Aquest és el comportament que la gent tem i que en realitat mai no falla.
cp sense -a desfà els enllaços:
operador@srv-tramontana:~$ ln -s /opt/tramontana/app/version.txt ~/v.txt
operador@srv-tramontana:~$ cp ~/v.txt /tmp/v-copia.txt
operador@srv-tramontana:~$ ls -l /tmp/v-copia.txt
-rw-rw-r-- 1 operador operador 26 Aug 18 14:02 /tmp/v-copia.txt
operador@srv-tramontana:~$ cp -P ~/v.txt /tmp/v-enllac.txt
operador@srv-tramontana:~$ ls -l /tmp/v-enllac.txt
lrwxrwxrwx 1 operador operador 36 Aug 18 14:02 /tmp/v-enllac.txt -> /opt/tramontana/app/version.txtcp normal va produir un fitxer real amb el contingut; cp -P va produir un enllaç. En copiar un arbre amb enllaços, aquesta diferència decideix si obtens una còpia fidel o una còpia inflada on cada enllaç s'ha convertit en una còpia completa del fitxer.
El parany de la barra final amb enllaços a directoris. Aquest mossega tothom:
operador@srv-tramontana:~$ ln -s /opt/tramontana/app ~/app-enllac
# SENSE barra: el mateix enllaç
operador@srv-tramontana:~$ ls -ld ~/app-enllac
lrwxrwxrwx 1 operador operador 19 Aug 18 14:05 /home/operador/app-enllac -> /opt/tramontana/app
# AMB barra: el DIRECTORI al qual apunta
operador@srv-tramontana:~$ ls -ld ~/app-enllac/
drwxr-xr-x 4 root root 4096 Aug 18 08:30 /home/operador/app-enllac/La barra final significa «tracta'l com a directori», i això obliga el nucli a resoldre l'enllaç. I ara el perillós:
# Esborra NOMÉS l'enllaç. El destí intacte.
operador@srv-tramontana:~$ rm ~/app-enllac
# Amb barra final: error, perquè rm no esborra directoris sense -r
operador@srv-tramontana:~$ rm ~/app-enllac/
rm: cannot remove '/home/operador/app-enllac/': Is a directory
# AIXÒ SÍ QUE ESBORRA EL CONTINGUT DEL DESTÍ REAL
operador@srv-tramontana:~$ rm -rf ~/app-enllac/Aquesta última línia, amb -rf i barra final, travessa l'enllaç i esborra el contingut de /opt/tramontana/app. L'aplicació de producció, eliminada per una barra de més.
Regla d'or: no posis mai barra final després del nom d'un enllaç en una ordre destructiva. I en general, abans d'un rm -rf sobre alguna cosa que podria ser un enllaç, comprova:
operador@srv-tramontana:~$ ls -ld ~/app-enllac
operador@srv-tramontana:~$ readlink -f ~/app-enllac
/opt/tramontana/app
- Dur enfront de simbòlic: la taula
| Característica | Enllaç dur | Enllaç simbòlic |
|---|---|---|
| Què és | Una altra entrada de directori al mateix inode | Un fitxer el contingut del qual és una ruta |
| Inode | El mateix que el destí | Propi i diferent |
| Es crea amb | ln origen enllac |
ln -s origen enllac |
A ls -l |
Indistingible d'un fitxer normal | Comença per l, amb -> |
| Mida | La del fitxer | La longitud de la ruta |
| Comptador d'enllaços del destí | Augmenta | No canvia |
| Creua sistemes de fitxers | No | Sí |
| Apunta a directoris | No | Sí |
| Si s'esborra el destí | Les dades continuen accessibles | L'enllaç queda trencat |
| Si es mou el destí | Continua funcionant | Es trenca (tret dels relatius coherents) |
| Permisos | Els de l'inode compartit | lrwxrwxrwx, irrellevants |
| Detectar el destí | No es pot saber quin era «l'original» | readlink |
| Pot apuntar a alguna cosa inexistent | No | Sí |
| Cost al disc | Zero (només l'entrada) | Un inode i uns bytes |
| Ús típic | Còpies amb rsync --link-dest, deduplicació |
Tota la resta |
Quan triar cadascun, a la pràctica:
Fes servir enllaços simbòlics per defecte. Són visibles a ls -l, diuen on apunten, creuen sistemes de fitxers, funcionen amb directoris i es poden reemplaçar de manera atòmica. El 95 % dels enllaços que crearàs i que trobaràs en un sistema Linux són simbòlics.
Fes servir enllaços durs en dos casos concrets:
- Còpies de seguretat incrementals amb deduplicació.
rsync --link-destcrea una còpia completa aparent de cada dia, però els fitxers que no han canviat són enllaços durs als del dia anterior: ocupen zero espai addicional. És la base de les còpies tipus Time Machine i ho veuràs a la lliçó 05-08. - Quan necessites que el fitxer sobrevisqui a l'esborrat del nom original, sense que quedi un enllaç trencat.
- Enllaços en un sistema Linux real
Els enllaços no són una curiositat: són estructura del sistema.
La mateixa arrel:
operador@srv-tramontana:~$ ls -l / | grep '^l'
lrwxrwxrwx 1 root root 7 Apr 22 2024 bin -> usr/bin
lrwxrwxrwx 1 root root 7 Apr 22 2024 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Apr 22 2024 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 8 Apr 22 2024 sbin -> usr/sbinÉs la reorganització coneguda com a /usr-merge: els binaris es van consolidar sota /usr i es van deixar enllaços a l'arrel perquè els milers de referències existents a /bin/bash continuessin funcionant. Això és el que veies amb la fletxa a tree -L 1 / a la lliçó 02-03.
Fixa't que són relatius (usr/bin, no /usr/bin). Deliberadament: així continuen sent vàlids si l'arbre es munta en un altre punt, per exemple en reparar el sistema des d'un disc de rescat muntat a /mnt.
L'intèrpret de Python:
operador@srv-tramontana:~$ ls -l /usr/bin/python3
lrwxrwxrwx 1 root root 10 Aug 2 12:04 /usr/bin/python3 -> python3.12python3 és un enllaç a la versió concreta. Actualitzar Python a la 3.13 és, en essència, canviar aquest enllaç: tots els scripts que comencen per #!/usr/bin/python3 passen a fer servir la versió nova sense tocar-ne ni un.
Cadenes d'enllaços:
operador@srv-tramontana:~$ ls -l /usr/bin/vi
lrwxrwxrwx 1 root root 20 Aug 18 09:00 /usr/bin/vi -> /etc/alternatives/vi
operador@srv-tramontana:~$ ls -l /etc/alternatives/vi
lrwxrwxrwx 1 root root 17 Aug 18 09:00 /etc/alternatives/vi -> /usr/bin/vim.basic
operador@srv-tramontana:~$ readlink -f /usr/bin/vi
/usr/bin/vim.basicDos salts. readlink -f segueix la cadena sencera fins al final. El nucli permet fins a uns 40 nivells d'imbricació abans de donar Too many levels of symbolic links.
Els serveis de systemd:
operador@srv-tramontana:~$ ls -l /etc/systemd/system/multi-user.target.wants/ | head -n 4
lrwxrwxrwx 1 root root 44 Jul 1 10:22 ssh.service -> /lib/systemd/system/ssh.service
lrwxrwxrwx 1 root root 48 Jul 1 10:22 cron.service -> /lib/systemd/system/cron.serviceHabilitar un servei a systemd és literalment crear un enllaç simbòlic, i deshabilitar-lo és esborrar-lo. Quan executis systemctl enable a la lliçó 05-05, el que passarà per sota és un ln -s.
Els registres rotats:
operador@srv-tramontana:~$ ls -l /var/log/tramontana/
-rw-r----- 1 root adm 18K Aug 18 09:14 acces.log
-rw-r----- 1 root adm 2.1K Aug 17 23:59 acces.log.1.gzAquí no hi ha enllaços, però convé saber que alguns serveis mantenen current.log com a enllaç al fitxer del dia.
update-alternatives
update-alternativesDebian i Ubuntu resolen amb enllaços un problema concret: diversos paquets que proporcionen la mateixa funció. Quin editor obre sudo editor? Quin java s'executa si n'hi ha tres d'instal·lades? Què és vi si hi ha vim, nvi i elvis?
El mecanisme és una doble capa d'enllaços simbòlics:
La primera capa és fixa i és al PATH. La segona, a /etc/alternatives/, és la que es canvia. Així l'administrador no toca mai /usr/bin, que pertany als paquets.
operador@srv-tramontana:~$ update-alternatives --display editor
editor - auto mode
link best version is /bin/nano
link currently points to /bin/nano
link editor is /usr/bin/editor
/bin/nano - priority 40
/usr/bin/vim.basic - priority 30Canviar-ho:
operador@srv-tramontana:~$ sudo update-alternatives --config editor
There are 2 choices for the alternative editor (providing /usr/bin/editor).
Selection Path Priority Status
------------------------------------------------------------
* 0 /bin/nano 40 auto mode
1 /bin/nano 40 manual mode
2 /usr/bin/vim.basic 30 manual mode
Press <enter> to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in manual modeoperador@srv-tramontana:~$ ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 18 Aug 18 14:20 /etc/alternatives/editor -> /usr/bin/vim.basicL'enllaç de la segona capa ha canviat. Ordres principals:
| Ordre | Què fa |
|---|---|
--display <nom> |
Mostra l'estat i les opcions |
--config <nom> |
Triar interactivament |
--list <nom> |
Només les rutes disponibles |
--set <nom> <ruta> |
Fixar sense interacció (per a scripts) |
--auto <nom> |
Tornar al mode automàtic per prioritat |
Per què això importa per a l'EDITOR de la teva sessió: quan executis sudo visudo o crontab -e, l'editor que s'obre el decideix la variable d'entorn EDITOR i, si no està definida, l'alternativa editor. Si no has configurat mai res i t'apareix nano, ara saps per què i com canviar-ho. La variable EDITOR és de la lliçó 03-01.
- El patró de desplegament de Tramontana
Aquí és on tot l'anterior es converteix en una decisió d'arquitectura. És el patró que Tramontana Reserves adoptarà i que reapareixerà a la lliçó 04-07 i al Mòdul 8.
El problema
Avui, el desplegament d'una versió nova significaria: aturar el servei, esborrar o moure /opt/tramontana/app, copiar la versió nova, arrencar. Problemes:
- Finestra d'indisponibilitat proporcional al temps de còpia. Copiar 48 MB triga segons; en producció, copiar una release gran pot trigar minuts.
- Estat intermedi incoherent: durant la còpia, el directori té mitja versió vella i mitja de nova. Si el servei arrenca en aquell moment, es comporta de manera impredictible.
- Tornar enrere és un altre desplegament complet, amb la seva pròpia finestra i els seus propis riscos, executat sota pressió perquè alguna cosa ha fallat.
- La versió anterior ja no hi és, així que tornar enrere requereix recuperar-la d'una còpia.
La solució
Separar el contingut (els releases, cadascun al seu directori immutable) de el punter (un enllaç simbòlic que diu quin està actiu).
/opt/tramontana/
├── releases/
│ ├── 3.1.0/ <- versió anterior, intacta
│ ├── 3.2.0/ <- versió anterior, intacta
│ └── 3.2.1/ <- versió nova
├── app -> releases/3.2.1 <- EL PUNTER
└── shared/
└── uploads/ <- dades que sobreviuen als desplegamentsflowchart TD
S["Servei Tramontana<br/>llegeix /opt/tramontana/app"] --> L["app<br/>(enllaç simbòlic)"]
L -.->|"apunta a"| R321["releases/3.2.1<br/>ACTIVA"]
R320["releases/3.2.0<br/>en reserva"]
R310["releases/3.1.0<br/>en reserva"]
L -.->|"un ln -sfn<br/>i apunta aquí"| R320
El desplegament complet:
# 1. Pujar la versió nova al seu propi directori. El servei continua
# corrent sobre 3.2.1 i no se n'assabenta de res.
operador@srv-tramontana:~$ sudo mkdir -p /opt/tramontana/releases/3.3.0
operador@srv-tramontana:~$ sudo tar -xzf /srv/tramontana/enviaments/app-3.3.0.tar.gz \
-C /opt/tramontana/releases/3.3.0 --strip-components=1
# 2. Verificar la versió nova ABANS d'activar-la
operador@srv-tramontana:~$ cat /opt/tramontana/releases/3.3.0/version.txt
Tramontana Reserves 3.3.0
operador@srv-tramontana:~$ ls -l /opt/tramontana/releases/3.3.0/executable
-rwxr-xr-x 1 root root 51203584 Aug 18 14:40 executable
# 3. Deixar constància de què hi havia abans
operador@srv-tramontana:~$ readlink /opt/tramontana/app
releases/3.2.1
# 4. EL DESPLEGAMENT: una sola ordre
operador@srv-tramontana:~$ cd /opt/tramontana
operador@srv-tramontana:/opt/tramontana$ sudo ln -sfn releases/3.3.0 app
# 5. Verificar
operador@srv-tramontana:/opt/tramontana$ ls -l app
lrwxrwxrwx 1 root root 16 Aug 18 14:41 app -> releases/3.3.0
operador@srv-tramontana:/opt/tramontana$ cat app/version.txt
Tramontana Reserves 3.3.0
# 6. Recarregar el servei
operador@srv-tramontana:/opt/tramontana$ sudo systemctl restart tramontanaLa marxa enrere:
operador@srv-tramontana:/opt/tramontana$ sudo ln -sfn releases/3.2.1 app
operador@srv-tramontana:/opt/tramontana$ sudo systemctl restart tramontanaUna ordre. Aquest és tot el rollback. Sense copiar res, sense recuperar d'una còpia de seguretat, sense finestra de manteniment. I es pot executar a les tres de la matinada per algú nerviós sense risc d'equivocar-se.
Per què ln -sfn i no una altra cosa
Les tres lletres importen:
-ssimbòlic, òbviament.-fforça el reemplaçament. Sense ella,lnfalla perquèappja existeix.-nés la crítica. Sense-n,lnveu queappés un enllaç a un directori, hi entra i crea l'enllaç allà dins:
# SENSE -n: el desastre silenciós
operador@srv-tramontana:/opt/tramontana$ sudo ln -sf releases/3.3.0 app
operador@srv-tramontana:/opt/tramontana$ ls -l app/
lrwxrwxrwx 1 root root 15 Aug 18 14:45 3.3.0 -> releases/3.3.0
executable plantilles version.txtHa creat releases/3.2.1/3.3.0 en comptes de canviar app. L'enllaç app continua apuntant a la versió vella, el servei no s'actualitza i a sobre has embrutat el directori del release anterior, que havia de ser immutable. -n sempre.
Per què és «atòmic»
Reemplaçar un enllaç simbòlic es fa, per sota, amb la crida al sistema rename(), que el nucli garanteix com a atòmica: no hi ha cap instant en què app no existeixi o apunti a mitges. Qualsevol procés que obri /opt/tramontana/app en qualsevol moment veurà o la versió vella completa o la nova completa, mai un estat intermedi.
Compara-ho amb l'alternativa ingènua de rm app && ln -s releases/3.3.0 app: entre les dues ordres hi ha una finestra, breu però real, en què app no existeix. Un procés que intenti obrir un fitxer just llavors rebrà un error. Per això el patró és ln -sfn, en una sola ordre.
Avantatges del patró, resumits
| Aspecte | Desplegament clàssic | Patró amb enllaç |
|---|---|---|
| Temps d'indisponibilitat | El que trigui la còpia | El reinici del servei |
| Estat intermedi | Existeix i és incoherent | No existeix |
| Tornar enrere | Un altre desplegament complet | Una ordre |
| Versió anterior | Sobreescrita | Intacta al disc |
| Verificar abans d'activar | No es pot | Sí, amb calma |
| Espai al disc | Una còpia | N còpies (cal rotar) |
La contrapartida és a l'última fila: cal eliminar els releases antics o /opt creixerà sense límit. La política habitual és conservar els tres o cinc últims, i l'script que ho fa l'escriuràs al Mòdul 4.
Un altre detall de l'esquema: el directori shared/. Les dades que han de sobreviure als desplegaments —fitxers pujats pels usuaris, memòries cau persistents— no poden viure dins del release, perquè el release següent no les tindria. Es posen a shared/ i cada release les enllaça:
Aquest patró, que aquí muntes a mà, és exactament el que implementen les eines de desplegament professionals (Capistrano el va popularitzar, i les estructures de desplegament de molts PaaS el repliquen). L'automatitzaràs amb un script a la lliçó 04-07 i el posaràs en producció al Mòdul 8.
Errors Comuns i Consells
Creure que un enllaç dur és una còpia. En modifiques un i canvies tots dos, perquè són el mateix fitxer.
Creure que esborrar un enllaç dur esborra les dades. Només si era l'últim nom.
Fer ln -sf sense -n sobre un enllaç a directori. Crea l'enllaç a dins en comptes de substituir-lo, silenciosament.
Posar barra final després d'un enllaç en un rm -rf. Travessa l'enllaç i esborra el destí real. L'error més car d'aquesta lliçó.
Copiar arbres amb enllaços fent servir cp -r. Converteix els enllaços en còpies reals i infla el resultat. Fes servir cp -a o rsync -a.
Fer servir enllaços absoluts dins d'un arbre que es copiarà o mourà. La còpia acaba depenent de l'original. ln -sr.
Refiar-se de ls -l per saber si un enllaç funciona. Mostra els trencats igual que els bons. readlink -e o find -xtype l.
Consell: readlink -f abans de qualsevol operació destructiva sobre una ruta que pugui contenir enllaços. Et diu on apunta de debò.
Consell: posa els enllaços relatius dins d'un mateix arbre i absoluts entre arbres diferents. És la regla que evita el 90 % dels enllaços trencats.
Consell: find /ruta -xtype l de tant en tant. Els enllaços trencats són un símptoma: alguna cosa s'ha mogut o s'ha esborrat i ningú no ha actualitzat el que hi apuntava.
Consell: documenta els enllaços estructurals. Un README a /opt/tramontana explicant el patró de releases evita que el pròxim administrador esborri releases/ pensant que sobra.
Exercicis
Exercici 1: comprovar el comportament dels inodes
A /tmp, sense mirar la teoria:
- Crea
original.txtamb tres línies. Anota'n l'inode i el comptador d'enllaços. - Crea un enllaç dur
dur.txti un enllaç simbòlictou.txt. Mostra els tres ambls -lii explica cada columna. - Afegeix una línia a través de
dur.txt. Què veuenoriginal.txtitou.txt? - Esborra
original.txt. Què passa amb cadascun dels altres dos? Explica per què en termes d'inodes. - Torna a crear un
original.txtnou amb contingut diferent. A què apunta aratou.txt? Idur.txt?
Exercici 2: muntar el patró de desplegament
Munta a srv-tramontana l'estructura de releases completa:
- Crea
/opt/tramontana/releases/3.2.1amb el contingut actual de/opt/tramontana/app. - Substitueix
/opt/tramontana/appper un enllaç relatiu a aquest release, sense que el contingut deixi d'estar accessible en cap moment verificable. - Simula el desplegament d'una versió 3.3.0 i activa-la.
- Simula que falla i torna a 3.2.1.
- Escriu una comprovació d'una línia que verifiqui que l'enllaç apunta a un release que existeix realment.
Exercici 3: l'enllaç d'en Luis
En Luis ha muntat al seu portàtil un directori de treball compartit i et passa això perquè ho repliquis al servidor:
La Marta et pregunta si ho pot aprovar. Analitza la proposta, enumera els problemes i proposa l'alternativa correcta amb la seva justificació.
Solucions
Solució 1
operador@srv-tramontana:~$ cd /tmp
operador@srv-tramontana:/tmp$ printf 'línia 1\nlínia 2\nlínia 3\n' > original.txt
operador@srv-tramontana:/tmp$ ls -li original.txt
393221 -rw-rw-r-- 1 operador operador 27 Aug 18 15:02 original.txtInode 393221, comptador 1: un sol nom hi apunta.
operador@srv-tramontana:/tmp$ ln original.txt dur.txt
operador@srv-tramontana:/tmp$ ln -s original.txt tou.txt
operador@srv-tramontana:/tmp$ ls -li original.txt dur.txt tou.txt
393221 -rw-rw-r-- 2 operador operador 27 Aug 18 15:02 dur.txt
393221 -rw-rw-r-- 2 operador operador 27 Aug 18 15:02 original.txt
393225 lrwxrwxrwx 1 operador operador 12 Aug 18 15:03 tou.txt -> original.txtLectura columna a columna:
dur.txtioriginal.txtcomparteixen l'inode 393221 i tots dos mostren comptador 2. Són dos noms del mateix fitxer, sense jerarquia entre ells.tou.txtté inode propi (393225), tipusl, permisoslrwxrwxrwx(que no signifiquen res), mida 12 —els caràcters d'original.txt— i la fletxa al destí.
operador@srv-tramontana:/tmp$ echo "línia 4" >> dur.txt
operador@srv-tramontana:/tmp$ cat original.txt
línia 1
línia 2
línia 3
línia 4
operador@srv-tramontana:/tmp$ cat tou.txt
línia 1
línia 2
línia 3
línia 4Tots tres veuen el mateix. dur.txt i original.txt perquè són el mateix inode; tou.txt perquè es resol a original.txt, que continua existint.
operador@srv-tramontana:/tmp$ rm original.txt
operador@srv-tramontana:/tmp$ ls -li dur.txt tou.txt
393221 -rw-rw-r-- 1 operador operador 36 Aug 18 15:05 dur.txt
393225 lrwxrwxrwx 1 operador operador 12 Aug 18 15:03 tou.txt -> original.txt
operador@srv-tramontana:/tmp$ cat dur.txt
línia 1
línia 2
línia 3
línia 4
operador@srv-tramontana:/tmp$ cat tou.txt
cat: tou.txt: No such file or directorydur.txt funciona perfectament i el seu comptador ha baixat de 2 a 1. rm va eliminar una entrada de directori i va decrementar el comptador; com que no va arribar a 0, l'inode i les seves dades continuen allà.
tou.txt està trencat. Contenia el text original.txt, i aquest nom ja no existeix al directori. L'enllaç no guardava cap referència a l'inode, només una cadena.
operador@srv-tramontana:/tmp$ echo "contingut completament diferent" > original.txt
operador@srv-tramontana:/tmp$ ls -li original.txt dur.txt tou.txt
393221 -rw-rw-r-- 1 operador operador 36 Aug 18 15:05 dur.txt
393230 -rw-rw-r-- 1 operador operador 32 Aug 18 15:08 original.txt
393225 lrwxrwxrwx 1 operador operador 12 Aug 18 15:03 tou.txt -> original.txt
operador@srv-tramontana:/tmp$ cat tou.txt
contingut completament diferent
operador@srv-tramontana:/tmp$ cat dur.txt
línia 1
...Aquest últim pas és el que fixa el concepte:
tou.txts'ha «arreglat» sol i ara apunta al fitxer nou, que té un inode diferent (393230). L'enllaç simbòlic es resol per nom, en el moment de fer-lo servir, així que segueix qualsevol fitxer que ocupi aquell nom. Aquesta és la seva virtut i el seu perill: pot acabar apuntant a alguna cosa que no té res a veure amb l'original.dur.txtcontinua amb el contingut antic a l'inode 393221. Mai no va dependre del nom.
Resum en una frase: l'enllaç dur està lligat al contingut; el simbòlic està lligat al nom.
Solució 2
# 1. Crear el release a partir del contingut actual, preservant-ho tot
operador@srv-tramontana:~$ sudo mkdir -p /opt/tramontana/releases
operador@srv-tramontana:~$ sudo rsync -a /opt/tramontana/app/ /opt/tramontana/releases/3.2.1/
operador@srv-tramontana:~$ sudo diff -rq /opt/tramontana/app /opt/tramontana/releases/3.2.1
operador@srv-tramontana:~$ echo $?
0rsync -a amb barra final a l'origen copia el contingut, no la carpeta. I diff -rq sense sortida confirma que la còpia és idèntica abans de tocar res.
# 2. Substituir el directori per l'enllaç
operador@srv-tramontana:~$ cd /opt/tramontana
# Primer reanomenar (no esborrar): si alguna cosa va malament, l'original continua allà
operador@srv-tramontana:/opt/tramontana$ sudo mv app app.directori-original
# Crear l'enllaç relatiu
operador@srv-tramontana:/opt/tramontana$ sudo ln -sfn releases/3.2.1 app
# Verificar que el contingut continua accessible per la ruta de sempre
operador@srv-tramontana:/opt/tramontana$ ls -l app
lrwxrwxrwx 1 root root 16 Aug 18 15:20 app -> releases/3.2.1
operador@srv-tramontana:/opt/tramontana$ cat app/version.txt
Tramontana Reserves 3.2.1
operador@srv-tramontana:/opt/tramontana$ ls app/
executable plantilles version.txt
# Només quan tot està verificat, eliminar el directori antic
operador@srv-tramontana:/opt/tramontana$ sudo rm -rI app.directori-originalEl detall important és mv en comptes de rm al pas intermedi. Reanomenar és instantani i reversible; esborrar no. Si l'enllaç hagués sortit malament, un mv de tornada ho restaurava tot.
# 3. Desplegar 3.3.0
operador@srv-tramontana:/opt/tramontana$ sudo cp -a releases/3.2.1 releases/3.3.0
operador@srv-tramontana:/opt/tramontana$ echo "Tramontana Reserves 3.3.0" | sudo tee releases/3.3.0/version.txt
Tramontana Reserves 3.3.0
# Verificar ABANS d'activar
operador@srv-tramontana:/opt/tramontana$ cat releases/3.3.0/version.txt
Tramontana Reserves 3.3.0
# Anotar l'estat actual per si cal tornar
operador@srv-tramontana:/opt/tramontana$ readlink app
releases/3.2.1
# Activar
operador@srv-tramontana:/opt/tramontana$ sudo ln -sfn releases/3.3.0 app
operador@srv-tramontana:/opt/tramontana$ cat app/version.txt
Tramontana Reserves 3.3.0# 4. Marxa enrere
operador@srv-tramontana:/opt/tramontana$ sudo ln -sfn releases/3.2.1 app
operador@srv-tramontana:/opt/tramontana$ cat app/version.txt
Tramontana Reserves 3.2.1Una ordre i ets a la versió anterior. I releases/3.3.0 continua al disc, així que reintentar el desplegament després de corregir el problema és una altra ordre.
# 5. Comprovació de l'enllaç
operador@srv-tramontana:/opt/tramontana$ readlink -e app > /dev/null && echo "OK: $(readlink app)" || echo "FALLADA: enllaç trencat"
OK: releases/3.2.1Desglossament: readlink -e resol l'enllaç i falla amb codi diferent de 0 si el destí no existeix. Amb && i || de la lliçó 02-01, la línia informa en tots dos casos. És exactament la comprovació que anirà al principi de l'script de desplegament del Mòdul 4, per no arrencar mai el servei amb l'enllaç apuntant al no-res.
Solució 3
Problemes de la proposta d'en Luis, de més a menys greu:
1. Producció dependria del directori personal d'un desenvolupador. L'enllaç apunta a /home/luis/projectes/tramontana/build. Això significa que l'aplicació en producció s'executa amb el codi que en Luis tingui a la seva carpeta de treball en aquell instant. Cada vegada que en Luis compili, producció canvia. Sense revisió, sense aprovació de la Marta, sense avís. És exactament l'escenari que el procés de desplegament existeix per impedir.
2. Aquest directori no existeix a srv-tramontana. /home/luis és una ruta del portàtil d'en Luis. Al servidor, l'enllaç naixeria trencat:
operador@srv-tramontana:~$ ls -l /opt/tramontana/app
lrwxrwxrwx 1 root root 38 Aug 18 15:40 app -> /home/luis/projectes/tramontana/build
operador@srv-tramontana:~$ readlink -e /opt/tramontana/app
operador@srv-tramontana:~$ echo $?
1ls -l ho mostra tan tranquil, però el servei no arrencaria. I el missatge d'error seria No such file or directory: /opt/tramontana/app, quan app sí que existeix: és el destí el que no. Diagnosticar això sense conèixer els enllaços costa una bona estona.
3. sudo rm app destrueix l'original abans de tenir res. Si l'enllaç nou surt malament —i sortirà malament—, l'aplicació ja no hi és. No hi ha marxa enrere. Hauria de ser mv, i només esborrar quan el nou estigui verificat.
4. Trenca l'FHS. El Mòdul 1 va justificar /opt/tramontana/app precisament perquè /home/usuari/projecte és el pitjor lloc possible: donar de baixa un empleat tomba el servei. Aquí l'enllaç reintrodueix aquesta dependència per la porta del darrere, amb l'agreujant que a ls -l /opt/tramontana sembla que tot està bé.
5. Problema de permisos i seguretat. El codi de producció quedaria sota el control d'un usuari sense privilegis, que el pot modificar en qualsevol moment sense sudo. Qualsevol que comprometi el compte d'en Luis controla l'aplicació en producció. La lliçó 02-07 et donarà el vocabulari exacte per explicar per què això és greu.
6. No hi ha versionatge ni manera de tornar enrere. No se sap quina versió està desplegada ni com recuperar l'anterior.
Alternativa correcta, que és el patró de l'apartat 12:
# En Luis genera un paquet versionat des del seu entorn de compilació
luis@portatil-luis:~$ tar -czf tramontana-app-3.3.0.tar.gz -C build .
luis@portatil-luis:~$ sha256sum tramontana-app-3.3.0.tar.gz > tramontana-app-3.3.0.tar.gz.sha256
# L'operador el rep, en verifica la integritat i el desplega com a release
operador@srv-tramontana:~$ sha256sum -c /srv/tramontana/enviaments/tramontana-app-3.3.0.tar.gz.sha256
tramontana-app-3.3.0.tar.gz: OK
operador@srv-tramontana:~$ sudo mkdir -p /opt/tramontana/releases/3.3.0
operador@srv-tramontana:~$ sudo tar -xzf /srv/tramontana/enviaments/tramontana-app-3.3.0.tar.gz \
-C /opt/tramontana/releases/3.3.0
# Verificar abans d'activar
operador@srv-tramontana:~$ cat /opt/tramontana/releases/3.3.0/version.txt
Tramontana Reserves 3.3.0
# Activar amb una sola ordre atòmica
operador@srv-tramontana:~$ cd /opt/tramontana && sudo ln -sfn releases/3.3.0 app
operador@srv-tramontana:/opt/tramontana$ readlink -e app && cat app/version.txt
/opt/tramontana/releases/3.3.0
Tramontana Reserves 3.3.0Resposta per a la Marta:
No convé aprovar la proposta tal com està. La idea de fons —poder canviar de versió ràpidament— és bona, i de fet és la que implementarem; el problema és l'execució concreta.
Tal com està plantejada, l'aplicació en producció passaria a executar directament el directori de treball del portàtil d'en Luis. Això vol dir tres coses: que qualsevol compilació seva canviaria producció a l'instant i sense avís, que no sabríem en cap moment quina versió està funcionant, i que si en Luis marxa de l'empresa o canvia de portàtil, el servei deixa de funcionar. A més, al servidor aquesta ruta ni tan sols existeix, així que el canvi deixaria l'aplicació caiguda immediatament.
L'alternativa que proposo aconsegueix el mateix objectiu amb garanties: cada versió s'instal·la al seu propi directori numerat dins del servidor, i un punter indica quina està activa. Desplegar és canviar el punter, i tornar a la versió anterior també, en tots dos casos amb una sola ordre i sense copiar res. La versió anterior es manté intacta al servidor, així que la marxa enrere és immediata i segura. I en tot moment es pot consultar quina versió és en producció i qui la va aprovar.
L'hi comento a en Luis: el que necessitem d'ell és el paquet de la versió, no accés directe des del seu equip.
Conclusió
Has desmuntat la suposició amb què vas començar el mòdul: un fitxer no és un nom.
- Un inode guarda totes les metadades i les adreces dels blocs, menys el nom. Els directoris són índexs de parells
(nom, inode). - Aquesta arquitectura explica per què
mvés instantani, per què.comparteix inode amb el seu directori, per què es pot esborrar un fitxer obert i per quèdfidude vegades no quadren. - Un enllaç dur és un altre nom per al mateix inode.
rmdesenllaça: el contingut viu mentre el comptador no arribi a zero i ningú no el tingui obert. - Els enllaços durs no creuen sistemes de fitxers perquè el número d'inode no significa res fora del seu, i no apunten a directoris perquè permetrien cicles que trencarien els recorreguts recursius i la recollida d'espai.
- Un enllaç simbòlic és un fitxer el contingut del qual és una ruta. Creua sistemes de fitxers, apunta a directoris, pot apuntar al que no existeix, i es resol per nom en el moment de fer-lo servir.
- Relatius dins d'un mateix arbre, absoluts entre arbres diferents, i
ln -srels calcula per tu. - Els enllaços trencats no els detecta
ls: els detectenreadlink -eifind -xtype l. - Cada ordre decideix si segueix l'enllaç o no, i els paranys seriosos són
cpsense-a, que desfà els enllaços, i la barra final en unrm -rf, que travessa l'enllaç i esborra el destí real. - Els enllaços són estructura del sistema:
/bin,/usr/bin/python3,/etc/alternativesi habilitar un servei de systemd és crear un enllaç. - I Tramontana ja té el seu patró de desplegament:
releases/immutables i unapp -> releases/Nque converteix publicar i revertir en unln -sfnatòmic, amb-nperquè sense ell el desplegament falla en silenci.
Queda l'última peça del mòdul, i és la que més conseqüències té. Has vist que /etc/tramontana/app.conf és -rw-r----- i pertany a root:tramontana, que l'executable és -rwxr-xr-x, i que els permisos d'un enllaç simbòlic no signifiquen res. Has fet servir sudo sense preguntar-te del tot per què calia. A la propera lliçó, Permisos i Propietat dels Fitxers, tot això deixa de ser soroll: llegiràs la cadena d'ls -l caràcter a caràcter, entendràs per què x significa coses diferents en un fitxer i en un directori, traduiràs entre notació octal i simbòlica en tots dos sentits, calcularàs què fa la umask, reconeixeràs SUID, SGID i sticky bit quan els vegis, i dissenyaràs els permisos correctes de cada ruta de Tramontana. És la lliçó que separa un servidor que funciona d'un servidor que a més és segur.
Curs de Linux: De Principiant a Administrador de Sistemes
Mòdul 1: Introducció a Linux
- Què és Linux?
- Història de Linux
- Distribucions de Linux
- Instal·lant Linux
- Primer Contacte amb el Sistema
- Estructura del Sistema de Fitxers de Linux
Mòdul 2: Comandes Bàsiques de Linux
- Introducció a la Línia de Comandes
- Obtenir Ajuda i Documentació del Sistema
- Navegant pel Sistema de Fitxers
- Operacions amb Fitxers i Directoris
- Visualització i Edició de Fitxers
- Enllaços Durs i Simbòlics
- Permisos i Propietat dels Fitxers
Mòdul 3: Habilitats Avançades en la Línia de Comandes
- L'Entorn del Shell: Variables, Àlies i Historial
- Ús de Comodins i Expressions Regulars
- Cerca de Fitxers i Contingut: find, locate i grep
- Canonades i Redirecció
- Processament de Text: cut, sort, uniq, sed i awk
- Gestió de Processos
- Programació de Tasques amb Cron
- Comandes de Xarxa
Mòdul 4: Scripting en Shell
- Introducció al Scripting en Shell
- Variables i Tipus de Dades
- Entrada, Sortida i Arguments d'un Script
- Estructures de Control
- Funcions i Biblioteques
- Depuració i Gestió d'Errors
- Scripts de Producció: Bones Pràctiques
Mòdul 5: Administració del Sistema
- Gestió d'Usuaris i Grups
- sudo i Permisos Especials
- Gestió de Paquets
- Gestió de Discs
- systemd i la Gestió de Serveis
- Registres del Sistema: journald i syslog
- Monitoratge del Sistema i Optimització del Rendiment
- Còpies de Seguretat i Restauració
Mòdul 6: Xarxes i Seguretat
- Configuració de Xarxes
- SSH i Accés Remot
- Tallafocs i Seguretat Perimetral
- Sistemes de Detecció d'Intrusions
- Gestió de Secrets i Certificats TLS
- Assegurant Sistemes Linux
Mòdul 7: Temes Avançats
- El Procés d'Arrencada i la Recuperació del Sistema
- Diagnòstic Avançat: strace, perf i eBPF
- Optimització del Nucli de Linux
- Virtualització amb Linux
- Contenidors de Linux i Docker
- Automatització amb Ansible
- Alta Disponibilitat i Balanceig de Càrrega
