Mentre scrivo applicazioni web, Ho spesso bisogno di scaricare compiti pesanti di calcolo su uno script lavoratore asincrono, pianificare compiti per dopo, o anche scrivere un demone che ascolta un socket per comunicare direttamente con i clienti.
Anche se a volte ci possono essere strumenti migliori per questo lavoro – considerate sempre di usare prima il software esistente, come ad esempio un server di code di attività – scrivere il proprio servizio può darvi un livello di flessibilità che non avrete mai quando siete legati ai vincoli di software di terze parti.
La cosa bella è che è abbastanza facile creare un servizio Linux: usate il vostro linguaggio di programmazione preferito per scrivere un programma a lunga esecuzione, e trasformatelo in un servizio usando systemd.
Creiamo un piccolo server usando PHP. Vedo le vostre sopracciglia alzarsi, ma funziona sorprendentemente bene. Ascolteremo la porta UDP 10000, e restituiremo qualsiasi messaggio ricevuto con una trasformazione ROT13:
Avviamo:
$ php server.php
E testiamolo in un altro terminale:
$ nc -u 127.0.0.1 10000
Hello, world!
Uryyb, jbeyq!
Fico, funziona. Ora vogliamo che questo script venga eseguito in ogni momento, sia riavviato in caso di fallimento (uscita inaspettata), e sopravviva anche al riavvio del server. È qui che entra in gioco systemd.
Trasformarlo in un servizio
Creiamo un file chiamato /etc/systemd/system/rot13.service
:
Description=ROT13 demo service
After=network.target
StartLimitIntervalSec=0
Type=simple
Restart=always
RestartSec=1
User=centos
ExecStart=/usr/bin/env php /path/to/server.php
WantedBy=multi-user.target
Avrete bisogno di:
- impostare il vostro vero nome utente dopo
User=
- impostare il percorso corretto del vostro script in
ExecStart=
Ecco fatto. Ora possiamo avviare il servizio:
$ systemctl start rot13
E farlo partire automaticamente all’avvio:
$ systemctl enable rot13
Andare oltre
Ora che il servizio (si spera) funziona, può essere importante immergersi un po’ più a fondo nelle opzioni di configurazione, e assicurarsi che funzioni sempre come ci si aspetta.
Iniziare nel giusto ordine
Ti sarai chiesto cosa fa la direttiva After=
. Significa semplicemente che il tuo servizio deve essere avviato dopo che la rete è pronta. Se il tuo programma si aspetta che il server MySQL sia attivo e funzionante, dovresti aggiungere:
After=mysqld.service
Riavvio all’uscita
Per default, systemd non riavvia il tuo servizio se il programma esce per qualsiasi motivo. Questo di solito non è quello che si vuole per un servizio che deve essere sempre disponibile, quindi gli stiamo ordinando di riavviare sempre all’uscita:
Restart=always
Si potrebbe anche usare on-failure
per riavviare solo se lo stato di uscita non è 0
.
Di default, systemd tenta un riavvio dopo 100ms. È possibile specificare il numero di secondi da attendere prima di tentare un riavvio, utilizzando:
RestartSec=1
Evitare la trappola: il limite di avvio
Io personalmente ci sono caduto più di una volta. Per impostazione predefinita, quando si configura Restart=always
come abbiamo fatto noi, systemd rinuncia a riavviare il servizio se non riesce a partire più di 5 volte in un intervallo di 10 secondi. Per sempre.
Ci sono due opzioni di configurazione responsabili di questo:
StartLimitBurst=5
StartLimitIntervalSec=10
La direttiva RestartSec
ha anche un impatto sul risultato: se si imposta il riavvio dopo 3 secondi, allora non si può mai raggiungere 5 tentativi falliti entro 10 secondi.
La semplice soluzione che funziona sempre è impostare StartLimitIntervalSec=0
. In questo modo, systemd tenterà di riavviare il servizio per sempre.
È una buona idea impostare RestartSec
ad almeno 1 secondo però, per evitare di mettere troppo stress sul vostro server quando le cose iniziano ad andare male.
In alternativa, è possibile lasciare le impostazioni predefinite e chiedere a systemd di riavviare il server se il limite di avvio viene raggiunto, utilizzando StartLimitAction=reboot
.