Ricostruisci il contenitore Docker sulle modifiche ai file


86

Per eseguire un'applicazione ASP.NET Core, ho generato un dockerfile che crea l'applicazione e copia il codice sorgente nel contenitore, che viene recuperato da Git utilizzando Jenkins. Quindi nel mio spazio di lavoro, eseguo le seguenti operazioni nel dockerfile:

WORKDIR /app
COPY src src

Mentre Jenkins aggiorna correttamente i file sul mio host con Git, Docker non lo applica alla mia immagine.

Il mio script di base per la creazione:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

Ho provato diverse cose come --rme --no-cacheparametro per docker rune anche fermare / rimuovere il contenitore prima che il nuovo fosse compilato. Non sono sicuro di cosa sto facendo di sbagliato qui. Sembra che docker stia aggiornando correttamente l'immagine, poiché la chiamata di COPY src srccomporterebbe un ID livello e nessuna chiamata alla cache:

Step 6 : COPY src src
 ---> 382ef210d8fd

Qual è il modo consigliato per aggiornare un contenitore?

Il mio scenario tipico sarebbe: l'applicazione è in esecuzione sul server in un container Docker. Ora parti dell'app vengono aggiornate, ad esempio modificando un file. Ora il contenitore dovrebbe eseguire la nuova versione. Docker sembra consigliare di costruire una nuova immagine invece di modificare un contenitore esistente, quindi penso che il modo generale di ricostruire come faccio io sia corretto, ma alcuni dettagli nell'implementazione devono essere migliorati.


Puoi elencare i passaggi esatti che hai eseguito per creare il tuo contenitore, incluso il tuo comando di compilazione e l'intero output di ciascun comando?
BMitch

Risposte:


136

Dopo alcune ricerche e test, ho scoperto di avere dei malintesi sulla durata dei container Docker. Il semplice riavvio di un contenitore non fa sì che Docker utilizzi una nuova immagine, quando nel frattempo l'immagine è stata ricostruita. Invece, Docker sta recuperando l'immagine solo prima di creare il contenitore. Quindi lo stato dopo l'esecuzione di un contenitore è persistente.

Perché è necessaria la rimozione

Pertanto, la ricostruzione e il riavvio non sono sufficienti. Pensavo che i contenitori funzionassero come un servizio: arrestare il servizio, apportare le modifiche, riavviarlo e verranno applicate. Questo è stato il mio più grande errore.

Poiché i contenitori sono permanenti, devi docker rm <ContainerName>prima rimuoverli utilizzando . Dopo che un contenitore è stato rimosso, non puoi semplicemente avviarlo docker start. Questo deve essere fatto utilizzando docker run, che a sua volta utilizza l'immagine più recente per creare una nuova istanza di contenitore.

I contenitori dovrebbero essere il più indipendenti possibile

Con questa conoscenza, è comprensibile il motivo per cui l'archiviazione dei dati nei contenitori è considerata una cattiva pratica e Docker consiglia invece i volumi di dati / il montaggio di directory host : poiché un contenitore deve essere distrutto per aggiornare le applicazioni, anche i dati memorizzati all'interno andrebbero persi. Ciò causa un lavoro extra per i servizi di arresto, i dati di backup e così via.

Quindi è una soluzione intelligente escludere completamente quei dati dal contenitore: non dobbiamo preoccuparci dei nostri dati, quando sono archiviati in modo sicuro sull'host e il contenitore contiene solo l'applicazione stessa.

Perché -rfpotrebbe non aiutarti davvero

Il docker runcomando ha un'opzione Clean up chiamata -rf. Si interromperà il comportamento di mantenere i container docker in modo permanente. Usando -rf, Docker distruggerà il contenitore dopo che è stato chiuso. Ma questo interruttore ha due problemi:

  1. Docker rimuove anche i volumi senza un nome associato al contenitore, il che potrebbe uccidere i tuoi dati
  2. Utilizzando questa opzione, non è possibile eseguire contenitori in background utilizzando -dinterruttore

Sebbene l' -rfinterruttore sia una buona opzione per risparmiare lavoro durante lo sviluppo per test rapidi, è meno adatto in produzione. Soprattutto a causa dell'opzione mancante per eseguire un contenitore in background, che sarebbe per lo più richiesta.

Come rimuovere un contenitore

Possiamo aggirare queste limitazioni semplicemente rimuovendo il contenitore:

docker rm --force <ContainerName>

L' opzione --force(o -f) che utilizza SIGKILL sui container in esecuzione. Invece, potresti anche fermare il contenitore prima:

docker stop <ContainerName>
docker rm <ContainerName>

Entrambi sono uguali. docker stoputilizza anche SIGTERM . Ma l'utilizzo di --forceswitch ridurrà lo script, soprattutto quando si utilizzano server CI: docker stopgenera un errore se il contenitore non è in esecuzione. Ciò indurrebbe Jenkins e molti altri server CI a considerare la build erroneamente come fallita. Per risolvere questo problema, devi prima controllare se il contenitore è in esecuzione come ho fatto nella domanda (vedi containerRunningvariabile).

Script completo per la ricostruzione di un container Docker

In base a questa nuova conoscenza, ho corretto il mio script nel modo seguente:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

echo Delete old container...
docker rm -f $containerName

echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName

Funziona perfettamente :)


4
"Ho scoperto di avere dei malintesi sulla durata dei container Docker", mi hai tolto le parole di bocca. Grazie per questa spiegazione dettagliata. Lo consiglierei ai neofiti dei docker. Questo chiarisce la differenza tra VM e container.
Vince Banzon

2
Dopo la tua spiegazione, quello che ho fatto è prendere nota di ciò che ho fatto alla mia immagine esistente. Per mantenere le modifiche, ho creato un nuovo Dockerfile per creare una nuova immagine che include già le modifiche che voglio aggiungere. In questo modo, la nuova immagine creata viene (in qualche modo) aggiornata.
Vince Banzon

L' --force-recreateopzione su Docker Compose è simile a quella che descrivi qui? E se è così, non varrebbe invece la pena di usare questa soluzione (scusate se la domanda è stupida ma io sono un noob
portuale

2
@cglacet Sì, è simile, non direttamente confrontabile. Ma docker-composeè più intelligente dei semplici dockercomandi. Lavoro regolarmente con docker-composee il rilevamento delle modifiche funziona bene, quindi lo uso --force-recreatemolto raramente. Proprio docker-compose up --buildè importante quando si sta costruendo un'immagine personalizzata ( builddirettiva nel file di comporre) invece di utilizzare un'immagine, ad esempio mozzo Docker.
Lion

33

Ogni volta che vengono apportate modifiche in dockerfile o compose o requisiti, rieseguirlo utilizzando docker-compose up --build. In modo che le immagini vengano ricostruite e aggiornate


1
Avendo un contenitore Docker MySQL come un servizio, il DB sarebbe vuoto dopo di che se si usasse un volume per /opt/mysql/data:/var/lib/mysql?
Martin Thoma

Per me, non sembra esserci alcun aspetto negativo nell'usare sempre --buildin ambienti di sviluppo locali. La velocità con cui docker copia nuovamente i file che altrimenti potrebbe presumere non sia necessario copiare richiede solo un paio di millisecondi e consente di risparmiare un gran numero di momenti WTF.
Danack

0

Puoi eseguire buildper un servizio specifico eseguendo in docker-compose up --build <service name>cui il nome del servizio deve corrispondere a come lo hai chiamato nel tuo file docker-compose.

Esempio Supponiamo che il tuo file docker-compose contenga molti servizi (.net app - database - crittografiamo ... ecc.) E desideri aggiornare solo l'app .net denominata come applicationnel file docker-compose. Puoi quindi semplicemente correredocker-compose up --build application

Parametri extra Nel caso in cui desideri aggiungere parametri extra al tuo comando, come -dper l'esecuzione in background, il parametro deve essere prima del nome del servizio: docker-compose up --build -d application

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.