Totem non riproduce più i video con Ubuntu 22.04

totem
Il logo di Totem

Conosciuto anche come Videos, Totem è un riproduttore di film progettato per GNOME [3].

Dopo aver avanzato il mio OS alla versione di Ubuntu 22.04 ho notato un problema (nel senso: da un po’ non vedevo i video e facendone girare uno delle vacanze estive mi sono accorto del probema che solo poi ho trovato essere legato alla nuova release di Ubuntu).

Facendo doppio click su un video, si apre il programma ma, invece di avviarsi il video, appare una finestra di alert con scritto

Il file non è stato trovato

Ho trovato la descrizione del problema nel forum [1] assieme alla soluzione, meglio spiegata in [2].

In breve, con la versione 22.04 di Ubuntu è stata introdotta una incompatibilità con GStreamer, un framework multimediale per gestire media file [4]. Con Ubuntu 22.04 non è più necessario per cui si può eliminare ed elimare con esso il problema:

$ sudo apt remove gstreamer1.0-vaapi

Problema risolto, i video si vedon di nuovo.

Riferimenti

Appunti di Sistemi Operativi (OS)

Sistemi operativi
Sistemi operativi

Un piccolo breviario con la spiegazione succinta dei principali concetti riguardante i sistemi operativi (operating systems, OS).

Sistemi Operativi

Sono i software fondamentali per poter utilizzare di una macchina di tipo Von Neumann (quella col processore e la memoria per dati/programmi). Consentono di usare il processore, la memoria, il network, l’I/O, di gestire l’allocazione in memoria dei programmi e dei processi e di regolare il loro accesso alle risorse e le interazioni tra di loro.

Come diceva la mia prima insegnante di programmazione, un computer senza sistema operativo è solo ferraglia (hardware, in inglese).

Kernel

Il kernel è il sottoinsieme di programmi del sistema operativo che sovraintende all’accesso delle risorse hardware (processore, RAM, memoria di massa, rete e I/O). I programmi “utente” quelli che servono ad eseguire le azioni della vita quotidiana – far girare un sito web, gestiore un foglio di calcolo, montare un video, mantenere un database – accedono alle risorse hardware solamente per mezzo del sistema operativo (e se lo saltano la circostanza è eccezionale e si fa solo in casi estremi).

Quali sono i principali compiti svolti dal kernel dei sistemi operativi?

Sintetizzando al massimo si possono individuare nel kernel le seguenti 2 attività:

  1. Allocazione delle risorse (memoria e I/O)
  2. Pianificazione e gestione dei processi (scheduling, ciclo di vita, segnalazioni tra processi, arbitraggio)

Allocazione delle risorse (RAM e I/O)

Spazio kernel e spazio utente

I computer moderni dividono la memoria in spazio del kernel e spazio utente. Lo spazio utente è il luogo in cui viene eseguito il software applicativo, mentre lo spazio del kernel è dedicato al lavoro dietro le quinte necessario per far funzionare un computer, come l’allocazione della memoria e la gestione dei processi. A causa di questa separazione tra spazio kernel e spazio utente, il lavoro svolto dal kernel non è in genere visibile all’utente.

Il sistema operativo è responsabile dell’occupazione/liberazione dello spazio di memoria per i processi. Mantiene le mappature dalla memoria virtuale a quella fisica (che sono archiviate nelle tabelle delle pagine). Decide anche quanta memoria allocare a ciascun processo e quando un processo deve essere rimosso dalla memoria.

L’accesso alla memoria RAM è un processo molto dispendioso anche in termini di tempo. Per eseguire operazioni di I/O in memoria infatti il microprocessore si avvale di un dispositivo hardware di controllo dedicato detto Memory Controller. Il MC gestisce l’individuazione delle locazioni di memoria attraverso l’indirizzamento e l’I/O dei dati da e verso il processore attraverso l’infrastruttura del bus dati (data bus). Nei processori di ultima generazione il memory controller è stato integrato nei microprocessori (infatti si chiama IMC – Integrated Memory Controller) ma l’infrastruttura fisica del bus (con i suoi clock) e la RAM rimangono pur sempre periferici e quindi introducono latenze.

Se un programma deve inizalizzare delle variabili non va a prendersi la RAM direttamente, ma se la fa allocare dal kernel. Ogni processo ha a disposizione una memoria virtuale visibile come heap (“mucchio”, area di memoria utilizzata per variabili globali) o come stack (“pila”, area di memoria utilizzata per chiamate a funzione, ricorsione e variabili locali): per ogni processo il sistema operativo gli presenta quello che a lui sembra essere l’intervallo di memoria completamente indirizzabile. Quindi su una macchina a 32 bit, ogni processo “pensa” di avere a sua disposizione 4 GB di memoria contigua.

In realtà, il sistema operativo, dietro le quinte, è impegnato a mappare le allocazioni di memoria virtuale su blocchi reali di memoria fisica. Quindi, ad esempio, un’allocazione di memoria virtuale di 400 byte viene mappata su 100 blocchi fisici da 4 byte. Quei blocchi fisici non devono essere contigui (e quasi mai lo sono – nulla impedisce che accada, ma su una macchina che esegue qualsiasi tipo di lavoro, è altamente improbabile) ma l’allocazione della memoria virtuale deve essere contigua.

Memoria Virtuale

La memoria virtuale è una tecnica che dà a un programma applicativo l’impressione di avere una memoria di lavoro RAM non frammentata, mentre in realtà può essere fisicamente frammentata e si può persino estendere nello spazio di archiviazione su disco. I sistemi che utilizzano questa tecnica semplificano la programmazione di applicazioni di grandi dimensioni e utilizzano la memoria fisica reale (ad es. RAM) in modo più efficiente rispetto a quelli senza memoria virtuale.

Attenzione che la “memoria virtuale” non è solo “l’utilizzo dello spazio su disco per estendere le dimensioni della memoria fisica”. L’estensione della memoria è una normale conseguenza dell’utilizzo di tecniche di memoria virtuale, che può essere gestita con le sovrapposizioni (overlay) o lo scambio completo di programmi e relativi dati (swap) su disco mentre questi sono inattivi.

Per “memoria virtuale” si intende più precisamente l’inganno che viene fatto ai programmi facendo credere loro di utilizzare grandi blocchi di indirizzi contigui.

malloc

Facciamo un esempio. Un programma C alloca dinamicamente una locazione di memoria. Cioè lo fa a runtime, non viene riservata una memoria iniziale dal compilatore (ad esempio come quando dichiaro una variabile int) ma lo fa in corsa per esempio per dichiarare un array di dimensione arbitraria che lo user si inventa al momento, mentre il programma sta già girando.

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
 
    // Questo puntatore contiene l'indirizzo base del blocco creato
    int* ptr;
    int n, i;
 
    printf("Scrivi il numero di elementi dell'array:");
    scanf("%d",&n);
 
    // Alloca dinamicamente la memoria usando malloc()
    ptr = (int*)malloc(n * sizeof(int));
...

In questo esempio al momento della compilazione non sappiamo quanta memoria servirà al programma perché è l’utente che lo decide dopo averlo avviato.

L’istruzione malloc() riserverà al processo, che rappresenta la realizzazione fisica del programma C, un’area di memoria (virtuale) n volte la dimensione di una variabile di tipo intero. Per esempio se la variabile intera è rappresentata in 32 bit (4 byte) e n=100, il processo dovrà allocare 400 byte.

Dove sono realmente questi 400 byte? Il programma non lo sa (il programmatore ancora meno).

Il kernel gli assegnerà 400 byte che lui – il programma – vedrà come contigui, ma dietro le quinte il kernel sa esattamente nella RAM fisica dove si trovano questi 400 byte che possono anche essere sparpagliati. Per mantenere questa associazione il kernel usa una tecnica chiamata paginazione (paging) che ha il compito non soltanto di fornire una mappatura tra indirizzi virtuali e fisici ma anche di segregare i processi in aree di memoria stagne e di fornire i puntamenti ai blocchi di RAM non contigui in modo tale che la memoria virtuale risulti non frammentata.

Gestione dei processi

Un programma si può definire come una sequenza (“passiva”) di istruzioni scritte in un determinato linguaggio che ha lo scopo di risolvere un problema o, più in generale, di effettuare un’attività.

Processo

È l’effettiva esecuzione delle istruzioni che definiscono il programma. Un programma può essere suddiviso in più processi; per esempio se un programma apre più finestre, ogni finestra è un processo separato. I processi sono definiti e gestiti dal sistema operativo.

Un singolo processore del computer esegue una o più istruzioni alla volta (per ciclo di clock), una dopo l’altra. Per consentire agli utenti di eseguire più programmi contemporaneamente (ad esempio, in modo che il tempo del processore non venga sprecato in attesa di input da una risorsa), i sistemi informatici a processore singolo possono eseguire la divisione del tempo o time sharing.
La condivisione del tempo consente ai processi di passare da uno stato di esecuzione ad uno stato di attesa per continuare ad essere eseguiti.
Nella maggior parte dei casi ciò avviene molto rapidamente, fornendo l’illusione che diversi processi vengano eseguiti “contemporaneamente”. Questo è noto come concorrenza o multiprogrammazione.

Pianificazione e gestione dei processi.

Il sistema operativo mantiene separati i suoi processi e alloca le risorse di cui hanno bisogno in modo che abbiano meno probabilità di interferire tra loro e causare errori di sistema (ad esempio deadlock – la situazione in cui due processi aspettano a vicenda che l’altro acceda ad una locazione di memoria – o thrashing – situazione in cui il processo spende più tempo a paginare la memoria che ad eseguire le istruzioni). Il kernel può anche fornire meccanismi per la comunicazione tra processi per consentire ai processi di interagire in modi sicuri e prevedibili.

Arbitraggio dei processi

Quando il kernel decide che bisogna parcheggiare un processo (P1) e servirne un altro (P2) in base a un qualche criterio, avviene il salvataggio in una particolare area della RAM del set di registri e flag del processore, del program counter (il registro che contiene l’indirizzo della prossima istruzione del programma da caricare nel processore) e dello stack (che rappresenta la nidificazione delle chiamate a funzione) relativi al processo P1 e preleva dalla stessa area, in una locazione diversa destinata al processo P2, i diversi valori per lo stesso set di registri e dello stack per il processo P2 e li carica nel processore per poi avviarne l’esecuzione. Questa sequenza scrittura – lettura si chiama commutazione del contesto (context switching).

Sincronizzazione dei processi concorrenti.

Se ci sono processi che concorrono a scrivere in un stessa locazione di memoria (ad esempio due utenti che condividono un conto bancario ed eseguono operazioni allo stesso momento), il sistema operativo deve orchestrare le operazioni su questa locazione di memoria perché questa rappresenti in ogni istante il risultato che tutti si attendono.

Esempio

Alice e Bob aprono un conto bancario condiviso. Il loro saldo iniziale è di 0 €. Ognuno di loro deposita 100 €. Ci aspettiamo che il saldo finale debba essere di 200 €.

Ma consideriamo la seguente sequenza di operazioni:

  • 1) Alice legge Saldo (legge 0 €),
  • 2) Alice incrementa Saldo ma non lo salva ancora: Saldo + 100 €: a questo proposito si consideri che l’istruzione seguente
    s = s + 100
    viene eseguita in più passaggi dal processore (eax, eay sono registri interni del processore – il processore esegue tutte le operazioni aritmetiche internamente perché così opera in modo enormemente più veloce – e s la locazione di memoria virtuale che contiene il Saldo)
    mov     eay, 0
    mov     eax, 100
    add     eax, eay
    push    s
  • 3) Bob legge il Saldo (legge anche lui 0 €, poiché Alice non ha ancora scritto il nuovo Saldo, non è ancora arrivata a push s),
  • 4) Bob incrementa il Saldo e neanche lui lo sovrascrive: Saldo + 100 €,
  • 5) Alice scrive il Saldo (scrive 100 €),
  • 6) Bob scrive Saldo (scrive anche lui 100 €).

Questa sequenza porta al saldo finale di 100 € (che non è corretto). Questo esempio illustra chiaramente l’importanza della sincronizzazione tra i processi di Alice e Bob. In particolare, la seguente sequenza (se applicata) produrrebbe risultati corretti:

  1. Alice legge,
  2. Alice incrementa,
  3. Alice scrive,
  4. Bob legge,
  5. Bob incrementa,
  6. Bob scrive .

Potrebbero esserci altre sequenze di letture e scritture che producono ugualmente risultati corretti, però l’importante è vedere come il sistema operativo esegua l’arbitraggio dei due processi affinché venga eseguito prima completamente il processo di Alice – che è partito prima – e poi quello di Bob.

Commutazione del contesto

Un altro dei compiti del kernel è fare in modo che ogni processo abbia accesso alla CPU e alle sue risorse per un certo periodo limitato di tempo. Questo è dovuto al principio che un processore serve un processo alla volta. Ultimamente – negli ultimi 20 anni – con l’avvento dei processori multicore abbiamo effettivamente la possibilità di eseguire più processi in contemporanea moltiplicando il numero di CPU. Attualmente per i processori commerciali siamo a 8 core per cui si possono eseguire 8 processi contemporaneamente. Ma il principio è sempre quello: una CPU (un core) esegue un solo processo alla volta. La tecnica del multitasking prevede un uso a divisione di tempo della singola CPU (time sharing).

Risorse web

Funzione anonima↔Espressione lambda

Espressione lambda
Espressione lambda

C’è un concetto che per tanti anni mi è apparso misterioso, quello di espressione lambda o lambda expression in inglese – che poi mi è risultato analogo a quello di funzione anonima – da quando me l’hanno raccontato nel corso di Automi e Linguaggi Formali all’Università nell’ambito del corso di LISP moltissimi anni fa.

Sì, faccio pubblicamente ammenda di questa cosa, dovrei saperlo e non lo so.

Non è in ogni caso un concetto particolarmente complicato.

Espressione lambda

Una espressione lambda è una funzione senza nome (o anonima) in cui descrivo solo quali sono gli argomenti e i risultati della funzioni.

Una sintassi generale non legata ad alcun linguaggio di programmazine potrebbe essere:

argomenti -> operazioni

Per chi ha qualche confidenza con la matematica, una definizione di funzione di solito si scrive così:

\begin{gathered}
	f: \mathbb{R} \rightarrow \mathbb{R} \\
	x     \mapsto    2x 
\end{gathered}

Ecco: l’epressione nella seconda riga è esattamente la lambda expression.

Definizione di una espressione lambda in Python

Vediamo come si definiscono e si utilizzano le espressioni lambda in vari linguaggi di programmazione.

In Python le funzioni ordinarie si definiscono con la keyword def

def double(x):
  return 2*x

Quindi c’è la parola chiave def, poi il nome della funzione, l’elenco degli argomenti – tra parentesi tonde – e infine il corpo della funzione, complicato quanto si vuole.

Invece le espressioni lambda si definiscono con la keyword lambda, non è necessario dare loro un nome e occupano una sola riga:

lambda x: 2*x

quindi in generale

lambda argomenti: espressione

Poi per poterle invocare un nome bisogna comunque darglielo, ad esempio per l’espressione lambda precedente:

double = lambda x: 2*x

y = double(5)

Funzioni anonime in PHP

Le funzioni anonime in PHP sono l’equivalente delle espressioni lambda per Python:

<?php
$double = function($x) {
    return 2*$x;
}

$y = $double(5);

Ora, si potrebbe obiettare che comunque diamo sempre un nome a queste funzioni anonime, per cui: sono anonime o no?

L’uso più appropriato che da il nome di funzione anonima è la definzione di una callback, ossia una funzione che deve venire invocata quando si verifica un determinato evento.

Utilizzo in Javascript

In Javascript possiamo fare questo esempio: una funzione viene chamata quando si verifica un evento (un click, del mouse, la pressione su un link, la digitazione di un tasto della tastiera e così via). La funzione da invocare (che viene detta callback) è definita e invocata sul posto e quindi non ha alcuna necessità di avere un nome:

document.queryselector("#callback-btn")
    .addEventListener("click", function() {    
      console.log("User has clicked on the button!");
});

In questo esempio la funzione di callback viene passata alla funzione addEventListener() che ha lo scopo di agganciare l’evento “click” ad una funzione che svolge un determinato compito. In questo caso specifico, quando questo click si verifica, il programma va a scrivere una riga di log. Non c’è alcuna necessità di invocarle altrove questa funzione per cui va bene anche non darle un nome, forma a cui si perviene con la sintassi function() (senza il nome tra la keyword e le parentesi come si fa di solito).

Risorse web

Problema con l’uso della libreria curl con PHP

curl-logo
curl-logo

Da tempo sono perseguitato da un errore che si ripete (per quanto sospetto) ad ogni aggiornamento di php e che riguarda la libreria curl.

Sia l’invocazione da Apache che da client mi presentano un errore con questo tono:

$ php -r "curl_init();"
PHP Warning:  PHP Startup: Unable to load dynamic library 'curl.so' (tried: 
    /usr/lib/php/20210902/curl.so    (/usr/lib/php/20210902/curl.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4), 
    /usr/lib/php/20210902/curl.so.so (/usr/lib/php/20210902/curl.so.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4)
) in Unknown on line 0
PHP Fatal error:  Uncaught Error: Call to undefined function curl_init() in Command line code:1
Stack trace:
\#0 {main}
  thrown in Command line code on line 1

Un paio di volte ho pensato di aver risolvto il problema (vedi link in fondo al post) ma il problema è risaltato fuori al primo aggiornamento proposto da Canonical.

È stato stressante non trovare una soluzione in tutti questi anni perché non ne avevo il tempo. Solo che bisogna ad un certo punto venire ad un compromesso tra le cose da fare e lo stress che ci procura il nostro lavoro. Passato un certo segno, lo stress non è più tollerabile per cui le cose da fare passano assolutamente in seconda categoria di importanza.

Fondamentalmente l’articolo che mi trovavo sempre a leggere quando googlavo su questo argomento è quello riportato con Risorsa web [1] con la risposta illuminante dello sviluppatore finlandese:

And there’s your problem.

Toni Viemerö

Il riferimento che fa todeveni è al fatto che un aggiornamento manuale della libreria curl ha rotto quella di sistema.

Intanto mi assicuro che il file oggetto esista

$ stat /usr/lib/php/20210902/curl.so
  File: /usr/lib/php/20210902/curl.so
  Dim.: 117000      Blocchi: 232        Blocco di IO: 4096   file regolare
Device: 801h/2049d  Inode: 12327696    Coll.: 1
Accesso: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Accesso  : 2022-09-09 10:21:33.681311330 +0200
Modifica : 2022-08-15 11:40:11.000000000 +0200
Cambio   : 2022-09-02 15:54:45.221958306 +0200
Creazione: 2022-09-02 15:54:45.181957917 +0200

Il file c’è. Poi cerco se è stato agganciato per PHP. Per vedere questo devo vedere la directory dei mods-available del PHP (non quella di Apache!):

$ cd /etc/php/8.1/mods-available/
$ ll | grep curl
-rw-r--r-- 1 root root   68 dic 31  2021 curl.ini
$ $ cat curl.ini 
; configuration for php curl module
; priority=20
extension=curl.so

Sempre nell’articolo in cui todeveni spiega cos’è successo trovo questo comando che mi conferma che c’è un problema nella libreria:

$ ldd /usr/lib/php/20210902/curl.so 
/usr/lib/php/20210902/curl.so: 
   /usr/local/lib/libcurl.so.4: 
      no version information available (required by /usr/lib/php/20210902/curl.so)
   linux-vdso.so.1 (0x00007fffe63f1000)
...

Il comando ldd (List Dynamic Dependencies) stampa gli oggetti condivisi (librerie condivise) richiesti da ciascun programma o oggetto condiviso specificato nella riga di comando. Un esempio del suo utilizzo e output è il seguente:

marcob@jsbach:/usr/local/lib$ ldd /bin/ls
	linux-vdso.so.1 (0x00007ffe77541000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fda76ff6000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fda76dce000)
	libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fda76d32000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fda77069000)

Tornando al file /usr/lib/php/20210902/curl.so, se cerco le sue dipendenze trovo

marcob@jsbach:/etc/php$ ldd /usr/lib/php/20210902/curl.so 
/usr/lib/php/20210902/curl.so: /usr/local/lib/libcurl.so.4: no version information available (required by /usr/lib/php/20210902/curl.so)
	linux-vdso.so.1 (0x00007fffe63f1000)
	libcurl.so.4 => /usr/local/lib/libcurl.so.4 (0x00007f9a73200000)
...

Attenzione: trovo che il file nella libreria di PHP (/usr/lib/php/20210902/) dipende da un file posizionato nalla cartella /usr/local/lib/ e però è quest’ultimo ad avere qualche problema.

Provo a localizzare il file libcurl.so.4; ce n’è una marea:

$ locate libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.0/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.5/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.8/amd64/libcurl.so.4
/home/marcob/.snap/auxdata/netcdf_natives/8.0.9/amd64/libcurl.so.4
....
/usr/local/lib/libcurl.so.4 <-- questo è il file che vedo con ldd /usr/lib/php/20210902/curl.so 
/usr/local/lib/libcurl.so.4.4.0
/var/lib/flatpak/runtime/org.freedesktop.Platform/x86_64/18.08/1f7a5575c84c1df838ff07540cbc155e1eb78fee764860cbcbfab6d766328588/files/lib/x86_64-linux-gnu/libcurl.so.4
...

Però effettivamente il php punta a quello nella cartella /usr/local/lib/.

Attenzione: se cerco quali sono le librerie da da cui dipende il file in questa cartella non vedo nessun errore:

$ ldd /usr/local/lib/libcurl.so.4 | grep curl
$

ma non trovo neanche una dipendenza.

Ora però vediamo chi è davvero questo file so:

$ ls -l /usr/local/lib/libcurl.so.4
lrwxrwxrwx 1 root root 16 set  7 11:23 /usr/local/lib/libcurl.so.4 -> libcurl.so.4.4.0

Questo file non fa riferimento ad una libreria di sistema ma al file libcurl.so.4.4.0 che è stato buildato il 23/05/2017 e che ormai non è più allineato con quello ufficiale rilasciato da Canonical.

La soluzione arriva dalla Risorsa Web [4].

Dunque siamo alla soluzione, che comprende due operazioni

Rimuovo il file .so dalla /usr/lib/local

marcob@jsbach:/etc/php$ cd /usr/local/lib
marcob@jsbach:/usr/local/lib$ ll
totale 14368
drwxr-xr-x 10 root root     4096 set  7 11:23 ./
drwxr-xr-x 19 root root     4096 set 24  2021 ../
-rw-r--r--  1 root root     1957 set  2 17:07 curl.txt
-rw-r--r--  1 root root  1006444 mag 23  2017 libcurl.a
-rwxr-xr-x  1 root root     1042 mag 23  2017 libcurl.la*
lrwxrwxrwx  1 root root       16 set  7 11:23 libcurl.so.4 -> libcurl.so.4.4.0*
-rwxr-xr-x  1 root root   542272 mag 23  2017 libcurl.so.4.4.0*
-rw-r--r--  1 root root  4129514 gen 30  2018 libfilezilla.a
...
$ 
marcob@jsbach:/usr/local/lib$ sudo unlink libcurl.so.4
marcob@jsbach:/usr/local/lib$

E lo faccio puntare ad una libreria di sistema

$ sudo ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4 
$ ll libcurl.so.4 
lrwxrwxrwx  1 root root       38 set  9 11:35 libcurl.so.4 -> /usr/lib/x86_64-linux-gnu/libcurl.so.4

Praticamente sono passato da

libcurl.so.4 -> libcurl.so.4.4.0

a

libcurl.so.4 -> /usr/lib/x86_64-linux-gnu/libcurl.so.4

Ora, se ripeto un comando che dava l’errore:

marcob@jsbach:/usr/local/lib$ php -r "curl_init();"
marcob@jsbach:/usr/local/lib$

Ma qualsiasi comando con php dovrebbe darlo e non lo da più. In particolare quello che mi conferma che ora il modulo curl è caricato per il client:

marcob@jsbach:/usr/local/lib$ php -m | grep curl
curl

Faccio ripartire anche Apache:

marcob@jsbach:/usr/local/lib$ sudo apache2ctl restart
marcob@jsbach:/usr/local/lib$

E finalmente il comando phpinfo(); mi mostra il modulo caricato anche da Apache:

php curl, problema risolto
php curl, problema risolto

Risorse web

  1. Primo tentativo di fixare il problema
  2. Secondo tentativo
  3. Il file a cui punta il modulo PHP non è corretto (Todeveni)
  4. Una soluzione definitiva (spero, grazie a Chenjian e Scott Skiles)
  5. Pagina Linux man di ldd

Network: quando l’interfaccia di rete non comunica con l’esterno.

network: non raggiungo nessun host per nome
network: non raggiungo nessun host per nome

Mi è capitato un paio di volte di rimanere in braghe di tela col network: il browser mi dice che non sono connesso a internet, un ping generico all’host che voglio raggiungere mi dice questo:

$ ping www.google.com
ping: www.google.com: Nome o servizio sconosciuto

Sintomo 2: posso fare ping direttamente ad un nameserver pubblico come quello di Google:

marcob@jsbach:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=15.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=15.8 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=115 time=15.7 ms
^C

Sì, ok, non è detto che la porta ICMP di qualunque server sia aperta perché PING sia un comando che sempre ci può dire se un host è vivo oppure no. Però nel caso di Google va bene.

Sembra quindi che la mia interfaccia di rete sia attiva ma non sia in grado di raggiungere i DNS, perché l’interfaccia ha effettivamente un indirizzo IP:

$ ifconfig 
...
enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.1.23.54  netmask 255.255.255.0  broadcast 10.1.23.255
        inet6 fe80::f152:defa:9f22:1355  prefixlen 64  scopeid 0x20<link>
        ether dc:4a:3e:db:c2:e9  txqueuelen 1000  (Ethernet)
        RX packets 394867  bytes 307996101 (307.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 280505  bytes 82589311 (82.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...

Spegnere e riaccendere l’interfaccia non da il risultato sperato

$ sudo if down/up enp1s0

Nemmeno il riavvio del servizio di networking:

$ sudo service networking restart

L’unico comando che funziona (che permette di resuscitare il nome a dominio) è aprire la configurazione di rete (Impostazioni > Rete) e impostare il Metodo ipv4 su “Automatico (DHCP”) (era impostato su “Manuale”…) :

GUI gestione network
GUI gestione network

Non so la causa di questo malfunzionamento, può essere che l’attivazione di una delle VPN che uso sconfiguri l’interfaccia cancellando i record DNS.

In effetti in questa schermata gli IP address dei server DNS primario e secondario sono scritti a mano (per un altro malfunzionamento di una VPN che non mi configura in automatico questi dati). Forse l’origine del problema è proprio qui.

Risorse web

Cercare info su un pacchetto Ubuntu 22.04

Ubuntu logo 2022
Official Ubuntu logo since 2022 March; used in the 22.04 release for the first time.

Ci sono vari modi in Ubuntu per cercare informazioni su un pacchetto, in generale sono mutuamente esclusivi (un pacchetto si gestisce con un gestore o con l’altro, mai con tutti quanti).

Esempi

$ dpkg --print-avail vim-common
Package: vim-common
Priority: important
Section: editors
Installed-Size: 323
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Architecture: amd64
Source: vim
Version: 2:7.4.712-2ubuntu4
Replaces: vim-gui-common (<< 2:7.4.488-4~)
Depends: libc6 (>= 2.3.4)
Recommends: vim | vim-gnome | vim-gtk | vim-athena | vim-nox | vim-tiny
Breaks: vim-gui-common (<< 2:7.4.488-4~)
Filename: pool/main/v/vim/vim-common_7.4.712-2ubuntu4_amd64.deb
Size: 102918
MD5sum: 53a26189ee129613ef153cc8d43dbb4c
Description: Vi IMproved - Common files
Original-Maintainer: Debian Vim Maintainers <pkg-vim-maintainers@lists.alioth.debian.org>
SHA1: 30391762a053f76f766b263f5adb9665108b12fd
SHA256: 87b12bd7bba0df3e5ffdd1fcfc38103a6cc2aeb4958717f5f7dd635051c5a2f6
Homepage: http://www.vim.org/
Description-md5: dc8579ec9ee0dc36b43d271645170c36
Supported: 9m
Task: minimal

oppure

$ apt-cache show conky
Package: conky
Architecture: all
Version: 1.12.2-1
Priority: optional
Section: universe/utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Vincent Cheng <vcheng@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 28
Depends: conky-std | conky-cli | conky-all
Filename: pool/universe/c/conky/conky_1.12.2-1_all.deb
Size: 3244
MD5sum: 471cf8fb8bcd0d625eb765dcd769a6e4
SHA1: 858a207e872da62504526dad7121734e4dcbed7c
SHA256: 64ea4228325b775b1f714cc8a1b26ca2d871de6142b8de70a017b4addf9b4b2b
SHA512: 2540b39ce9a36185646b70feba7fe42f2d62633e4aa7f8c1f14bbc8aa9a36c2b1708c2bf7f4d245835e790ff4f59ebe04ee9bc7be85c09c44bc0eb67ec33d312
Homepage: https://github.com/brndnmtthws/conky
Description-it: monitor di sistema altamente configurabile (pacchetto di transizione)
 Conky è un monitor di sistema che può visualizzare quasi tutto, sulla
 finestra root del desktop o in una propria finestra. Conky ha molti
 oggetti incorporati, oltre alla capacità di eseguire programmi o script
 esterni (sia esternamente sia attraverso l'uso interno di Lua).
 .
 Questo è un pacchetto fittizio per facilitare la transizione al nuovo
 schema di pacchettizzazione. Può essere rimosso senza problemi dopo
 l'aggiornamento/l'installazione.
Description-md5: 6ae2b0f9855afb8d0a149407cf95bd93

oppure

$ aptitude show conky
Pacchetto: conky                                    
Versione: 1.12.2-1
Stato: non installato
Priorità: opzionale
Sezione: universe/utils
Responsabile: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Architettura: all
Dimensione pacchetto installato: 28,7 k
Dipende: conky-std | conky-cli | conky-all
Descrizione: monitor di sistema altamente configurabile (pacchetto di transizione)
 Conky è un monitor di sistema che può visualizzare quasi tutto, sulla finestra root del desktop o in una propria finestra. Conky ha molti oggetti incorporati, oltre alla capacità di eseguire programmi o
 script esterni (sia esternamente sia attraverso l'uso interno di Lua). 
 
 Questo è un pacchetto fittizio per facilitare la transizione al nuovo schema di pacchettizzazione. Può essere rimosso senza problemi dopo l'aggiornamento/l'installazione.
Homepage: https://github.com/brndnmtthws/conky

oppure

$ apt show conky
Package: conky
Version: 1.12.2-1
Priority: optional
Section: universe/utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Vincent Cheng <vcheng@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 28,7 kB
Depends: conky-std | conky-cli | conky-all
Homepage: https://github.com/brndnmtthws/conky
Download-Size: 3.244 B
APT-Sources: http://it.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages
Description: monitor di sistema altamente configurabile (pacchetto di transizione)
 Conky è un monitor di sistema che può visualizzare quasi tutto, sulla
 finestra root del desktop o in una propria finestra. Conky ha molti
 oggetti incorporati, oltre alla capacità di eseguire programmi o script
 esterni (sia esternamente sia attraverso l'uso interno di Lua).
 .
 Questo è un pacchetto fittizio per facilitare la transizione al nuovo
 schema di pacchettizzazione. Può essere rimosso senza problemi dopo
 l'aggiornamento/l'installazione.

oppure ancora

$ snap info sweethome3d-homedesign 
name:      sweethome3d-homedesign
summary:   An interior design application to draw house plans & arrange furniture
publisher: Jean-Baptiste Lallement (jibel)
store-url: https://snapcraft.io/sweethome3d-homedesign
contact:   https://launchpad.net/~jibel
license:   GPL-2.0
description: |
  Sweet Home 3D is a free interior design application that helps you place
  your furniture on a house 2D plan, with a 3D preview.
  
  Available at http://www.sweethome3d.com/, this program is aimed at people
  who want to design their interior quickly, whether they are moving or they
  just want to redesign their existing home. Numerous visual guides help you
  draw the plan of your home and layout furniture. You may draw the walls of
  your rooms upon the image of an existing plan, and then, drag and drop
  furniture onto the plan from a catalog organized by categories. Each change
  in the 2D plan is simultaneously updated in the 3D view, to show you a
  realistic rendering of your layout.
snap-id: EPpzG3puHCgp1HI89uqVPTmAuKgbLhV1
channels:
  latest/stable:    6.6 2021-08-25 (15) 140MB -
  latest/candidate: 6.6 2021-08-25 (15) 140MB -
  latest/beta:      6.6 2021-08-25 (15) 140MB -
  latest/edge:      6.6 2021-08-25 (15) 140MB -

Che differenza c’è?

Con apt si accede al parco applicazioni ufficialmente distribuite da Canonical.

Con snap so accede ad altre applicazioni non facenti parte della distribuzione Ubuntu che in genere si scaricano con tutte le dipendenze necessarie. A volte queste applicazioni potrebbero non integrarsi alla perfezione con Ubuntu.

dpgk è il gestore di pacchetti di Debian di più basso livello, e apt/aptitude sono client che utilizzano dpkg per installare, aggiornare o rimuovere pacchetti. Nel man di dpkg infatti si legge:

dpkg is a medium-level tool to install, build, remove and manage Debian packages.  
The primary and more user-friendly front-end for dpkg 
as a CLI (command-line interface) is apt(8) and 
as a TUI (terminal user interface) is aptitude(8).  
dpkg itself is controlled entirely via command line parameters, which consist of exactly one action and zero or more options. The action-parameter tells dpkg what to do and options control the behavior of the action in some way.

Dunque

  • apt è la command-line interface (il comando come espresso dall’esempio sopra) di dpgk e
  • aptitude è la terminal user interface di dpkg, una interfaccia molto oldish che mi ricorda Norton Commander
ubuntu aptitude
ubuntu aptitude

I cari vecchi anni ’80…

Riferimenti

Protetto: Configurazione Docker per un’applicazione Python/PostgreSQL

Questo contenuto è protetto da password. Per visualizzarlo inserisci la password qui sotto.

Accedere alla shell di un container Docker

Docker logo white
Docker logo white

Una volta avviato un container, come faccio ad accedere alla sua shell?

Prima di tutto occorre elencare i container:

$ sudo docker container ls -a
CONTAINER ID   IMAGE             COMMAND                   CREATED          STATUS                      PORTS                                       NAMES
42d1ab376b0d   sent_crunch       "/bin/sh -c \"/bin/ba…"   12 minutes ago   Exited (0) 12 minutes ago                                               mybeautifulproject-app-1
e18da64098a2   postgis/postgis   "docker-entrypoint.s…"    23 hours ago     Up 10 minutes (healthy)     0.0.0.0:5433->5432/tcp, :::5433->5432/tcp   mybeautifulproject-postgres_db-1

Qui nel mio caso l’unico container running (al quale ha senso quindi collegarsi) è il e18da64098a2. Quindi

sudo docker exec -it e18da64098a2 /bin/bash
postgres@e18da64098a2:/$ 

Fatto.

I file docker-compose

Docker logo white
Docker logo white

Lo sviluppo di applicazioni con l’utilizzo di Docker può diventare difficile quando si creano più container per erogare più servizi. Impariamo Docker Compose, lo strumento che ci aiuterà a eseguire ambienti applicativi multi-container.

In questo tutorial impareremo tutto su Docker Compose, i vantaggi dell’utilizzo di questo strumento, i suoi casi d’uso e le sue funzionalità.

Cos’è Docker Compose?

Un’applicazione può essere costituita da più contenitori che eseguono servizi diversi. Può essere complicato avviare e gestire i contenitori manualmente, quindi Docker ha creato uno strumento utile che aiuta ad accelerare il processo: Docker Compose.

Docker Compose è un software utilizzato per definire ed eseguire applicazioni Docker multi-container. Può gestire più container contemporaneamente nell’ambiente di produzione, staging, sviluppo, test e CI [Continuous Integration]. Pertanto, utilizzare Docker Compose per gestire l’intero ciclo di vita dello sviluppo del software [SDLC, software development life cycle].

Docker Compose funziona applicando le regole definite in un file docker-compose.yaml. Il file YAML configura i servizi dell’applicazione e include regole che specificano come si desidera che vengano eseguiti. Con questo file, puoi avviare, interrompere o ricostruire tutti i servizi utilizzando un unico comando. Inoltre, puoi controllare lo stato di un servizio, visualizzare gli output del registro ed eseguire comandi una tantum.

Docker Compose viene fornito in due “gusti”:

  • standalone: occorre installare un nuovo programma che è docker-compose, per questo di veda [2].
  • full package: se si è installato docker, sarà anche disponibile l’opzione compose per cui si usa il comando docker compose senza dover installare altro.

Casi d’uso di Docker Compose

I casi in cui risulta utile usare Docker Compose sono i seguenti:

  • Ambienti di test automatizzati. Compose supporta il test automatizzato, che è una parte essenziale di CI/CD in quanto può creare e distruggere facilmente l’ambiente di test richiesto. Gli sviluppatori possono definire e configurare l’ambiente necessario per l’esecuzione di test end-to-end automatizzati utilizzando il file Docker Compose appropriato.
  • Installazioni su singolo host. In Docker Compose, i container sono progettati per essere eseguiti su un singolo host poiché sono stati tradizionalmente focalizzati sullo sviluppo e sui flussi di lavoro di test.
  • Ambienti di sviluppo. Compose è un modo semplice e veloce per avviare progetti in quanto può creare rapidamente nuovi ambienti di sviluppo isolati. Il software documenta e configura tutte le dipendenze del servizio dell’applicazione (inclusi database, cache, API del servizio Web, ecc.). Consente di creare e avviare uno o più contenitori per ciascuna dipendenza utilizzando un unico comando.

I vantaggi di Docker Compose

Ecco alcuni dei principali vantaggi dell’utilizzo di Docker Compose:

  • Configurazione semplice e veloce. Grazie agli script YAML e alle variabili di ambiente, puoi configurare o modificare facilmente i servizi applicativi.
  • Comunicazione interna sicura. Compose crea una rete per tutti i servizi da condividere. Ciò aggiunge un ulteriore livello di sicurezza per l’app poiché non è possibile accedere ai servizi dall’esterno.
  • Portabilità e supporto CI/CD. Poiché tutti i servizi sono definiti all’interno del file docker-compose, gli sviluppatori possono facilmente accedere e condividere l’intera configurazione. Estraendo il file YAML e il codice sorgente, possono avviare l’ambiente in pochi minuti. Ciò contribuisce a creare e abilitare una pipeline CI/CD efficiente.
  • Uso efficiente delle risorse. Docker Compose consente di ospitare più ambienti isolati su un solo host. L’esecuzione di tutto su un singolo componente hardware ti consente di risparmiare molte risorse. Le sue caratteristiche che gli consentono di memorizzare una configurazione nella cache e di riutilizzare i contenitori esistenti, contribuiscono all’uso efficiente delle risorse.

Caratteristiche di Docker Compose

Esistono diverse importanti funzionalità di Docker Compose che offrono i vantaggi sopra menzionati.

Hosting di più ambienti isolati su un singolo host

Per impostazione predefinita, il nome del progetto è il nome di base della directory del progetto, mentre la directory del progetto è la directory di base del file docker-compose.yml. È possibile modificare i valori predefiniti:

  • Impostando il nome del progetto utilizzando l’opzione della riga di comando -p o la variabile di ambiente COMPOSE_PROJECT_NAME.
  • Impostando la directory del progetto usando la variabile di ambiente il parametro --project-directory da linea di comando.

È possibile utilizzare questa funzionalità su un host di sviluppo per eseguire copie stabili di ciascun ramo di funzionalità del progetto creando più copie dell’ambiente con nomi diversi.

Supporto delle variabili di ambiente

È possibile personalizzare i contenitori per ambienti o utenti diversi aggiungendo variabili di ambiente nel file docker-compose. Ciò offre maggiore flessibilità durante la configurazione dei contenitori con Compose, poiché i valori delle variabili non sono codificati nella configurazione.

I valori delle variabili possono essere impostati nell’ambiente della shell (da cui si esegue docker-compose) o in un file .env (memorizzato nella directory del progetto). Per impostazione predefinita, Docker Compose applica i valori specificati nel file .env. Tuttavia, i valori impostati nell’ambiente shell hanno la precedenza su quelli del file .env.

Ad esempio posso definire così il nome del progetto, in base a quanto visto poco fa: nella directory del progetto (quella in cui c’è il file docker-compose-yml) creo un file .env che contiene la linea:

COMPOSE_PROJECT_NAME=My Beautiful Project

Compose cerca il file automaticamente nella directory del progetto, oppure nelle variabili di ambiente impostati da shell.

Attenzione! I valori impostati nell’ambiente shell hanno la precedenza su quelli impostati nel file .env.

Se lancio la composizione del progetto, tra i log leggo:

$ sudo docker compose up
[+] Running 2/2
 ⠿ Container mybeautifulproject-postgres_db-1  Recreated                                                                                                                                                 0.2s
 ⠿ Container mybeautifulproject-app-1          Created    
.....      

Conservazione dei dati sul volume

Un’altra grande caratteristica di Docker Compose è che salva i dati utilizzati dai servizi. Pertanto, non devi preoccuparti di perdere i dati creati nei contenitori. Se sono presenti contenitori di esecuzioni precedenti, Compose li troverà e copierà i relativi volumi nella nuova esecuzione.

Riutilizzo dei contenitori esistenti

Compose ricrea solo i contenitori che sono stati modificati dall’ultima esecuzione. Se non ci sono modifiche, riutilizza il contenitore esistente.

Questa funzione si basa sulla capacità del software di memorizzare nella cache le configurazioni dei container, consentendoti di configurare i tuoi servizi più velocemente.

I comandi di base di Docker Compose

L’uso di Compose è fondamentalmente un processo in tre fasi:

  1. Definire l’ambiente della app con un Dockerfile in modo che possa essere riprodotto ovunque.
  2. Definire i servizi che compongono la app nel file docker-compose.yml in modo che possano essere eseguiti insieme in un ambiente isolato.
  3. Esegui docker compose up (docker <spazio> compose) e il comando Docker Compose si avvia ed esegue l’intera app.
    In alternativa, puoi eseguire docker-compose up (docker <trattino> compose) utilizzando Composer standalone (docker-compose eseguibile binario, attenzione al trattino!).

Un esenpio di file docker-compose.yml è il seguente:

version: "3.9"  # optional since v1.27.0
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    depends_on:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

Per ulteriori informazioni sulla sintassi del file docker-compose.yml, vedere il Compose file reference.

Compose dispone di comandi per la gestione dell’intero ciclo di vita della tua applicazione:

  • Avvio, arresto e ricostruzione dei servizi
  • Visualizzazione dello stato dei servizi in esecuzione
  • Logging dell’output del registro dei servizi in esecuzione
  • Esecuzione di comandi una tantum su un servizio

Una breve sintassi di base per eseguire i comandi Docker Compose è compendiata nel seguente elenco:

ComandoDescrizione
docker compose helpmostra la guida, le istruzioni per l’uso e gli argomenti per il comando docker-compose
docker compose buildcerca tutti i servizi che contengono l’istruzione build: nel file docker-compose.yml ed esegui un docker build per ognuno
docker compose run esegue un comando una tantum su un servizio
docker compose up compila, (ri)crea, avvia e collega ai contenitori per un servizio
docker compose -f [comando] specifica la posizione di un file di configurazione docker-compose aggiungendo il flag -f
docker compose startavvia un contenitore esistente perché svolga un serivizio
docker compose stoparresta i contenitori (senza rimuoverli)
docker compose pausemetti in pausa un servizio erogato da un contenitore
docker compose unpauseriavvia dalla pausa un servizio erogato da un contenitore
docker compose downarresta i contenitori (e rimuovi contenitori, reti, volumi e immagini)
docker compose pselenca i contenitori all’interno del file di configurazione di docker-compose
docker compose imageselenca le immagini su cui sono basati i contenitori
docker compose lselenca i progetti Compose in esecuzione
Tabella – elenco dei principali comandi Docker Compse

Altri articoli betaingegneria che parlano di Docker

Riferimenti esterni

  1. What is Docker Compose su Phoenixnap.com (by Sofija Simic)
  2. Installazione di docker-compose su Ubuntu 20.04 su Phoenixnap.com (by Sofija Simic)
  3. Documentazione ufficiale Docker

Docker task#1: montare un volume

Docker types of mounts: volume
Docker types of mounts: volume

Nell’applicazione che sto progettando e che farà utilizzo di Docker, ci sono anlcune directory host che voglio vengano viste dal container dell’applicaizone. Per fare questo in Docker si usa il concetto di volume.

C’è una directory che sarà destinata ad ospitare file prodotti dall’applicazione Python che andrà a costituire il mio primo volume. Per ora mi va bene che sia una directory isolata del mio host che monto nell’immagine che conterrà la mia applicazione dockerizzata. Più avanti mi ripropongo di montare la partizione di un NAS.

Il secondo volume che mi serve è quello che contiene il codice sorgente: voglio lavorare nell’host normalmente e fare in modo che il container Docker possa accedere al codice sorgente Python up to date. Voglio evitare la situazione in cui sviluppo nell’host e poi copio, ad ogni piccola modifica, i file dentro alla directory del contenitore docker.

Voglio in definitiva che il container veda il codice live.

Montaggio di una directory host come volume di dati Docker

Possiamo quindi montare una directory esistente dall’host a un container. Questi tipi di volumi sono chiamati Volumi Host.

È possibile montare i volumi host utilizzando il flag -v e specificando il nome della directory host.

Tutto ciò che si trova all’interno della directory host è quindi disponibile nel contenitore. Inoltre, tutti i dati generati all’interno del contenitore e inseriti nel volume di dati vengono archiviati in modo sicuro nella directory host e posso accedervi dall’OS dell’host con la shell o con Nautilus.

La sintassi di base per il montaggio di una directory host è:

$ docker run -v "$(pwd)":[volume_name] [docker_image]

L’attributo "$(pwd)" indica a Docker di montare la directory in cui si trova attualmente l’utente.

L’esempio seguente illustra come farlo.

1. Innanzitutto, crea una directory di esempio sull’host con il nome tmp e spostati in essa:

$ mkdir tmp && cd tmp

2. Una volta all’interno della directory, crea un file di test per vedere se sarà disponibile dal contenitore:

$ touch file.txt

3. Controllare se esiste l’immagine che vogliamo istanziare:

$ docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED      SIZE
<none>        <none>    e7a5d4ae5d10   3 days ago   1.44GB
sent_crunch   latest    6dcb0bbb67c7   3 days ago   1.27GB

Nel mio caso l’immagine da usare è sent_crunch.

4. Quindi, usa il comando docker run per avviare un contenitore Ubuntu con la directory host collegata ad esso:

$ docker run -it -v "$(pwd)":/data1 [docker_image]

data1 è il nome del volume visto dal contenitore Docker. In definitiva, questo comando avvia il contenitore in modalità interattiva e monta un volume con il nome data1.

Nel mio esempio:

$ cd IdeaProjects/Python/SentCrunch
$ docker run -it -v "$(pwd)":/src sent_crunch
root@fbaaee970e3c:~# pwd
/app
root@fbaaee970e3c:~# ls
root@fbaaee970e3c:~# cd ..
root@fbaaee970e3c:/# ls
app  bin  boot	dev  etc  home	lib  lib32  lib64  libx32  media  mnt  opt  proc  root	run  sbin  src	srv  sys  tmp  usr  var
root@fbaaee970e3c:~# cd /src  <----- ho montato qui la directory sorgente
root@fbaaee970e3c:/src# ls -l
total 260
-rw-rw-r-- 1 root root   1626 Aug 12 13:52 Dockerfile
-rwxrwxr-x 1 root root  10758 Feb 17 15:23 GPF_Smart_Agri-sat_Cruncher.xml
-rw-rw-r-- 1 root root    359 Dec 24  2021 Logger.py
-rw-rw-r-- 1 root root   2523 Oct 12  2021 Resample.xml
-rw-rw-r-- 1 root root    542 Nov 22  2021 SentinelCruncher.iml
-rw-rw-r-- 1 root root 104523 Jul  8 15:02 SentinelCruncher.py
-rw-rw-r-- 1 root root      0 Feb  1  2022 __init__.py
drwxrwxr-x 2 root root   4096 Jul  8 15:21 __pycache__
drwxrwxr-x 2 root root   4096 Jul 11 09:19 archexport
-rw-rw-r-- 1 root root  10396 May 10 09:50 changelog.txt
-rw-rw-r-- 1 root root   1536 May  9 09:22 constant.local.py
-rw-rw-r-- 1 root root   1563 Jul  8 15:14 constant.py
-rw-rw-r-- 1 root root   1926 May  9 09:18 constant.stage.py
-rw-rw-r-- 1 root root    665 Nov  9  2021 country.xml
-rw-rw-r-- 1 root root   1086 Aug  9 13:08 docker-compose.yml
-rw-rw-r-- 1 root root    851 Jul 27 06:14 docker_compose.yml
-rw-rw-r-- 1 root root      0 Jul  8 10:21 exportFile.dmp
-rw-rw-r-- 1 root root  16799 Oct  7  2021 install_pg.log
drwxrwxr-x 3 root root   4096 Nov 18  2021 latex
-rw-rw-r-- 1 root root  34498 Feb 25 16:02 libcruncher.py
-rw-rw-r-- 1 root root      0 Nov 18  2021 math
-rw-rw-r-- 1 root root    132 Jul 11 08:37 shp2psql.cmd
drwxrwxr-x 3 root root   4096 Jul 25 14:58 sql
drwxrwxr-x 2 root root   4096 Jul 11 16:34 test
-rw-rw-r-- 1 root root  10785 Jan 28  2022 xmlOut.xml

root@fbaaee970e3c:/src# cd test
root@fbaaee970e3c:/src/test# ./test001_dbconnect.py 
Ciao Docker!

Riferimenti