{"id":521,"date":"2023-10-16T19:46:40","date_gmt":"2023-10-16T18:46:40","guid":{"rendered":"https:\/\/blog.sguazz.it\/?p=521"},"modified":"2023-10-16T19:46:40","modified_gmt":"2023-10-16T18:46:40","slug":"systemd-e-postgresql-avvio-alla-bisogna","status":"publish","type":"post","link":"https:\/\/blog.sguazz.it\/index.php\/archives\/521","title":{"rendered":"Systemd e PostgreSQL: avvio alla bisogna"},"content":{"rendered":"\n<p><a href=\"https:\/\/systemd.io\/\">systemd<\/a> ha tante caratteristiche interessanti, una delle quali \u00e8 l&#8217;attivazione tramite <em>socket<\/em>, vale a dire che si pu\u00f2 configurare systemd si mette in attesa su una certa porta TCP (o una <em>pipe<\/em>, o altro tipo di <em>socket<\/em>) al posto di un altro programma. Quando arriva una richiesta di connessione, systemd l&#8217;accetta e attiva il servizio configurato, poi questo deve comunicare con systemd, prendere la richiesta in arrivo e servirla.<\/p>\n\n\n\n<p>Perch\u00e9 \u00e8 interessante? Perch\u00e9 si possono configurare molti servizi su una sola macchina, ma attivarli solo quando veramente sono richiesti. Oppure, durante l&#8217;avvio del computer, si possono attivare vari servizi in contemporanea, con l&#8217;attivazione tramite <em>socket<\/em>, e proseguire come se gi\u00e0 fossero attivi.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Come si configura questa attivazione? Con una <em>unit<\/em> di tipo \u00absocket\u00bb che indica su quale <em>socket<\/em> ascoltare, e una <em>unit<\/em> di tipo \u00abservice\u00bb (con lo stesso nome) che indica il servizio da attivare quando arriva una connessione sul <em>socket<\/em>. La prima <em>unit<\/em> va installata sul <em>target<\/em> corretto (in genere il multi-user). Il servizio pu\u00f2 essere un programma o un <em>container<\/em>.<\/p>\n\n\n\n<p>Vediamo un esempio: cerchiamo di configurare l&#8217;avvio di PostgreSQL in questo modo.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-configurazione-predefinita-di-debian\">Configurazione predefinita di Debian<\/h1>\n\n\n\n<p>Per prima cosa vediamo quali sono le <em>unit<\/em> fornite con i pacchetti standard di <a href=\"https:\/\/www.debian.org\/\">Debian<\/a> per <a href=\"https:\/\/www.postgresql.org\/\">PostgreSQL<\/a> per controllare che ci sia anche quella da usare come modello per generare la vera <em>unit<\/em> (\u00e8 quella identificata da una chicciolina nel nome):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ find \/usr\/lib\/systemd \/var\/lib\/systemd\/ \/etc\/systemd\/ -name \\*postgre\\*\n\/usr\/lib\/systemd\/system\/<strong>postgresql@.service<\/strong>\n\/usr\/lib\/systemd\/system\/postgresql.service\n\/usr\/lib\/systemd\/system-generators\/postgresql-generator\n\/var\/lib\/systemd\/deb-systemd-helper-enabled\/multi-user.target.wants\/postgresql.service\n\/var\/lib\/systemd\/deb-systemd-helper-enabled\/postgresql.service.dsh-also<\/code><\/pre>\n\n\n\n<p>creiamo un <em>cluster<\/em> con PostgreSQL 16 che chiamiamo linuxday<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo pg_createcluster 16 linuxday\nCreating new PostgreSQL cluster 16\/linuxday ...\n&#91;...]\n$ pg_lsclusters Ver Cluster  Port Status Owner    Data directory                  Log file\n16  linuxday 5434 <strong>down<\/strong>   postgres \/var\/lib\/postgresql\/16\/linuxday \/var\/log\/postgresql\/postgresql-16-linuxday.log<\/code><\/pre>\n\n\n\n<p>e notiamo che systemd continua a non sapere nulla di PostgreSQL<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ systemctl <strong>list-units<\/strong> | grep postg\n$<\/code><\/pre>\n\n\n\n<p>Proviamo ad avviare il <em>cluster<\/em> con i comandi di PostgreSQL e ricontrolliamo systemd<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo pg_ctlcluster 16 linuxday start\n$ systemctl <strong>list-units<\/strong> | grep postg\npostgresql@16-linuxday.service                                                                  loaded active     <strong>running<\/strong>   PostgreSQL Cluster 16-linuxday\nsystem-postgresql.slice                                                                         loaded active     <strong>active<\/strong>    Slice \/system\/postgresql<\/code><\/pre>\n\n\n\n<p>adesso si vede che systemd \u00e8 a conoscenza del <em>cluster<\/em>, che \u00e8 ora attivo. A dire il vero ci sono due diverse <em>unit<\/em>, una di tipo <em><a href=\"https:\/\/www.freedesktop.org\/software\/systemd\/man\/systemd.service.html\">service<\/a><\/em>, che corrisponde ad un file sul file system, ed una di tipo <em><a href=\"https:\/\/www.freedesktop.org\/software\/systemd\/man\/systemd.slice.html\">slice<\/a><\/em>. Quest&#8217;ultima \u00e8 una <em>unit<\/em> generata internamente da un&#8217;applicazione o da systemd stesso, che non ha un file che la descrive.<\/p>\n\n\n\n<p>Si possono usare i normali comandi di systemd per vedere lo stato del server, come ad esempio:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ systemctl <strong>status<\/strong> postgresql@16-linuxday.service\n\u25cf postgresql@16-linuxday.service - PostgreSQL Cluster 16-linuxday\nLoaded: loaded (\/lib\/systemd\/system\/<strong>postgresql@.service<\/strong>; enabled-runtime; preset: enabled)\nActive: <strong>active<\/strong> (running) since Sat 2023-10-14 09:35:38 CEST; 2min 26s ago\nProcess: 15725 ExecStart=\/usr\/bin\/pg_ctlcluster --skip-systemctl-redirect 16-linuxday start (code=exited, status=0\/SUCCESS)\nMain PID: 15730 (postgres)\nTasks: 6 (limit: 19055)\nMemory: 20.3M\nCPU: 220ms\nCGroup: \/system.slice\/system-postgresql.slice\/postgresql@16-linuxday.service\n\u251c\u250015730 \/usr\/lib\/postgresql\/16\/bin\/postgres -D \/var\/lib\/postgresql\/16\/linuxday -c config_file=\/etc\/postgresql\/16\/linuxday\/postgresql.conf\n\u251c\u250015731 \"postgres: 16\/linuxday: checkpointer \"\n\u251c\u250015732 \"postgres: 16\/linuxday: background writer \"\n\u251c\u250015734 \"postgres: 16\/linuxday: walwriter \"\n\u251c\u250015735 \"postgres: 16\/linuxday: autovacuum launcher \"\n\u2514\u250015736 \"postgres: 16\/linuxday: logical replication launcher \"<\/code><\/pre>\n\n\n\n<p>notare che il nome del file della unit \u00e8 quello del generatore, con la @.<\/p>\n\n\n\n<p>Oppure si pu\u00f2 usare il comando di PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ pg_lsclusters\nVer Cluster  Port Status Owner    Data directory                  Log file\n16  linuxday 5434 <strong>online<\/strong> postgres \/var\/lib\/postgresql\/16\/linuxday \/var\/log\/postgresql\/postgresql-16-linuxday.log<\/code><\/pre>\n\n\n\n<p>Vediamo ora su che porte sta ascoltando PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo lsof -p 15730 2>&amp;1 | grep LISTEN\npostgres 15730 postgres    6u     IPv4              81476      0t0      TCP localhost:<strong>5434<\/strong> (LISTEN)\npostgres 15730 postgres    7u     unix 0x00000000cca3376a      0t0    81477 \/var\/run\/postgresql\/<strong>.s.PGSQL.5434<\/strong> type=STREAM (LISTEN)<\/code><\/pre>\n\n\n\n<p>La prima \u00e8 una porta TCP, la 5434, la seconda \u00e8 una <em>named pipe<\/em> in <code>\/var\/run\/postgresql<\/code>.<\/p>\n\n\n\n<p>Ora colleghiamoci al <em>cluster<\/em> come utente postgres, che \u00e8 il super utente, e creiamo un utente per me:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo su - postgres -c 'psql --cluster 16\/linuxday'\npsql (16.0 (Debian 16.0-1.pgdg120+1))\nDigita \"help\" per avere un aiuto.\n\npostgres=# <strong>create user giuseppe superuser<\/strong>;\nCREATE ROLE\npostgres=# \\password giuseppe\nInserisci la password per l'utente \"giuseppe\":\nConferma password:\npostgres=#\\q<\/code><\/pre>\n\n\n\n<p>E lo spegniamo usando systemctl al posto dei comandi di PostgreSQL e poi controlliamo che sia spento:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo systemctl <strong>stop<\/strong> postgresql@16-linuxday.service\n$ pg_lsclusters\nVer Cluster  Port Status Owner    Data directory                  Log file\n16  linuxday 5434 <strong>down<\/strong>   postgres \/var\/lib\/postgresql\/16\/linuxday \/var\/log\/postgresql\/postgresql-16-linuxday.log<\/code><\/pre>\n\n\n\n<p>Quindi, possiamo accendere e spegnere il nostro <em>cluster<\/em> PostgreSQL indifferentemente con i comandi originali o con systemd. Ma con systemd possiamo indicare le dipendenze e inserire nel punto corretto la <em>unit<\/em> da avviare con l&#8217;accensione della macchina.<\/p>\n\n\n\n<p>Per i curiosi, l&#8217;integrazione tra i due sistemi \u00e8 stata fatta nel comando <code>pg_ctlcluster<\/code>, il quale se viene invocato normalmente fa passare l&#8217;azione da systemd, il quale a sua volta richiama pg_ctlcluster con un argomento speciale che dice di fare effettivamente l&#8217;azione richiesta (avvio, arresto, ricaricamento della configurazione). Lo si pu\u00f2 vedere nella sezione [Service] della unit postgresql@.service con il comando<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl cat postgresql@16-linuxday.service<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"h-attivazione-via-socket\">Attivazione via <em>socket<\/em><\/h1>\n\n\n\n<p>L&#8217;attivazione via <em>socket<\/em> richiede che il programma da avviare collabori con systemd, difatti se il <em>socket<\/em> viene creato da systemd, che si mette in ascolto, l&#8217;applicazione non potr\u00e0 a sua volta creare quel <em>socket<\/em> perch\u00e9 lo trover\u00e0 gi\u00e0 in uso.<\/p>\n\n\n\n<p>PostgreSQL <strong>non<\/strong> \u00e8 (ancora) stato modificato per interagire con systemd per l&#8217;attivazione via <em>socket<\/em>, ma systemd ha un modulo, chiamato <a href=\"https:\/\/manpages.debian.org\/testing\/systemd\/systemd-socket-proxyd.8.en.html\">systemd-socket-proxyd<\/a> che pu\u00f2 essere usato in queste situazioni: si fa in modo che l&#8217;attivazione via <em>socket<\/em> comunichi con il proxy, il quale collabora con systemd e poi passa la comunicazione al programma finale.<\/p>\n\n\n\n<p>Ovviamente vale la regola che l&#8217;applicazione non pu\u00f2 usare il socket in uso da systemd. Quindi in questo caso mettiamo in ascolto sulla porta TCP il proxy e lasciamo PostgreSQL in ascolto <span style=\"text-decoration: underline;\">solo sulla pipe<\/span>.<\/p>\n\n\n\n<p>Per farlo, cambiamo il valore del parametro listen_adresses in &#8221; (stringa vuota) nel file <code>\/etc\/postgresql\/16\/linuxday\/postgresql.conf<\/code>.<\/p>\n\n\n\n<p>Poi creiamo la unit <code>\/etc\/systemd\/system\/postgresql-proxy.socket<\/code> che indica a systemd di ascoltare sulla porta TCP 5434 e poi la unit <code>\/etc\/systemd\/system\/postgresql-proxy.service<\/code> che viene attivata da systemd alla connessione TCP, e che avvia systemd-socket-proxyd che a sua volta comunicher\u00e0 con postgresl tramite <em>pipe<\/em>.<\/p>\n\n\n\n<p>La prima unit \u00e8 un semplice <em>socket<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cat \/etc\/systemd\/system\/postgresql-proxy.<strong>socket<\/strong>\n&#91;Socket]\nListenStream=5434\n\n&#91;Install]\nWantedBy=sockets.target<\/code><\/pre>\n\n\n\n<p>La seconda <span style=\"text-decoration: underline;\">dipende<\/span> da PostgreSQL, quindi quando il <em>socket<\/em> verr\u00e0 richiamato, systemd attiver\u00e0 prima PostgreSQL e poi il proxy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cat \/etc\/systemd\/system\/postgresql-proxy.service\n&#91;Unit]\nRequires=postgresql@16-linuxday.service\nAfter=postgresql@16-linuxday.service\nRequires=postgresql-proxy.socket\nAfter=postgresql-proxy.socket\n\n&#91;Service]\nType=notify\nExecStart=\/usr\/lib\/systemd\/systemd-socket-proxyd \/var\/run\/postgresql\/<strong>.s.PGSQL.5434<\/strong>\nPrivateTmp=yes\nPrivateNetwork=yes<\/code><\/pre>\n\n\n\n<p>facciamo rileggere i nostri file a systemd e li attiviamo; poi controlliamo che il <em>socket<\/em> sia in LISTEN<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo systemctl daemon-reload\n$ sudo systemctl enable postgresql-proxy.socket\n$ sudo systemctl start postgresql-proxy.socket\n$ sudo netstat -alnp | grep :543\ntcp6       0      0 :::5434                 :::*                    LISTEN      1\/init<\/code><\/pre>\n\n\n\n<p>Quindi, quello che abbiamo ora \u00e8 che c&#8217;\u00e8 qualcosa in ascolto sulla normale porta di PostgreSQL 5434, ma PostgreSQL \u00e8 completamente spento.<\/p>\n\n\n\n<p>Ora cerchiamo di collegarci a PostgreSQL con il client <code>psql<\/code> indicando l&#8217;accesso tramite TCP (tramite <em>pipe<\/em> non funzionerebbe perch\u00e9 \u00e8 ancora spento). Per curiosit\u00e0 ci facciamo dire quanto tempo psql ci mette a collegarsi al database e fare una semplice query.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ time psql --host localhost --port 5434 -c \"select version();\"\npsql: errore: connection to server at \"localhost\" (127.0.0.1), port 5434 failed: FATALE:  Autenticazione Peer fallita per l'utente \"giuseppe\"\n\nreal\t0m2,542s\nuser\t0m0,002s\nsys\t0m0,006s\n\n$ time psql --host localhost --port 5434 -c \"select version();\"\npsql: errore: connection to server at \"localhost\" (127.0.0.1), port 5434 failed: FATALE:  Autenticazione Peer fallita per l'utente \"giuseppe\"\n\nreal\t0m0,093s\nuser\t0m0,034s\nsys\t0m0,004s<\/code><\/pre>\n\n\n\n<p>OK, non ha funzionato l&#8217;accesso, ma la procedura ha comunque funzionato: difatti il <em>socket<\/em> \u00e8 stato usato per comunicare con systemd, il quale ha attivato il proxy, il quale aveva come dipendenza PostgreSQL; e poi la richiesta \u00e8 arrivata a PostgreSQL che per\u00f2 l&#8217;ha rifiutata.<\/p>\n\n\n\n<p>La seconda esecuzione mostra tempi di risposta decisamente minori perch\u00e9 il <em>cluster<\/em> era gi\u00e0 attivo. D&#8217;altronde abbiamo attivato un <em>cluster<\/em> di postgresql in due secondi e mezzo su un portatile del 2013!<\/p>\n\n\n\n<p>Veniamo all&#8217;errore. Controlliamo il log di PostgreSQL per vederne il dettaglio. Il file \u00e8 <code>\/var\/log\/postgresql\/postgresql-16-linuxday.log<\/code> e l&#8217;errore \u00e8:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>023-10-14 18:07:33.669 CEST &#91;31102] giuseppe@giuseppe LOG:  il nome utente fornito (giuseppe) e il nome utente autenticato (root) non combaciano\n2023-10-14 18:07:33.669 CEST &#91;31102] giuseppe@giuseppe FATALE:  Autenticazione Peer fallita per l'utente \"giuseppe\"\n2023-10-14 18:07:33.669 CEST &#91;31102] giuseppe@giuseppe DETTAGLI:  Connection matched file \"\/etc\/postgresql\/16\/linuxday\/pg_hba.conf\" line <strong>123<\/strong>: \"local   all             all                                     peer\"<\/code><\/pre>\n\n\n\n<p>La parte importante \u00e8 la prima riga: \u00e8 stato fatto l&#8217;accesso fornendo il nome utente giuseppe, ma la <em>named pipe<\/em> \u00e8 stata scritta dall&#8217;utent root (perch\u00e9 il nostro proxy \u00e8 attivo come utente root) e l&#8217;autenticazione era di tipo <span style=\"text-decoration: underline;\">peer<\/span>. \u00c8 giusto: la configurazione predefinita di PostgreSQL, quando si accede tramite <em>pipe<\/em>, \u00e8 proprio quella che prevedere di fidarsi dell&#8217;utente che apre la <em>pipe<\/em> controllandone uid e gid.<\/p>\n\n\n\n<p>Allora cambiamo l&#8217;autenticazione da <span style=\"text-decoration: underline;\">peer<\/span> a <span style=\"text-decoration: underline;\">scram-sha-256<\/span> nel file <code>\/etc\/postgresql\/16\/linuxday\/pg_hba.conf<\/code> alla riga <strong>123<\/strong> indicata nel messaggio d&#8217;errore. Anzi, ne aggiungiamo una, subito prima, che valga solo per l&#8217;utente giuseppe:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># TYPE  DATABASE  USER      ADDRESS    METHOD\nlocal   all       giuseppe             scram-sha-256<\/code><\/pre>\n\n\n\n<p>facciamo rileggere il file <code>pg_hba.conf<\/code> a PostgreSQL e poi riproviamo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo pg_ctlcluster 16 linuxday <strong>reload<\/strong>\n$ time psql --host localhost --port 5434 -d postgres -c \"select version();\"\nInserisci la password per l'utente giuseppe:\n                        version\nPostgreSQL 16.0 (Debian 16.0-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit\n(1 riga)\n\nreal\t0m1,807s\nuser\t0m0,045s\nsys\t0m0,004s<\/code><\/pre>\n\n\n\n<p>FUNZIONA!<\/p>\n\n\n\n<p>Adesso, molto emozionati, ci rendiamo conto che siamo a met\u00e0 strada: si potr\u00e0 fare in modo che systemd spenga PostgreSQL dopo un po&#8217; che non \u00e8 usato?<\/p>\n\n\n\n<p>Pare che questo non sia ancora possibile se si utilizza il proxy: <a href=\"https:\/\/github.com\/systemd\/systemd\/issues\/2106\">https:\/\/github.com\/systemd\/systemd\/issues\/2106<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>systemd ha tante caratteristiche interessanti, una delle quali \u00e8 l&#8217;attivazione tramite socket, vale a dire che si pu\u00f2 configurare systemd si mette in attesa su una certa porta TCP (o una pipe, o altro tipo di socket) al posto di un altro programma. Quando arriva una richiesta di connessione, systemd l&#8217;accetta e attiva il servizio [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,3,11,10],"tags":[],"class_list":["post-521","post","type-post","status-publish","format-standard","hentry","category-sysadmin","category-computer","category-debian","category-open-source"],"_links":{"self":[{"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/posts\/521","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/comments?post=521"}],"version-history":[{"count":2,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/posts\/521\/revisions"}],"predecessor-version":[{"id":523,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/posts\/521\/revisions\/523"}],"wp:attachment":[{"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/media?parent=521"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/categories?post=521"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.sguazz.it\/index.php\/wp-json\/wp\/v2\/tags?post=521"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}