A 03-06 et vaig ensenyar ps, kill i nohup, i et vaig dir amb totes les lletres que res d'això no serveix per gestionar un servei de debò. A 03-07 vas muntar la còpia nocturna amb cron i et vaig avisar que existia alguna cosa millor. Aquesta és la lliçó on se salden les dues promeses, i és el cor del mòdul. Avui Tramontana Reserves s'arrenca a mà: si srv-tramontana es reinicia, l'aplicació no torna; si el procés 1284 mor, ningú no l'aixeca; desplegar.sh canvia l'enllaç simbòlic i no reinicia res; i la còpia de les 4:20 depèn d'una línia de cron sense control real de solapament ni registre decent. En acabar tindràs tramontana.service escrit, enfortit i habilitat, i copia_tramontana.sh convertit en un servei amb el seu timer.
Contingut
- Què és un sistema d'init i què resol systemd
- PID 1, cgroups i els tipus d'unitat
systemctl: consultar, controlar i habilitar- On viuen les unitats i quina precedència tenen
- Escriure una unitat
.servicesecció per secció - Enfortir la unitat i posar-li sostre amb cgroups v2
- Targets i nivells d'execució
- Temporitzadors: el substitut de cron
- Analitzar l'arrencada i llançar treballs transitoris
- Cas Tramontana: el servei, el temporitzador i el desplegament complet
- Què és un sistema d'init i què resol systemd
El nucli arrenca, munta l'arrel i executa un procés: l'init, amb PID 1, del qual descendeix tota la resta. La seva feina és aixecar el sistema, mantenir els serveis vius i apagar-lo amb ordre.
| Aspecte | SysV init | systemd |
|---|---|---|
| Arrencada | Scripts seqüencials /etc/init.d/* |
Paral·lela, guiada per dependències declarades |
| Descripció | Codi de shell que reinventa arrencar, parar i recarregar | Fitxer declaratiu de poques línies |
| Supervisió i seguiment | Cap; fitxers PID que menteixen | Reinici automàtic amb política i límits; cgroups, que el nucli sí que coneix |
| Registre / tasques | Cadascú al seu fitxer; cron a part | Journal estructurat (05-06); temporitzadors integrats |
| Aïllament | Res | Espais de noms, ProtectSystem, capacitats |
La conseqüència pràctica dels cgroups és enorme: si la teva aplicació llança cinc fills i un queda orfe, systemctl stop els mata tots, perquè el nucli els comptabilitza al mateix grup de control. Amb un fitxer PID, aquell orfe continuava escrivint mentre tu et pensaves haver aturat el servei.
- PID 1, cgroups i els tipus d'unitat
$ ps -p 1 -o comm= ; systemd-cgls --no-pager | sed -n '4,5p'
systemd
│ ├─tramontana.service
│ │ └─1284 /opt/tramontana/app/bin/tramontana --config ...Tot a systemd és una unitat, i el sufix del fitxer diu de quin tipus:
| Tipus | Per a què serveix | Exemple |
|---|---|---|
.service |
Un procés gestionat: arrencar, parar, supervisar | tramontana.service |
.socket / .path |
Activa el servei a la primera connexió / en canviar un fitxer | ssh.socket |
.target / .timer |
Punt de sincronització que agrupa unitats / dispara una altra unitat a una hora | multi-user.target, tramontana-copia.timer |
.mount / .slice |
Punt de muntatge (un per línia de fstab) / grup que reparteix CPU, memòria i E/S |
srv-tramontana-backups.mount |
systemctl: consultar, controlar i habilitar
systemctl: consultar, controlar i habilitar$ systemctl status tramontana.service
● tramontana.service - Tramontana Reserves (aplicació web)
Loaded: loaded (/etc/systemd/system/tramontana.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-08-18 04:31:02 CEST; 7h 12min ago
Main PID: 1284 (tramontana)
Tasks: 12 (limit: 4571)
Memory: 214.8M (max: 512.0M available: 297.1M)
CGroup: /system.slice/tramontana.service
└─1284 /opt/tramontana/app/bin/tramontana --config /etc/tramontana/app.conf
Aug 18 11:02:17 srv-tramontana tramontana[1284]: db_timeout després de 30s (connexions=200)Cal llegir la sortida sencera:
Loaded: de quin fitxer ve i si estàenabled(arrenca sol en iniciar la màquina) odisabled. Un servei pot estaractiveidisabled: funciona ara, però no tornarà després d'un reinici.Active:active (running)per a un dimoni,active (exited)per a unoneshotque va acabar bé,failedamb el codi de sortida i el senyal si va morir.Tasks,Memory,CGroup: consum real del cgroup i arbre complet de processos, fills inclosos, no una estimació. Les últimes línies són registre del journal, que exprimiràs a 05-06.
sudo systemctl start|stop|restart tramontana.service
sudo systemctl reload-or-restart tramontana.service # recarrega (si hi ha ExecReload) o reinicia
systemctl is-active|is-enabled|is-failed tramontana.service # scripts: codi 0/3
systemctl --failed ; systemctl list-units --type=service --state=runningenable és literalment un enllaç simbòlic
Aquí es tanca el cercle amb 02-06:
$ sudo systemctl enable tramontana.service
Created symlink /etc/systemd/system/multi-user.target.wants/tramontana.service → /etc/systemd/system/tramontana.service.
$ sudo systemctl mask tramontana.service
Created symlink /etc/systemd/system/tramontana.service → /dev/null.enable crea l'enllaç dins del .wants del target que indiqui [Install] i disable l'esborra: res més. Per això enable no arrenca el servei (per a això, enable --now) i disable no l'atura. mask enllaça la unitat a /dev/null, i així no es pot arrencar en absolut —ni a mà, ni com a dependència, ni per un sòcol—, que és el que vols durant un manteniment; unmask ho reverteix.
| Estat | Arrenca en iniciar? | A mà? |
|---|---|---|
enabled / disabled |
Sí / no | Sí en tots dos casos |
masked / static |
No / només com a dependència (no té [Install]) |
No, de cap manera / sí |
sudo systemctl daemon-reload és obligatori després de crear o editar qualsevol fitxer d'unitat: si no, systemd continua treballant amb la versió anterior en memòria i et tornaràs boig. I recarregar la configuració no reinicia el servei: això és un restart a part.
systemctl cat tramontana.service # la unitat efectiva, amb els seus drop-ins
systemctl show tramontana.service -p Restart -p User -p MemoryMax
systemctl list-dependencies tramontana.service
sudo systemctl edit tramontana.service # crea un DROP-IN sense tocar l'originaledit obre /etc/systemd/system/tramontana.service.d/override.conf, on escrius només la secció i les directives que cal canviar. Un drop-in és millor que editar la unitat original: sobreviu a l'actualització del paquet que la va portar, deixa explícit què has canviat tu i s'elimina esborrant un fitxer. Avís amb les directives de llista (ExecStart, Environment): per substituir cal buidar-les abans amb ExecStart= en blanc, o s'acumulen.
- On viuen les unitats i quina precedència tenen
Per prioritat decreixent: /etc/systemd/system/ (on escrius tu), /run/systemd/system/ (unitats transitòries, a la RAM) i /lib/systemd/system/ (les dels paquets). Les teves unitats van a /etc/systemd/system/; les dels paquets no es toquen, s'ajusten amb drop-ins. Un fitxer amb el mateix nom a /etc substitueix completament el de /lib.
- Escriure una unitat
.service secció per secció
.service secció per secció[Unit]: què és i amb qui es relaciona
Aquí s'hi embolica tothom, perquè hi ha dos eixos independents: l'ordre (quan arrenca respecte d'una altra unitat) i la dependència (si la necessita per existir).
| Directiva | Eix | Significat |
|---|---|---|
After= / Before= |
Ordre | «Arrenca'm després/abans de X», sense obligar que X existeixi |
Wants= |
Dependència feble | Intenta arrencar X; si X falla, jo arrenco igualment |
Requires= / BindsTo= |
Dependència forta | Si X falla, jo no arrenco; amb BindsTo, a més m'aturo si X s'atura |
Conflicts= |
Exclusió | X i jo no podem estar actius alhora |
L'error clàssic és posar Requires=postgresql.service i creure que garanteix que la base de dades estigui llesta abans que tu. No ho garanteix: Requires sense After arrenca les dues en paral·lel, així que calen les dues directives. Com a norma fes servir Wants=: si el servei de registres remot falla, prefereixes que la teva aplicació arrenqui igualment.
[Service]: com s'executa
Type= |
Quan es considera arrencat | Quan fer-lo servir |
|---|---|---|
simple / exec |
En llançar ExecStart / quan l'execve() té èxit |
Procés en primer pla; exec detecta a més un binari inexistent |
forking |
Quan el pare surt i deixa el fill | Dimonis clàssics; requereix PIDFile= |
oneshot |
Quan el procés acaba | Còpies i migracions; amb RemainAfterExit=yes queda com a actiu |
notify |
Quan el procés avisa per sd_notify() |
Serveis que saben dir «ja estic llest» |
Un servei modern en Go, Java o Python gairebé sempre és simple o exec: no l'enviïs al fons amb & ni facis servir nohup, perquè systemd ja se n'encarrega. La resta de directives del dia a dia:
ExecStartPre=/usr/bin/test -r /etc/tramontana/app.conf # si falla, no arrenca
ExecStart=/opt/tramontana/app/bin/tramontana --config /etc/tramontana/app.conf
ExecReload=/bin/kill -HUP $MAINPID # què fa 'systemctl reload'
ExecStop=/opt/tramontana/app/bin/tramontana --parada-neta
User=svc-tramontana
Group=tramontana
Environment=TRAMONTANA_ENTORN=produccio
EnvironmentFile=-/etc/tramontana/entorn # el '-': si no existeix, continua igual
Restart=on-failure
RestartSec=5
StartLimitBurst=5
TimeoutStopSec=30
KillMode=mixedRestart=:no(per defecte),on-failure(codi diferent de zero o senyal fatal: l'opció correcta per a una aplicació),always,on-abnormal.RestartSec+StartLimitBurst+StartLimitIntervalSec: reinicia als 5 s, i si ho fa 5 vegades en 300 s es rendeix i quedafailed. Sense aquest límit, una aplicació que no arrenca entra en un bucle infinit que consumeix CPU i omple el registre; per tornar-hi,systemctl reset-failed.TimeoutStopSeciKillMode=mixed: quant s'espera després del SIGTERM abans del SIGKILL, i que aquest arribi a tot el cgroup. És la seqüència «SIGTERM → esperar → verificar →-9» de 03-06, automatitzada.
[Install]: quan arrencar sol
Sol tenir una línia, WantedBy=multi-user.target, i és el que llegeix enable per saber en quin directori .wants ha de crear l'enllaç. Una unitat sense [Install] no es pot habilitar: apareixerà com a static.
- Enfortir la unitat i posar-li sostre amb cgroups v2
És una de les coses més valuoses de systemd, i és de franc: unes línies deixen el teu servei amb moltíssima menys superfície d'atac de la que aconseguiries a mà.
| Directiva | Què fa |
|---|---|
NoNewPrivileges=yes |
El procés no pot guanyar privilegis: anul·la SUID i setcap de 05-02 |
PrivateTmp= / PrivateDevices= / ProtectHome= / ProtectKernelTunables= |
/tmp propi; només dispositius bàsics; /home i /root invisibles; /proc/sys de només lectura |
ProtectSystem=strict + ReadWritePaths= |
Tot el FS en només lectura tret de la llista mínima que obris |
RestrictAddressFamilies= |
Quines famílies de sòcols pot fer servir (AF_INET AF_INET6 AF_UNIX) |
CapabilityBoundingSet= / AmbientCapabilities= |
Sostre de capacitats (buit = cap) / les que sí que concedeix, p. ex. CAP_NET_BIND_SERVICE |
LockPersonality=yes, MemoryDenyWriteExecute=yes |
Tanquen vies d'explotació conegudes |
systemd-analyze security tramontana.service puntua el resultat: la unitat que escriurem al final de la lliçó treu 2.9 OK, davant del 9.6 UNSAFE que treia sense enfortir. Nota pràctica: ProtectSystem=strict trencarà el teu servei la primera vegada, perquè escriu en algun lloc que no has declarat. No ho desactivis: mira el registre, afegeix la ruta a ReadWritePaths i torna-ho a provar. Aquell exercici t'obliga a saber exactament on escriu la teva aplicació.
A la mateixa secció [Service] pertanyen els límits de recursos, que systemd aplica amb cgroups v2:
MemoryMax=512M # dur: en superar-lo, l'OOM killer actua DINS del servei
MemoryHigh=384M # a partir d'aquí es pressiona perquè alliberi, sense matar
CPUQuota=150% # com a molt, 1,5 nuclis dels 2; TasksMax=64 conté una fork bomb
IOWeight=50 # prioritat relativa d'E/S$ systemd-cgtop --iterations=1 | sed -n '3p'
system.slice/tramontana.service 12 2.8 214.8M 12.0K 840.0KAquell MemoryMax impedeix que una fuita de memòria s'endugui per davant la base de dades i el mateix SSH: en superar-lo, el nucli mata dins del cgroup del servei i Restart=on-failure l'aixeca.
- Targets i nivells d'execució
| Target | Què és |
|---|---|
multi-user.target (runlevel 3) |
Sistema complet en xarxa sense entorn gràfic: el normal en un servidor; graphical.target hi afegeix l'escriptori |
rescue.target / emergency.target |
Monousuari amb FS locals / només un shell amb / en lectura: on t'envia un fstab trencat |
També existeix network-online.target, el punt de sincronització que indica que hi ha xarxa utilitzable. systemctl get-default diu quin és el predeterminat, set-default el canvia i sudo systemctl isolate rescue.target canvia de target ara mateix, tallant els serveis que sobren.
- Temporitzadors: el substitut de cron
Un temporitzador és una unitat que n'activa una altra: per convenció, tramontana-copia.timer dispara tramontana-copia.service (mateix nom, un altre sufix).
| Aspecte | cron | Temporitzador de systemd |
|---|---|---|
| Sintaxi | Cinc camps críptics | OnCalendar= llegible i verificable |
| Execució perduda (màquina apagada) | Es perd | Persistent=true l'executa en arrencar |
| Registre i entorn | El que redirigeixis; PATH mínim |
Journal (journalctl -u); l'entorn de la unitat |
| Solapament i dependències | flock a mà; cap dependència |
No es rellança si continua actiu; After=, Requires= i l'enfortiment de la secció 6 |
| Veure el programat | crontab -l per usuari |
systemctl list-timers, global |
# /etc/systemd/system/tramontana-copia.timer
[Unit]
Description=Còpia diària de Tramontana Reserves
[Timer]
OnCalendar=*-*-* 04:20:00
Persistent=true
RandomizedDelaySec=180
Unit=tramontana-copia.service
[Install]
WantedBy=timers.targetVerifica sempre l'expressió abans de confiar-hi:
$ systemd-analyze calendar '*-*-* 04:20:00'
Next elapse: Wed 2026-08-19 04:20:00 CEST (From now: 17h left)Altres formes de disparament: OnBootSec=5min (després d'arrencar la màquina), OnUnitActiveSec=1h (cada hora des de l'última execució) i OnStartupSec=. I expressions llegibles: daily, weekly, Mon..Fri 08:00, *-*-01 03:00 (dia 1 de cada mes).
$ systemctl list-timers --all | tail -1
Wed 2026-08-19 04:20:00 CEST 17h left Tue 2026-08-18 04:21:43 CEST tramontana-copia.timerPersistent=true és la raó principal per migrar de cron: si el servidor estava apagat a les 4:20, la còpia s'executa tan bon punt arrenqui en lloc de perdre's fins l'endemà.
- Analitzar l'arrencada i llançar treballs transitoris
$ systemd-analyze
Startup finished in 3.412s (kernel) + 11.807s (userspace) = 15.219s
$ systemd-analyze blame | head -1
6.104s [email protected]
$ systemd-analyze critical-chain tramontana.service | head -3
tramontana.service +412ms
└─postgresql.service @5.201s +6.104s
$ sudo systemd-run --unit=purga --property=MemoryMax=256M /home/operador/scripts/purgar_releases.sh --dry-runblame ordena per durada; critical-chain mostra la cadena que de debò retarda l'arrencada d'una unitat concreta, que és el que importa: un servei lent del qual no depèn ningú no retarda res. I systemd-run llança alguna cosa puntual com a unitat transitòria, registrada al journal i amb els seus límits aplicats, a diferència d'un nohup.
- Cas Tramontana: el servei, el temporitzador i el desplegament complet
tramontana.service
# /etc/systemd/system/tramontana.service
[Unit]
Description=Tramontana Reserves (aplicació web)
Documentation=https://intranet.tramontana.example/runbook
Wants=network-online.target
After=network-online.target postgresql.service
[Service]
Type=exec
User=svc-tramontana
Group=tramontana
UMask=0027
WorkingDirectory=/opt/tramontana/app
Environment=TRAMONTANA_ENTORN=produccio
ExecStartPre=/usr/bin/test -r /etc/tramontana/app.conf
ExecStart=/opt/tramontana/app/bin/tramontana --config /etc/tramontana/app.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StartLimitBurst=5
TimeoutStopSec=30
KillMode=mixed
# Enfortiment
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/log/tramontana /opt/tramontana/shared/uploads
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
CapabilityBoundingSet=
MemoryMax=512M
[Install]
WantedBy=multi-user.targetDecisions que convé entendre: Type=exec perquè l'aplicació es queda en primer pla i així un binari inexistent es detecta en arrencar; User=svc-tramontana amb Group=tramontana, les identitats de 05-01, i UMask=0027 per convenció del curs; ReadWritePaths amb només el registre i les pujades, perquè tota la resta —inclòs /opt/tramontana— ha de ser immutable per a l'aplicació; i CapabilityBoundingSet= buit perquè escolta al 8080 (si passés al 80 s'hi afegiria AmbientCapabilities=CAP_NET_BIND_SERVICE, substituint el setcap de 05-02, que es perdia a cada desplegament).
$ sudo systemctl daemon-reload && sudo systemctl enable --now tramontana.service
$ systemctl is-active tramontana.service && curl -sf -o /dev/null -w '%{http_code}\n' http://10.0.2.15:8080/salut
active
200El temporitzador de la còpia
# /etc/systemd/system/tramontana-copia.service
[Unit]
Description=Còpia de seguretat de Tramontana Reserves
RequiresMountsFor=/srv/tramontana/backups
[Service]
Type=oneshot
User=operador
Group=tramontana
UMask=0027
ExecStart=/home/operador/scripts/copia_tramontana.sh -q
TimeoutStartSec=30min
Nice=10
IOSchedulingClass=idle
ProtectSystem=strict
ReadWritePaths=/srv/tramontana/backups /var/log/tramontanaType=oneshot perquè l'script acaba, i sense [Install] perquè no s'habilita: el dispara el temporitzador, i per això apareixerà com a static. RequiresMountsFor= és la joia d'aquesta unitat: si el volum LVM de 05-04 no està muntat, la còpia no s'executa en lloc d'escriure alegrement al directori buit de /, una fallada silenciosa que només descobriries el dia de la restauració. Nice=10 i IOSchedulingClass=idle fan que la còpia cedeixi el pas a l'aplicació, cosa que a 05-07 resultarà important.
$ sudo systemctl daemon-reload && sudo systemctl enable --now tramontana-copia.timer
$ sudo systemctl start tramontana-copia.service # provar-la JA, sense esperar a les 4:20
$ systemctl status tramontana-copia.service | sed -n '3p'
Active: inactive (dead) since Tue 2026-08-18 11:41:09 CEST; 8s ago
$ sudo sed -i.bak-$(date +%F) '/copia_tramontana.sh/s/^/#MIGRAT A TIMER /' /etc/cron.d/tramontanainactive (dead) després d'un oneshot és èxit: l'script va acabar amb codi 0; un failed mostraria el codi de sortida que vas definir a 04-03. I aquell sed final retira la línia de cron, que és la meitat de la feina que la gent oblida: deixar les dues coses actives significa dues còpies simultànies barallant-se pel flock cada matinada.
desplegar.sh reinicia per fi
El deute del Mòdul 4: després de l'ln -sfn atòmic, l'script comprovava la salut contra un procés que continuava executant el release anterior, perquè el binari ja estava carregat en memòria. Ara:
ln -sfn "releases/${versio}" "${TRAMONTANA_BASE}/app.nou"
mv -T "${TRAMONTANA_BASE}/app.nou" "${TRAMONTANA_BASE}/app"
sudo systemctl restart tramontana.service || morir 74 "systemctl restart ha fallat"
for _ in 1 2 3 4 5 6; do # esperar que estigui amunt abans de mesurar la salut
systemctl is-active --quiet tramontana.service && break; sleep 2
done
if ! curl -fsS --max-time 5 "http://127.0.0.1:8080/salut" >/dev/null; then
error "la ${versio} no respon; revertint"
ln -sfn "releases/${previ}" "${TRAMONTANA_BASE}/app.nou"
mv -T "${TRAMONTANA_BASE}/app.nou" "${TRAMONTANA_BASE}/app"
sudo systemctl restart tramontana.service
morir 75 "reversió a ${previ} completada"
fiEl sudo systemctl restart funciona sense contrasenya interactiva gràcies a la regla d'/etc/sudoers.d/tramontana que vas escriure a 05-02: aquí és on les tres lliçons encaixen. I systemctl is-active --quiet en bucle evita la fallada clàssica de comprovar la salut abans que el procés hagi obert el port.
Errors Comuns i Consells
- Oblidar el
daemon-reload. Edites la unitat, reinicies i no canvia res: si has tocat un fitxer d'unitat,daemon-reloadabans que res. Ienableno arrenca el servei, només crea l'enllaç: fes servirenable --now. Requires=senseAfter=. No garanteix cap ordre: les dues unitats arrenquen en paral·lel. PrefereixWants=tret que de debò no puguis funcionar sense l'altra. I en migrar a un temporitzador, comenta la línia de cron en el mateix canvi o tindràs dues execucions simultànies.- Enviar el procés al fons amb
&onohupaExecStart. systemd el perd de vista i el dona per mort: deixa que corri en primer pla. Restart=alwayssenseStartLimitBurst. Una aplicació que no arrenca entra en un bucle que consumeix CPU i omple el disc de registres. I no et rendeixis davant deProtectSystem=strict: la fallada et diu on escriu la teva aplicació, així que afegeix la ruta aReadWritePathsen lloc de treure la protecció.- Editar la unitat d'un paquet a
/lib/systemd/system. La propera actualització s'endú la teva feina: drop-in a/etc/systemd/system/<unitat>.d/. - Consell: guarda les teves unitats a git al costat dels scripts. Una unitat és codi de producció i es revisa com a tal.
Exercicis
- Diagnòstic d'un servei que no arrenca.
tramontana.serviceestà enfailed. Enumera, en ordre, les cinc ordres que executaries per esbrinar-ne la causa, i digues què t'explica cadascuna. - Un temporitzador amb recuperació. Escriu el parell
.service+.timerque executi/home/operador/scripts/purgar_releases.shtots els dilluns a les 05:10, que es recuperi si el servidor estava apagat, que no se solapi amb la còpia i que quedi registrat. Verifica l'expressió de calendari. - Drop-in en lloc d'edició. El proveïdor demana que Tramontana arrenqui amb
TRAMONTANA_MAX_FILS=8i amb 768 MiB de límit de memòria, sense modificartramontana.service. Fes-ho i demostra que ha fet efecte.
Solucions
1.
systemctl status tramontana.service # 1. Estat, codi de sortida i últimes línies
journalctl -u tramontana.service -n 50 # 2. El registre complet de l'arrencada fallida
systemctl cat tramontana.service # 3. La unitat EFECTIVA, amb els seus drop-ins
sudo -u svc-tramontana /opt/tramontana/app/bin/tramontana --config /etc/tramontana/app.conf
systemd-analyze verify /etc/systemd/system/tramontana.service # 5. Sintaxi i referènciesLa quarta ordre llança l'aplicació a mà amb el seu usuari: falla l'aplicació o la unitat?
És el pas que més temps estalvia. Si a mà funciona i per systemd no, el culpable sol ser l'enfortiment —una ruta que falta a ReadWritePaths— o l'entorn, perquè la unitat no hereta les teves variables.
2.
# /etc/systemd/system/tramontana-purga.service
[Unit]
Description=Purga de releases antics de Tramontana
After=tramontana-copia.service
Conflicts=tramontana-copia.service
[Service]
Type=oneshot
User=operador
Group=tramontana
ExecStart=/home/operador/scripts/purgar_releases.sh
TimeoutStartSec=15min
ProtectSystem=strict
ReadWritePaths=/opt/tramontana/releases /var/log/tramontana
# /etc/systemd/system/tramontana-purga.timer
[Unit]
Description=Purga setmanal de releases
[Timer]
OnCalendar=Mon 05:10
Persistent=true
RandomizedDelaySec=300
Unit=tramontana-purga.service
[Install]
WantedBy=timers.target$ systemd-analyze calendar 'Mon 05:10' | grep 'Next elapse'
Next elapse: Mon 2026-08-24 05:10:00 CEST
$ sudo systemctl daemon-reload && sudo systemctl enable --now tramontana-purga.timerConflicts= més After= garanteixen que no coincideixin, el registre és automàtic al journal i no cal flock: systemd no rellança una unitat que ja està activa.
3. Amb sudo systemctl edit tramontana.service, que crea el drop-in:
### /etc/systemd/system/tramontana.service.d/override.conf
[Service]
Environment=TRAMONTANA_MAX_FILS=8
MemoryMax=768M$ sudo systemctl daemon-reload && sudo systemctl restart tramontana.service
$ systemctl show tramontana.service -p MemoryMax -p Environment
MemoryMax=805306368
Environment=TRAMONTANA_ENTORN=produccio TRAMONTANA_MAX_FILS=8systemctl show dona el valor efectiu (en bytes) i systemctl cat mostraria el drop-in al final de la unitat sense haver tocat l'original. I Environment= és acumulativa: la variable nova se suma a les que ja definia la unitat.
Conclusió
srv-tramontana ja es governa sol. Saps què és un init i què aporta systemd davant de SysV —arrencada paral·lela per dependències, supervisió real, cgroups que no menteixen, registre estructurat i aïllament—; coneixes els tipus d'unitat i manegues systemctl de punta a punta: llegeixes un status línia a línia, controles amb start/restart/reload, distingeixes enabled d'active i disable de mask, saps que enable és literalment crear un enllaç simbòlic i que daemon-reload és obligatori després de tocar qualsevol fitxer d'unitat. Ajustes amb drop-ins en lloc d'editar unitats alienes.
Escrius una unitat des de zero i entens les dues coses on tothom ensopega: la diferència entre ordre (After/Before) i dependència (Wants/Requires/BindsTo), i quin Type= correspon a cada manera d'arrencar. Enforteixes amb NoNewPrivileges, ProtectSystem=strict i ReadWritePaths, ho comproves amb systemd-analyze security, poses sostre amb MemoryMax, CPUQuota i TasksMax vigilant-ho amb systemd-cgtop, manegues targets, analitzes l'arrencada amb blame i critical-chain i llances treballs transitoris amb systemd-run. I els deutes estan saldats: tramontana.service existeix, enfortit, amb reinici automàtic i límits, i arrenca sol després d'un reinici del servidor; copia_tramontana.sh és ara tramontana-copia.service disparat per un temporitzador a les 04:20 amb Persistent=true i RequiresMountsFor sobre el volum de 05-04, amb la línia de cron ja retirada; i desplegar.sh fa el systemctl restart que li faltava, recolzant-se en la regla de sudoers de 05-02. Falta la memòria del sistema. El teu servei ja escriu al journal, però no saps consultar-lo; /var/log/tramontana/acces.log porta 412 línies i creixent sense que ningú el roti, que va ser el primer deute que vaig anunciar en tancar el Mòdul 4 i continua sense pagar; i quan la Marta pregunti «què va passar ahir a la nit a les 03:00?», ara mateix no sabries respondre amb precisió. A Registres del Sistema: journald i syslog veuràs les dues capes de registre que conviuen a Ubuntu, exprimiràs journalctl amb tots els seus filtres, faràs el journal persistent, escriuràs un /etc/logrotate.d/tramontana correcte i provat en simulació, connectaràs lib/comuns.sh amb el journal mitjançant logger, i aprendràs què mai no ha d'acabar escrit en un registre.
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
