Condividi tramite


Esercitazione: Rendere persistenti i dati in un'app contenitore usando volumi in VS Code

In questa esercitazione si apprenderà come rendere persistenti i dati in un'applicazione contenitore. Quando viene eseguito o aggiornato, i dati sono ancora disponibili. Esistono due tipi principali di volumi usati per rendere persistenti i dati. Questa esercitazione è incentrata su volumi denominati.

Imparerai anche a conoscere i montaggi uniti, che controllano il punto di montaggio esatto sull'host. È possibile usare i montaggi di associazione per rendere persistenti i dati, ma può anche aggiungere altri dati ai contenitori. Quando si lavora su un'applicazione, è possibile utilizzare un mount di legame per montare il codice sorgente nel contenitore, così da poter vedere le modifiche al codice, rispondere e visualizzare immediatamente i cambiamenti.

Questa esercitazione presenta anche la suddivisione in livelli delle immagini, la memorizzazione nella cache dei livelli e le compilazioni a più fasi.

In questa esercitazione si apprenderà come:

  • Comprendere i dati nei vari contenitori.
  • Rendere persistenti i dati usando volumi nominati.
  • Usare i montaggi di associazione.
  • Visualizza il livello dell'immagine.
  • Dipendenze della cache.
  • Comprendere le compilazioni in più fasi.

Prerequisiti

Questa esercitazione continua l'esercitazione precedente , Creare e condividere un'app contenitore con Visual Studio Code. Iniziare con quello, che include i prerequisiti.

Comprendere i dati tra i contenitori

In questa sezione si inizieranno due contenitori e si creerà un file in ognuno. I file creati in un contenitore non sono disponibili in un altro contenitore.

  1. Avviare un contenitore ubuntu usando questo comando:

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

    Questo comando richiama due comandi usando &&. La prima parte seleziona un singolo numero casuale e la scrive in /data.txt. Il secondo comando monitora un file per mantenere il contenitore in esecuzione.

  2. In Visual Studio Code, in Esplora contenitori fare clic con il pulsante destro del mouse sul contenitore ubuntu e scegliere Collega shell.

    Screenshot che mostra l'estensione Strumenti per i container con un contenitore selezionato e un menu di scelta rapida con Attacca Shell selezionato.

    Viene aperto un terminale che esegue una shell nel contenitore Ubuntu.

  3. Eseguire il comando seguente per visualizzare il contenuto del file /data.txt.

    cat /data.txt
    

    Il terminale mostra un numero compreso tra 1 e 10000.

    Per usare la riga di comando per visualizzare questo risultato, ottenere l'ID contenitore usando il comando docker ps ed eseguire il comando seguente.

    docker exec <container-id> cat /data.txt
    
  4. Avviare un altro contenitore ubuntu.

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    
  5. Usare questo comando per esaminare il contenuto della cartella.

    docker run -it ubuntu ls /
    

    Non deve essere presente alcun file data.txt perché è stato scritto nello spazio scratch solo per il primo contenitore.

  6. Selezionare questi due contenitori Ubuntu. Fare clic con il pulsante destro del mouse e selezionare Rimuovi. Dalla riga di comando è possibile rimuoverli usando il comando docker rm -f.

Rendere persistenti i dati todo usando volumi denominati

Per impostazione predefinita, l'app todo archivia i dati in un database SQLite in /etc/todos/todo.db. Il database SQLite è un database relazionale che archivia i dati di un singolo file. Questo approccio funziona per progetti di piccole dimensioni.

È possibile rendere persistente il singolo file nell'host. Quando lo rendi disponibile per il contenitore successivo, l'applicazione può continuare dal punto in cui era interrotta. Creando un volume e collegandolo o montandoloalla cartella in cui sono archiviati i dati, puoi mantenere persistenti i dati. Il contenitore scrive nel file todo.db e i dati vengono mantenuti nell'host nel volume.

Per questa sezione, usare un volume denominato . Docker mantiene la posizione fisica del volume sul disco. Fare riferimento al nome del volume e Docker fornisce i dati corretti.

  1. Creare un volume usando il comando docker volume create.

    docker volume create todo-db
    
  2. In CONTENITORI, selezionare guida-introduttiva e fare clic con il pulsante destro del mouse. Selezionare Stop per arrestare il container dell'app.

    Per arrestare il contenitore dalla riga di comando, usare il comando docker stop.

  3. Avviare il contenitore introduttivo usando il comando seguente.

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    Il parametro volume specifica il volume da montare e la posizione, /etc/todos.

  4. Aggiornare il browser per ricaricare l'app. Se hai chiuso la finestra del browser, vai al http://localhost:3000/. Aggiungere alcuni elementi all'elenco todo.

    Screenshot mostra l'app di esempio con diversi elementi aggiunti all'elenco.

  5. Rimuovere il contenitore introduttivo per l'app todo. Fare clic con il pulsante destro del mouse sul contenitore in Esplora contenitori e scegliere Rimuovi oppure utilizzare i comandi docker stop e docker rm.

  6. Avviare un nuovo contenitore usando lo stesso comando:

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    Questo comando monta la stessa unità di prima. Aggiornare il browser. Gli elementi aggiunti sono ancora presenti nell'elenco.

  7. Rimuovere di nuovo il contenitore di avvio.

I volumi denominati e i montaggi di associazione, descritti di seguito, sono i tipi principali di volumi supportati da un'installazione predefinita del motore Docker.

Proprietà Volumi denominati Associa montaggi
Posizione dell'ospite Docker sceglie Tu controlli
Esempio di montaggio (usando -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
Popola il nuovo volume con il contenuto del contenitore NO
Supporta i driver di volume NO

Sono disponibili molti driver plug-in di volume per supportare NFS, SFTP, NetApp e altro ancora. Questi plug-in sono particolarmente importanti per eseguire contenitori in più host in un ambiente cluster, ad esempio Swarm o Kubernetes.

Se ci si chiede dove Docker effettivamente archivia i dati, eseguire il comando seguente.

docker volume inspect todo-db

Guarda l'output, simile a questo risultato.

[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

Il Mountpoint è la posizione effettiva in cui vengono archiviati i dati. Nella maggior parte dei computer, è necessario disporre dell'accesso root per accedere a questa directory dall'host.

Usare i montaggi di associazione

Usando i bind mount per gli, si controlla il punto di montaggio esatto sull'host. Questo approccio rende persistenti i dati, ma viene spesso usato per fornire più dati nei contenitori. È possibile utilizzare un montaggio bind per montare il codice sorgente nel contenitore, permettendo al container di rilevare le modifiche al codice, rispondere e consentendo all'utente di visualizzare le modifiche immediatamente.

Per eseguire il contenitore per supportare un flusso di lavoro di sviluppo, seguire questa procedura:

  1. Rimuovere tutti i contenitori getting-started.

  2. Nella cartella app eseguire il comando seguente.

    docker run -dp 3000:3000 -w /app -v ${PWD}:/app node:lts-alpine sh -c "yarn install && yarn run dev"
    

    Questo comando contiene i parametri seguenti.

    • -dp 3000:3000 Uguale a prima. Eseguire in modalità separata e creare una mappatura delle porte.
    • -w /app Directory di lavoro all'interno del contenitore.
    • -v ${PWD}:/app" Effettuare il bind mount della directory corrente dal sistema host nella directory /app del contenitore.
    • node:lts-alpine L'immagine da usare. Questa immagine è l'immagine di base per l'app dal dockerfile .
    • sh -c "yarn install && yarn run dev" Un comando. Avvia una shell usando sh ed esegue yarn install per installare tutte le dipendenze. Viene quindi eseguito yarn run dev. Se si esamina il package.json, lo script di dev sta avviando nodemon.
  3. È possibile controllare i log usando docker logs.

    docker logs -f <container-id>
    
    $ nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching path(s): *.*
    [nodemon] watching extensions: js,mjs,json
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

    Quando viene visualizzata l'ultima voce in questo elenco, l'app è in esecuzione.

    Al termine dell'osservazione dei log, selezionare qualsiasi tasto nella finestra del terminale oppure selezionare CTRL+C in una finestra esterna.

  4. In VS Code, apri src/static/js/app.js. Modificare il testo del pulsante Aggiungi elemento alla riga 109.

    - {submitting ? 'Adding...' : 'Add Item'}
    + {submitting ? 'Adding...' : 'Add'}
    

    Salvare la modifica.

  5. Aggiornare il browser. Verrà visualizzata la modifica.

    Screenshot mostra l'app di esempio con il nuovo testo sul pulsante.

  6. Rimuovere il node:lts-alpine contenitore.

  7. app Nella cartella eseguire il comando seguente per rimuovere la node_modules cartella creata nei passaggi precedenti.

    rm -r node_modules
    

Visualizzare i livelli immagine

È possibile esaminare i livelli che costituiscono un'immagine. Eseguire il comando docker image history per visualizzare il comando usato per creare ogni livello all'interno di un'immagine.

  1. Usare docker image history per visualizzare i livelli nell'immagine introduttiva creata in precedenza nell'esercitazione.

    docker image history getting-started
    

    Il risultato dovrebbe essere simile a questo risultato.

    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "/app/src/ind…   0B                  
    f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB              
    a2c054d14948        36 seconds ago      /bin/sh -c #(nop) COPY dir:5dc710ad87c789593…   198kB               
    9577ae713121        37 seconds ago      /bin/sh -c #(nop) WORKDIR /app                  0B                  
    b95baba1cfdb        13 days ago         /bin/sh -c #(nop)  CMD ["node"]                 0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B                
    <missing>           13 days ago         /bin/sh -c apk add --no-cache --virtual .bui…   5.35MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.21.1      0B                  
    <missing>           13 days ago         /bin/sh -c addgroup -g 1000 node     && addu…   74.3MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.14.1     0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) ADD file:e69d441d729412d24…   5.59MB   
    

    Ognuna delle linee rappresenta un livello nell'immagine. L'output mostra la base nella parte inferiore con il livello più recente nella parte superiore. Usando queste informazioni, è possibile visualizzare le dimensioni di ogni livello, consentendo di diagnosticare immagini di grandi dimensioni.

  2. Diverse righe vengono troncate. Se si aggiunge il parametro --no-trunc, si otterrà l'output completo.

    docker image history --no-trunc getting-started
    

Dipendenze della cache

Una volta modificato un livello, è necessario ricreare anche tutti i livelli downstream. Ecco di nuovo il Dockerfile.

FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "/app/src/index.js"]

Ogni comando nel Dockerfile diventa un nuovo livello nell'immagine. Per ridurre al minimo il numero di livelli, è possibile ristrutturare Dockerfile per supportare la memorizzazione nella cache delle dipendenze. Per le applicazioni basate su Node, tali dipendenze vengono definite nel file package.json.

L'approccio consiste nel copiare solo il file in primo luogo, installare le dipendenze e quindi copiare tutto il resto. Il processo ricrea le dipendenze yarn solo se è stata apportata una modifica al package.json.

  1. Aggiornare il Dockerfile per copiare prima le dipendenze package.json, installare le dipendenze e quindi copiare tutto il resto. Ecco il nuovo file:

    FROM node:lts-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install --production
    COPY . .
    CMD ["node", "/app/src/index.js"]
    
  2. Creare una nuova immagine usando docker build.

    docker build -t getting-started .
    

    Verrà visualizzato un output simile ai risultati seguenti:

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Running in d53a06c9e4c2
    yarn install v1.17.3
    [1/4] Resolving packages...
    [2/4] Fetching packages...
    info fsevents@1.2.9: The platform "linux" is incompatible with this module.
    info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
    [3/4] Linking dependencies...
    [4/4] Building fresh packages...
    Done in 10.89s.
    Removing intermediate container d53a06c9e4c2
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> a239a11f68d8
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 49999f68df8f
    Removing intermediate container 49999f68df8f
    ---> e709c03bc597
    Successfully built e709c03bc597
    Successfully tagged getting-started:latest
    

    Tutti i livelli sono stati ricostruiti. Questo risultato è previsto perché è stata modificata la Dockerfile.

  3. Apportare una modifica al percorso src/static/ di index.html. Ad esempio, modificare il titolo in modo da indicare "The Awesome Todo App".

  4. Compilare ora l'immagine Docker usando di nuovo docker build. Questa volta, l'output dovrebbe avere un aspetto leggermente diverso.

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> Using cache
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Using cache
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> cccde25a3d9a
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 2be75662c150
    Removing intermediate container 2be75662c150
    ---> 458e5c6f080c
    Successfully built 458e5c6f080c
    Successfully tagged getting-started:latest
    

    Poiché si usa la cache di compilazione, dovrebbe essere molto più veloce.

Compilazioni in più fasi

Le compilazioni a più fasi sono uno strumento incredibilmente potente che consente di usare più fasi per creare un'immagine. Esistono diversi vantaggi:

  • Separare le dipendenze in fase di compilazione dalle dipendenze di runtime
  • Riduci le dimensioni complessive dell'immagine distribuendo solo ciò che è necessario per l'esecuzione dell'app

In questa sezione vengono forniti brevi esempi.

Esempio di Maven/Tomcat

Quando si compilano applicazioni basate su Java, è necessario un JDK per compilare il codice sorgente in bytecode Java. JDK non è necessario nell'ambiente di produzione. È possibile usare strumenti come Maven o Gradle per facilitare la compilazione dell'app. Questi strumenti non sono necessari anche nell'immagine finale.

FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

Questo esempio usa una fase, build, per eseguire la compilazione Java effettiva usando Maven. La seconda fase, a partire da "FROM tomcat", copia i file dalla fase build. L'immagine finale è solo l'ultima fase da creare, che può essere sottoposta a override usando il parametro --target.

Esempio di React

Quando si compilano applicazioni React, è necessario un ambiente Node per compilare il codice JavaScript, i fogli di stile Sass e altro ancora in HTML statico, JavaScript e CSS. Se non si esegue il rendering sul lato server, non è necessario nemmeno un ambiente Node per la compilazione di produzione.

FROM node:lts-alpine AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

In questo esempio viene usata un'immagine node:lts-alpine per eseguire la compilazione, che ottimizza la memorizzazione nella cache dei livelli e quindi copia l'output in un contenitore nginx.

Pulire le risorse

Continua a conservare tutto ciò che hai fatto finora per poter continuare questa serie di esercitazioni.

Passaggi successivi

Sono state illustrate le opzioni per rendere persistenti i dati per le app contenitore.

Cosa vuoi fare?