[]

  1. Introduzione
  2. Principali comandi docker
  3. Principali comandi Linux
  4. Lavorare con le immagini
  5. Lavorare con i container
  6. Docker-compose
  7. Deploy
  8. Immagini docker
  9. Risorse

Introduzione

  • Installazione di Docker  in  locale
  • Docker hub
  • Container vs Virtual Machine 
    • Entrambi danno la possibilità di eseguire applicazioni con tutti i software necessari e le buone versioni in completo isolamento.
    • La differenza principale sta nelle risorse che le VM mangiano al computer che le esegue. Una VM ha bisogno di simulare un OS e avere allocate una parte non irrisoria delle risorse fisiche.
    • Ogni container condivide il KERNEL dell’OS che lo ospita (non è necessario avere licenze aggiuntive).
    • In Windows 10 è possibile eseguire Windows + Linux container (Windows 10 ha built-in anche il Linux Kernel).
    • E’ un processo particolare perchè ha il suo proprio file system (fornito dall’immagine docker che è eseguita nel container).
    • Un container interroga il server tramite RESTful API (Architettura Client/Server.
  • Docker Architecture =>
    1. Docker deamon
      • E’ un servizio in esecuzione nel docker server. 
      • E’ responsabile della gestione dei container.
        • Può attivarli o disattivarli
        • Registra i log delle attività dei container.
        • Fa il build dei container sulla base delle ‘images’ scelte.
        • Espone strumenti di gestione tramite API.
      • Immagini
        • Sono file statici che contengono il software da eseguire.
        • Sono memorizzati nel file system e aspettano di essere eseguiti.
        • Sono i mattoni per creare i container.
    2. Registry (docker image repository)
      • Sono dei repository dove è possibile trovare le immagini con cui creare i container.
      • Possono essere publici (accessibili in Internet) o privati (accessibili solo dal proprio computer o all’interno di una Intranet).
      • E’ possibile fare il ‘pull’ di un’immagine presente in un’registry’. Modificarla e poi usarla come base per il nostro container.
      • I principali registry sono:
        • DockerHub
        • Amazon ECR (Elastic container registry)
        • Azure ACR (Azure container registry)
    3. Client
      • Espone una CLI per poter realizzare tutte le operazioni necessarie per creare e gestire i nostri container.

 

Principali comandi Docker [docker..]

  • [d=docker]
    
    -- LAVORE CON LE IMMAGINI
    d info
    d images                                            [visualizzo le immagini presenti nel mio docker]
    d image ls                                          [visualizzo e immagini presenti nel mio docker]
    d image prune                                       [fa il pruning (sfoltire) delle immagini pendenti (dangling)]
    d image rm <img-id>                                 [rimuove l'immagine indicata]
    d images -q | % { docker rmi $_ }                   [rimuove tutte le immagini presenti nel mio docker]
    d history <img-name>                                [mostra tutti i layers di cui è costituita l'immagine] 
    d build -t <my-img> <app_path>                      [crea l'imm <my-image> dai file presenti in <app_path>] 
    d build -t <my-img>:<img-tag> <app_path> 
    d image tag <my-img>:<old-tag> <my-img>:<mew-tag>   [modificare il tag di un'immagine]
    -- salvare un'immagine
    d image save -o <dest-path.zip|tar> <my-rep>:<tag>  [salvo l'immagine in nel file zippato specificato]
    d image load -i <dest-path.zip|tar>                 [creo l'immagine dal file zippato specificato]
    d tag <image_old_name> <image_new_name>:tag         [modificare il nome di un'immagine]
    
    -- PUBBLICARE SU DOCKERHUB 
    creare su dockerhub un repository con lo stesso nome dell'immagine che si vuole pushare, poi =>
    d build -t <my-rep>:<new-tag>                       [1 - fare un nuovo build con il nuovo tag] 
    d image tag <my-rep>:<new-tag> <dokhub-usr>/<my-rep>:<new-tag>     
                                                        [2 - taggare il build al repo in remoto]
    d login                                             [3 - loggarsi du dockerhub]
    d push <dochub-usr>/<my-rep>:<new-tag>              [4 - fare il push dalla nuova build]
    -- policy per i nomi delle immagini che si voglio pushare sul dockerhub
    local_image_name = <docherhub_user>/<local_image_name>
    d push leperegoriot1977/<mylocalimage>
    
    LAVORARE CON I CONTAINER
    -- lancio un nuovo container
    d run hello-world                                   [docker run = docker create + docker start]
                                                        [lancia una piccola immagine di test]
    d run <my-img>[:<tag>]                              [lancia l'img, se non in locale, ne fa prima il download]
    d run -d <my-img>[:<tag>]                           [lancia l'img in (-d) detached mode cioè in background] 
    d run -name <cont-name> <my-img>[:tag]             [lancia l'img associandogli il nome <cont-name>]
    d run -it <my-cont>                                 [eseguo il cont in modo iterativo, apro la shell]
    d run -it <my-cont> <shell>                         [eseguo il cont aprendo la shell al boot (<bash> o <sh>)]
    d run -it --rm <my-image> 3                         [eseguo l'immagine <my-image>, aprendo la shell, 
                                                         --rm => elimina il container quando la shell è terminata]
    -- eseguo cont mappando porta dell'host 
    d run -d -p 80:3000 <my-img>                        [eseguo e mappa la porta host 80 alla porta 3000 del cont.
                                                         L'app del cont è visibile sull'host a http://localhost:80]
    d inspect <my-cont>                                 [Visualizzo le info relative al container indicato]  
                                                        [trovo l'IP Address a cui interrogare il container]
    d container list -a                                 [visualizzo tutti i container presenti nel mio docker]    
    -- eseguo comandi in un cont running
    d exec -it -u <user> <my-cont> <shell> 
                                                        [eseguo il cont e apro la shell con l'utente indicato] 
    d exec <cont-name> <cmd>                            [eseguo il comando indicato <cmd> nel cont col dato nome] 
    -- liberare risorse
    d container prune                                   [fa il pruning dei container, eliminando quelli stoppati] 
    --visualizzo container 
    d ps                                                [mostra i container/processi attualmente running]
    d ps -a                                             [mostra i conteiner/processi, anche quelli fermati]
    -- operazioni in loop
    d ps -a -q | % { docker rm $_ }                     [visualizzo tutti i processi attivi e li rimuovo in loop]
    -- fermo e riavvio container
    d stop <cont-id> [arresta il <container-id>]        [ferma il container <cont-id>
    d start -i <cont-id>                                [riavvia il container <cont-id> che era fermo]
    -- container logs
    d logs [-f | -n | -t] <cont-id>                     [visualizzo log]
    -- rimuovere container
    d [container] rm -f <cont-id>                       [rimuovo cont. -f forza la rimozione di un cont running]
    
    TRASFERIRE FILE DA HOST a CONTAINER
    d cp <my-cont>:<file-to-copy> .                     [Copio nella root dell'app (.) sull' host il file specificato
                                                         <file-to-copy> presente nel containter <my-cont>]
    d cp <file-to-copy> <my-cont>:<dest-path>           [Copio in <dest-path> nel cont. <my-cont> il <file-to-copy>
                                                         presente sul computer host che lancia il container stesso]
    
    BINDING HOST-CONTAINER folder (utile in DEV)
    d run -dp 80:3000 -v ${PWD}:<dest-path> <img-id>    [Collego la root dell'app sull'host con il folder <dest-path>
                                                         nel container così ogni modifica viene riflessa senza build]
    LAVORARE CON I VOLUME 
    d volume create <my-vol>                            [creo il nuovo volume <my-vol>]
    [\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes\<my-vol> => path fisico nell'host Windows]
    d volume inspect <my-vol>                           [vedo il contenuto del volume <my-vol>]
    d volume ls                                         [visualizzo la lista di volumi]  
    d volume prune                                      [rimuovo i volumi non usati]
    d run -d -p 80:81 -v <my-vol>:<vol-path> <my-img>   [eseguo mappando <my-vol> al folder <vol-path> in <my-img>]
                                                        [es: d run -d -p 5000:3000 -v app-data:/app/data react-app]
    
    PULIRE DOCKER 
    d system prune -a
    d volume rm $(docker volume ls -q -f dangling=true)
    d system df
    
    CARTELLA DOVE SI IL DISCO VIRTUALE WSL2 
    wsl -l -v                                          [visualizza lo stato delle distribuzioni WSL presenti sull'HOST]
    wsl --shutdown                                     [arresta tutte le distribuzioni WSL in esecuzione sull'HOST
    C:\Users\<my-user>\AppData\Local\Docker\wsl\data\ext4.vhdx 
                                                       [path del disco virtuale che docker usa per storare tutte le 
                                                        immagini, container quando usa WSL2]
    
    INVIARE DATI ALL'APPLICAZIONE 
    --                                                 [I parametri dopo -- sono trasmessi all'app in esecuzione nel container e non trasmessi al comando docker] CONNETTERSI A UN CONTAINER IN ESEGUIZIONE docker start <my-cont> [Avvio il container <my-cont>] docker attach --sig-proxy=false <my-cont> [Mentre un container è in esecuzione è possibile connettervici per visualizzarne i risultati] [attacco quindi la console da cui sto eseguendo i comandi all'output del processo eseguito nel container] [--sig-proxy=false fa sì che Ctrl + C non arresti il processo ma solamente distacchi la console]
    • Images vs container =>
      • Creando un’immagine docker si crea una sorta di template che può essere istanziato molte volte creando molti container
      • docker build -t imagemvc5 .
        docker run -d -p 8080:80 --name mvc5container imagemvc5

    • Mappatura porte =>
      • Nella creazione di un container è necessario mappare  esterne (dell’host)  e le porte interne (del container) 
      • Nel caso usassimo la mappatura qui sotto allora all’indirizzo http://localhost:8080 usando nel browser della macchina host risponderebbe l’applicazione che punta alla porta 91 nel container docker 

 

 

Principali comandi Linux

  • pwd                                   [print working directory]
    echo $0                               [posizione del programma shell]
    hstory                                [lista dei comandi editati finora 
                                          (E' possibile richiamarli scrivendo: !<numero_del_comando>]
    exit                                  [uscire dalla session della shell]
    clear                                 [pulire la shell]
    cd ~                                  [vado alla directory home (root per l'utente root]
    ctrl + C                              [stoppo processo troppo lento o in loop]
    
    -- working with packages
    apt                                   [advanced package manage tool, package manager in Linux (come Nuget o Npm)]
    apt list                              [lista pacchetti presenti sulla macchina]
    apt update                            [aggiorna pacchetti presenti sulla macchina]
    apt install nano                      [installare un programma, es: nano]   
    apt remove nano                       [rimuovere un programma, es: nano]    
    
    -- file system
    /                                     [root directory]
     bin                                  [cartella con eseguibili e programmi]
     boot                                 [cartella con  programmi eseguiti al boot]
     dev                                  [cartella con i file di tipo device, in Linux 1 device => 1 file]
     etc                                  [cartella con i file di configurazione]
     home                                 [è directory home dell'utente]
     root                                 [è directory home dell'utente root]
     lib                                  [cartella con le librerie e dipendenze]
     var                                  [cartella con file aggiornati frequentemente (es: application data)]
     proc                                 [cartella con i file che rappresentano i processi in esecuzione]
    
    -- visualizzazione file
    nano                                  [semplice editor]     
    cat                                   [visualizzare piccoli file]
    more                                  [scorrere una sola volta un file]
    less                                  [leggere e ricercare parole in un file]
    head -n 5 e tail -n 5                 [visualizza le prime e ultime 5 linee di un file]
     
    
    -- redirezione dell'output (>)
    [>] cat file1.txt > file2.txt         [riendirizzo l'output cat file1.txt in file2.txt, quindi copio il file]
    [>>] echo DB_USER=nico >> .bashrc     [aggiungo una linea di testo al file .bashrc]
    -- trovare file
    grep -ir Hello /etc                   [cerco ricorsivamente (-r) e insensitive (-i) la parola Hello in /etc] 
    find [<path>] -type f -name "fail*"   [visualizza nella corrente directory dei file con nome fail*]
    find [<path>] -type d                 [elenco delle sole cartelle presenti nell'attuale directory]
    
    -- concatenare istruzioni
    mkdir test ; cd test ; echo done      [; => concetena operazioni e le esegue tutte anche in caso di errore]
    mkdir test && cd test && echo done    [&& => concatena operazioni ma le esegue fino ad un errore]
    mkdir test || echo done               [|| => esegue il primo cmd. Solo se non è riuscito, eseguire il secondo]
    ls /bin | less                        [| => concatena 2 comandi eseguendoli entrambi]
    mkdir test ;\ cd test ;\ echo done    [\ => suddividere un comando lungo su molte linee] 
                                          [In windows powershell si usa backtick (`)]  
    
    -- variabili globali
    printenv                              [lista di tutte le variabili globali]
    echo $PATH                            [leggo variabile globale PATH]
    export DB_USER=Nico                   [creo la variabile locale 'Nico' per la sessione]
    source <path>                         [ricarica il file indicato senza uscire e rientrare nella sessione shell]
    
    --processi
    ps                                    [visualizzo la lista dei processi]
    kill <PID>
    sleep 100 &                           [& => lancio il processo indicato in background]
    
    -- gestire utenti
    useradd -m John                       [aggiungo l'utente John e creo la sua cartella home]
    adduser                               [versione nuova che al suo interno chiama useradd]
    cat /etc/passwd                       [informazioni sugli utenti, anche l'ultimo che ho appena aggiunto]
    usermod -s /bin/bash John             [modifico il programma shell per l'utente John]
    userdel
    
    -- gestire gruppi
    groupadd <nome_grup>
    cat /etc/group
    usermod -G <nome_grup> <nome_utente>  [aggiungo a <nome_utente> il gruppo supplementare <nome_grup>]
    groups <nome_utente>                  [visualizzo la liste dei gruppi di un dato utente]
    groupmod
    groupdel
    
    -- permission
    chmod u+x <file_name>                 [add permesso di exe x per l'utente che è l'owner di <file_name>]
    chmod og+x+w-r <file_name>            [add permessi di exe e scrittura, levo la lettura a <others> e <group>]

 

Lavorare con le immagini

  • Dockerfile => Per poter lanciare un’applicazione in un container docker è sufficente aggiungere un file ‘dockerfile’ di configurazione a tale applicazione con le informazioni necessarie a Docker per impacchettare l’applicazione in un’immagine e poterla poi eseguire in un container.
    • Un’immagine docker può contentere
      • Versione ridotta dell’OS.
      • I file dell’applicazione.
      • Un’ambiente di runtime (es: node).
      • Librerie di terze parti.
      • Variabili globali.
  • Immagine docker
    • Kernel =>
      • Normalmente un’immagine docker è una strututtura a cipolla che si base su un kernel
    • Base image =>
      • Sopra il kernel normalmente c’è un’immagine ridotta di un sistema operativo (Alpine o Debian)
    • Writable container =>
      • Solo in cima a tale struttura c’è una parte scrivibile della nostra immagine, gli strati inferiori o intermedi sono in sola lettura. 
    • docker build -t <nome-immagine> <docker_file_path> 
      - esempio: [docker build -t react-app .]
      - prima di lanciare il comando, verificare di aver aggiunto alla cartella dell'applicazione il dockerfile.
      
      Livelli
      - quando un'immagine è buildata viene eseguito il dockerfile e ad ogni nuova istruzione di esso si crea 
        un nuovo layer (con i soli file aggiunti o modificati a seguito dell'esecuzione del comando corrispondente).
      
      Cache
      - Docker si accorge se qualcosa è cambiato e decide se rilanciare il comando del dockerfile oppure recuperare 
        il layer corrispondente dalla cache. Se un livello è cambiato. Tutti i successivi saranno ricreati.
      
      Ottimizzare il file
      - Iniziare con le istruzioni più stabili (che non cambiano nel tempo)
      - Mettere nelle ultime righe le istruzione o file che posso cambiare più frequentemente
  • Escludere file
    • .dockerignore  => per poter ignorare alcuni file presenti nella cartella delle progetto da dockerizzare è sufficiente aggiungere il file .dockerignore e indicare quali file escludere
  • Creare un utente != root
    • Di default la nostra immagine è eseguita dall’utente root e non vogliamo fare il deploy di una tale impostazione per motivi di sicurezza. E’ quindi utile preparare i comandi per creare un utente con meno privilegi e aggiungerlo ad un gruppo con lo stesso nome.
      • addgroup <prim_group>                  [creo gruppo che sarà quello primario dell'utente da creare]  
        adduser -S -G <prim_group> <user_name> [creo utente di tipo system e lo associo al primary group. 
                                                Normalmente utente e gruppo hanno lo stesso nome]
        
        oppure in un unica riga
        add group <prim_group> && adduser -S -G <prim_group> <user_name>
  • Dockerfile
    • Docker è attivo dal 2013.
    • Cosa è =>
      • Un file senza estensione che ha le informazioni necessarie affinché le applicazioni che si voglino deploiare usando i container docker funzionino.
      • E’ una sorta di ricetta che ci dice come fare il build di un’immagine 
      • Permette in altri termini di customizzare l’immagine scelta per il nostro container.
      • E’ possibile dare istruzioni al docker deamon per fare molte operazioni tra cui:
        • Copiare file.
        • Eseguire comandi sul SO.
        • Cambiare la directory di lavoro.
    • Esempio1 =>
      • -- ogni comando crea un livello successivo, che ha solo i file modificati rispetto al comando precedente
        FROM                           [specifico l'immagine di base da utilizzare]  
        COPY <source_path> <dest_path> [posso copiare file e directory dall'applicazione all'immagine docker] 
        ADD  <source_path> <dest_path> [ADD = COPY + gestione URL path + gestione ZIP file] 
        WORKDIR </app>                 [specifica la cartella di default => posso usare path rel. nell'img docker] 
        CMD <shell_form> o <exec_form> [esegue comandi al boot del container (comando <run>), più flessibile] 
        ENTRYPOINT <shell_form> o <exec_form>
                                       [esegue comandi al boot del container (comando <run>), meno flessibile] 
        RUN                            [esegue commandi durante il build dell'immagine (comando <build>)] 
        ENV                            [per impostare variabili d'ambiente] 
        EXPOSE                         [setto la porta su cui il container sarà in ascolto] 
        USER                           [specifico l'utente con cui eseguire i comandi successivi]
        
        -- esempio 1
        FROM node:alpine
        COPY ["my file1.txt"] ["."]    [Copio sull'img docker un file specifico un uno spazio nel suo nome]
        CMD ["node", "app.js"]
        
        -- esempio 2
        FROM node:14.16.0-alpine3.13
        COPY . /app/                   [copio tutti i file (.) dell'applicazione alla cartella '/app' nell'img docker]
        
        -- esempio 3
        FROM node:alpine
        
        -- esempio 4
        FROM node:alpine As build
        WORKDIR /app
        COPY index.ts .
        RUN npm install -g typescript \
            && tsc index.ts
        
        FROM nginx:alpine               [scelgo una version di alpine con il web server nginx installato]   
        WORKDIR /usr/share/nginx/html   [setto la working directory = /usr/share/nginx/html, così nel file system dell'immagine
                                         ogni volta che uso . faccio riferimento a tale directory]
        COPY index.html .               [sopra questa immagine, metto la mia applicazione web, copio 
                                                il file index.html (del mio file system) nella cartella /usr/share/nginx/html
                                                all'interno dell'immagine]  
        COPY --from=build /app/index.js
        ---------------------------------- 
        RUN addgroup app && adduser -S -G app app [aggiungo l'utente <app> e lo associo al nuovo gruppo <app>] 
        USER app                                  [setto l'utente da usare per i comandi successivi] 
        
        WORKDIR /app                   [imposto la cartella di default => posso usare path relativi a tale cartella]
        COPY package*.json *           [copio solo i file necessari a installazioni terze]
        RUN npm install
        COPY . .                       [copio il resto dei file. 
                                        source(.)= tutti i file del folder progetto, dest(.)= folder root = WORKDIR] 
        ENV API_URL=<my_api_url>       [setto variabile globale]
        EXPOSE 3000                    [l'applicazione contenuta nel container sarà in ascolto sulla porta 3000]
        
        CMD ["npm", "start"]           [esegue <npm start> ad ogni <run> del container]
        oppure CMD npm start 
        
        

    • Esempio2 =>

      • dockerfile =>
        • Per poter deployare le 2 applicazioni (es: Inventory e Accounting) in docker è necessario creare l’opportuno ‘docker file’
        • In Visual Studio aggiungere ad ognuno dei due progetti un file senza estensione:
          • MVC 5 docker file =>
            • FROM microsoft/aspnet                             //recupera l'immagine already-made con windows OS e .NET già installati
              COPY .bin/Release/Publish  /inetpub/wwwroot       //indico di copiare i file dalla cartella di release locale (il computer host che ospita docker)
                                                                //il path è relativo a dove si trova il dockerfile
                                                                //a quella presente nell'immagine

          • MVC Core docker file =>
            • //gestisco il build
              FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
              
              #imposto la cartella di lavoro rispetto alla root della cartella
              #contenente i file da cui partire per creare il container
              #se la cartella non esiste, viene creata
              #possiamo dire che il comando WORKDIR esegue implicitamente mkdir e cd 
              WORKDIR /App                                  
              
              # copio ogni cosa
              COPY . ./
              # recupero tutte le referenze necessarie alla app 
              RUN dotnet restore
              # lancio il publish e faccio il build della versione di release della mia app
              RUN dotnet publish -c Release -o out
              
              //gestisco l'esecuzione
              FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime
              WORKDIR /App
              //Creo un container a partire dall'immagine e ne eseguo l'applicazione
              //Copia l'applicazione pubblicata nel container e ne definisce il punto d'entrata
              COPY --from=build-env /App/out .
              ENTRYPOINT ["dotnet","Dotnet.Docker.dll"]

      • .dockerignore => E’ necessario aggiungere un file per indicare quali file non considerare
        • **/bin/
          **/obj/
      • Creo le immagini docker  per ospitare le 2 applicazioni =>
        • Creo una cartella nel computer host dal quale si lancia l’istanza docker e vi copio 
          • i file della cartella bin
          • dockerfile
          • .dockerignore file
        • Creo l’immagine e il container per l’applicazione MVC5
          • //nel computer host dove è abbiamo installato docker
            //apro la shell e mi posiziono nella cartella dove ho copiato i file con il build della mia applicazione MVC 5 
            //e il dockerfile e il .dockerignore file
            docker build -(t)ag imageMVC5 -(f)ile dockerfile .  //-t sta per tag significa dare un nome all'immagine che si sta creando
                                                                //-f indica il path dove si trova il dockerfile
                                                                //il punto . definisce il contesto di generazione dell'immagine //creo il container //-d sta per detached mode (il container è in background mode) docker run -(d)etach -p 8080:91 --name mvc5container imageMVC5

 

Lavorare con i container

  • [d=docker]
    --avviare e fermare container
    d start -i <container-id>                         [Avvia i container arrestati. docker start != docker run]
    d stop <container-id>                             [Arresta il container indicato]
    
    --visualizzare log
    d logs [-f|-n |-t] <container-id>                 [-f=--follow, -n=--tail]
    
    --pubblicare le porte
    d run -d -p 80:30 <my-img>                        [esegue il cont mappando la sua porta 30 alla porta 80 in host]
    
    --eseguire i comandi in un container in eseguzione
    d exec <container-name> <cmd>
    
    --rimuovere container
    d [container] rm -f <container-id>                [-f=--force, la parola chiave <container> può essere omessa] 
    d container prune                                 [fa il pruning dei container non più in esecuzione
    
    -- creare 
    d volume create <my-vol> 
    d run -d -p 80:81 -v <my-vol>:<vol-path> <my-img> [es: docker run -d -p 80:81 -v app-data:/app/data react-app]
    
    -- trasferire file tra host e container
    d cp <my-container>:<file-to-copy> .              [. => copio i file nella root dell'app sull'host] 
    d cp <file-to-copy> <my-container>:<dest-path>
    
    -- condividere il codice sorgente dell'app
    -- con il container che lo ospita
    d run -d -p 80:81 -v ${PWD}:<dest-path> <img-id>  [linka la root dell'app sull'host al folder indicato sul cont
                                                       Il path sull'host deve essere assoluto]

 

Docker-compose

  • Container multi-applicazione
    • E’ possibile lavorare su più applicazioni (o sottoapplicazioni) insieme.
    • Per esempio buildare immagini , avviare e arrestare i relativi container per un’applazione costituira dal frontend (react) , dal backend (node.js) e dal db (mongo).
    • Bisognare creare un file di configurazione con l’esplicitazione dei 3 servizi (docker-compose.yml) e usare il comando <docker-compose>.
  • Installlare docker compose (https://docs.docker.com/compose/install)
    • docker-compose --version   [Verifico se docker compose è già installato]
  • Ripulire il workspace
    • Tramite l’applicazione Docker Desktop
      • Aprire l’applicazione Docker Desktop => Cliccare sull’icona ‘Troubleshoot’ => Cliccare ‘Clean and purge data’ 
    • Tramite linee di comando
      • -- visualizzo la liste degli ID di tutti i container e le immagini presenti nel nostro workspace
        docker container ls -q                 [lista degli ID dei container presenti nel nostro workspace] 
        docker image ls -q                     [lista degli ID delle immagini presenti nel nostro workspace] 
        
        -- rimuovo prima tutti i container
        docker container rm -f $(docker container ls -qa) 
                                               [rimuovo i container passando la liste degli ID al cmd <rm>
                                                -f => forzo la rimozione anche dei container in esecuzione
                                                -qa => visualizzo gli ID anche dei container arrestati] 
        
        -- rimuovo in seguito tutte le immagini
        docker image rm $(docker image ls -q)  [rimuovo tutte le immagini passando la liste degli ID al cmd <rm>]
        
        -- alla fine non ci devono più essere nè immagini nè processi (attivi o non) 
        docker images
        docker ps -a
  • Configurare docker-compose [docker-compose.yml]
    • --docker-compose.yml
      version: "3.8"
      
      services:
        frontend:
          depends_on: 
            - backend
          build: ./frontend                    [path dove trovo il Dockfile di configurazione per il front-end]
          ports:                               
            - 3000:3000                        [setto la porta 3000 dell'host in ascolto sulla porta 3000 del cont.]
          volumes:
            - ./frontend:/app
      
        frontend-test:                         [posso creare un container in + per testare l'app]
           image: vidly_frontend
           volumes:
             - ./frontend:/app
           command: npm test
      
        backend: 
          depends_on: 
            - db
          build: ./backend                     [path dove trovo il Dockfile di configurazione per il back-end]
          ports: 
            - 3001:3001                        [scelgo una porta differente rispetto al front-end]
          environment: 
            DB_URL: mongodb://db/vidly
          command: ./docker-entrypoint.sh      [posso sovrascrivere i CMD definiti nel dockfile relativo all'app] 
          volumes:
            - ./backend:/app                   [mappo la cartella root nell'app backend con la cartella /app
                                                presente nel cont relativo così che ogni cambio nel codice sorgente
                                                possa essere visibile nel cont senza dover fare il rebuild
                                                Il path può essere un path relativo] 
      
        db:
          image: mongo:4.0-xenial              [non faccio il build, ma importo l'immagine di mongo dal cloud]
          ports:
            - 27017:27017                      [setto porta di defualt per il server mongo]
          volumes:
            - vidly:/data/db                   [attacco il volume <vidly> alla cartella del container </data/db>]
      
      volumes:
        vidly:                                 [comando per la creazione del volume <vidly> per persistere 
                                                i dati del DB]
      
  • Network
      • Quando si fa il build di una multi-app tramite docker-compose, quest’ultimo crea automaticamente una struttura (network) per far dialogare i vari servizi (applicazioni) di cui è composta la nostra multi-app.  
      • Ad ogni container viene assegnato un IP da un DNS-SERVER presente nativamente in docker . Nel container il componente DNS-Resolver permette di recuperare tali IP dal nome del container (servizio) così come configurato nel file docker-compose.yml
      • Ogni container può vedere gli altri registrati sotto lo stesso network.
  • Migrazione DB
    • Se tra le app che abbiamo composto per creare la nostra multi-app abbiamo per empio un DB e vogliamo che gli altri container attendato affinche il servizio DB sia attivo prima di lanciare degli script di migrazione ad esempio, dobbiamo utilizzare dei tool utilizzati in docker per permettere l’attesa (ad: wait-for)
    • Cercare in Internet “docker wait for container”
  • Principali comandi docker-compose [docker-compose..]
    • [d-c = docker-compose]
      -- fare il build di una multi-app
      d-c build [--no-cache]                    [--no-cache non usa la cache durante il building. Verrà creata 
                                                 una immagine per ogni servizio definito in docker-compose.yml.
                                                 Tutte le immagini prendo come prefisso la cartella del progetto]
      -- avviare multi-app
      d-c up -d [--build]                       [con il file docker-compose.yml questo comando aggiorna le 
                                                 dipendenze di cui la nostra multi-app ha bisogno.
                                                 --build => permette di fare il build prima di eseguire
                                                 -d => lancia l'applicazione in modo detached]
      -- arrestare multi-app
      d-c down                                  [stoppa i container legati alla multi-app. Le immagini restano]
       
      -- visualizzo i relativi container in esecuzione
      d-c ps                                    [mostra i processi (container) in uso per la mia multi-app
                                                 Mentre <docker ps> mostra ogni container configurato nell'host]
      
      -- visualizzo i network
      docker network ls                         [visualizzo i network presenti sull'host]
      
      -- visualizzo log
      docker-compose logs -f                    [visualizzo i log di tutti i container della mia multi-app]
      docker logs <cont-id> -f                  [posso sempre visualizzare i log di un solo container alla volta]
  • Docker compose postgreSQL + pgAdmin
    • //docker-compose.xml (postgreSQL + pgAdmin)
      version: '3.8'
      services:
        db:
          container_name: pg_container
          image: postgres
          restart: always
          environment:
             POSTGRES_USER: root
             POSTGRES_PASSWORD: root
             POSTGRES_DB: test_db
          ports:
            - "5432:5432"
        pgadmin:
          container_name: pgadmin4_container
          image: dpage/pgadmin4
          restart: always
          environment:
             PGADMIN_DEFAULT_EMAIL: admin@admin.com
             PGADMIN_DEFAULT_PASSWORD: root
          ports:
            - "5050:80"
      
      
      //Nella cartella dove é memorizzato il file docker-compose.xml lanciare
      docker compose up
      
      //Dal browser del computer host navigare alla console di amministrazione di pgAdmin
      http://localhost:5050/.
      
      Per sapere quale IP dare al server postgreSQL lanciare
      docker ps
      docker inspect <container-id>   //cercare  Networks/pgadmin_default/IPAddress

Deploy

  • Virtualization vs Containerization =>
    • Nell’esempio sotto un progetto è diviso in due app separate (Inventory e Accounting) 

 

    • Virtualization (VMWare / Hypervisor / VirtualBox) =>
      • Complica il deploy.
      • Duplica OS
        • Non è interessante il fatto di avere multipli OS (si vorrebbe piuttosto multiple istanze delle mie applicazioni).
    • Container =>
      • Invece di creare nuove VM con ognuna un suo sistema operativo si creano dei container usano una container technology come docker.
      • Kernel namespace  
        • Le tecnologie basate sui container (come docker) non replicano interamente un sistema operativo.
        • Piuttosto cercano di condividere il kernel del sistema operativo host per creare degli spazi isolati.
        • Per tale ragione il meccanismo della containerizzazione è molto più efficiente del meccanismo della virtualizzazione.
      • Per usare docker è necessario avere attivata la virtualizzazione BIOS nel sistema operativo host.
      • Un container è un package contenente:
        • il software,
        • le sue dipendenze
        • i file di configurazione.  
      • Un container è un’istanza di un’immagine 
      • Vantaggi
        • Prevedibilità
          • Un container può facilmente essere copiato dai computer di sviluppo o test al server di produzione.
          • Permettendo così di evitare il classico problema legato al deploy di applicazioni funzionanti in ambiente di sviluppo e test ma una volta deployate in produzione non funzionano.
          • E’ il principale vantaggio.
        • Compatibile con i principali provider cloud.
        • Performance
          • Un container si inizializza molto più velocemente che una VM.
        • Densità
          • Un server può gestire migliaia di container contro solo qualche dozzina di VM.
      • Svantaggi
        • Isolamento => Visto che i container condividono lo stesso SO del computer HOST, l’isolamento è minore che nel caso di VM.
  • Deploy verso un cluster
    • Kubernetes
    • Docker Swarm
  • Deploy verso un single-host
    • VPS
      • Digital Ocean
      • Google Cloud Platform (GCP)
      • Microsoft Azure
      • Amazon Web Service (AWS)
    • Installare docker-machine (https://github.com/docker/machine/releases)
    • -- tramite uno dei servizi di VPS possibili installiamo la nostra applicazione docker sul server
      docker-machine create `
      --driver <my-drive> `   
      --digitalocean-access-token <my-token>
      <app-server-name>
      
      -- connettersi all'host remoto sul server del servizio scelto
      docker-machine shh <app-server-name>
      
      -- creo un file docker-compose.prod.yml specifico per l'ambiente di produzione
      version: "3.8"
      
      services:
        frontend:
          build: 
            context: ./frontend
            dockerfile: Dockerfile.prod
          ports:
            - 80:80
          restart: unless-stopped         [per ogni servizio, indico cosa fare in caso il server cada]
         
        backend: 
          build: ./backend
          ports: 
            - 3001:3001
          environment: 
            DB_URL: mongodb://db/vidly
          restart: unless-stopped
          
        db:
          image: mongo:4.0-xenial
          ports:
            - 27017:27017
          volumes:
            - vidly:/data/db
          restart: unless-stopped
      
      volumes:
        vidly:
      
      -- Per ottimizzare la dimensione delle immagini è possibile creare
      -- dei file dockerfile.prod specifici per l'ambiente di produzione
      # Step 1: build stage
      FROM node:14.16.0-alpine3.13 AS build-stage
      WORKDIR /app
      COPY package*.json ./
      RUN npm install
      COPY . .
      RUN npm run build
      
      # Step 2: production
      FROM nginx:1.12-alpine AS production-stage
      RUN addgroup app && adduser -S -G app app
      USER app
      COPY --from=build-stage /app/build /urs/share/nginx/html  [copio i file dalla cartella dell'app /app/build
                                                                 alla cartella specificata sul server]
      EXPOSE 80
      ENTRYPOINT [ "nginx", "-g", "deamon off;" ]
      
      -- Faccio il build della nuova immagine con il nuovo dockerfile
      docker build -t <new-image-name> -f dockerfile.prod .
      -- faccio il build delle nuove immagini tutte insieme
      docker-compose -f docker-compose.prod.yml build
      
      

 

Immagini docker

  • 1 -- SQL SERVER
    --eseguire l'immagine sql server, impostandone la passwrdo e la porta
    docker run --name SqlServer -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=<my-passowrd>" 
               -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
    
    -- copiare la cartella dei log del container in locale 
    -- (es: C:\Users\richlab\Docker\Log)
    docker cp <container_id>:/var/opt/mssql/log/errorlog <local_log_path>
    
    
    2 -- MONGODB
    docker run -d -p 27017-27019:27017-27019 --name mongodb mongo:4.0.4
    
    3 -- PostgreSQL
    docker run --name my-postgres -p 5432:5432 -e POSTGRES_PASSWORD=mypassword -d postgres
    
    nella shell
    psql -d postgres -U postgres
    poi
    CREATE DATABASE myDB;
    CREATE USER myUser WITH PASSWORD ‘myPassword’; 
    GRANT ALL PRIVILEGES ON myDB TO myUser;
    
    

 

Risorse