2014-10-06 19 views
129

Sto cercando di creare una nuova immagine Docker per il nostro processo di sviluppo, utilizzando cpanm per installare un gruppo di moduli Perl come immagine di base per vari progetti.Come posso ispezionare il file system di una `docker build` fallita?

Durante lo sviluppo del file Docker, cpanm restituisce un codice di errore perché alcuni dei moduli non sono stati installati in modo pulito.

Sono abbastanza sicuro ho bisogno di ottenere apt installare alcuni più cose.

La mia domanda è, dove posso trovare la directory /.cpanm/work citato in uscita, al fine di ispezionare i registri? Nel caso generale, come posso ispezionare il file system di un comando docker build fallito?

Mattina modificare dopo mordere la pallottola e l'esecuzione di un find ho scoperto

/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm 

È questo affidabile, o sono meglio la costruzione di un contenitore "nuda" e l'esecuzione di roba manualmente finché non avrò tutte le cose Ho bisogno?

+0

su '/ var/lib/finestra mobile/aufs/diff/3afa404e [...]/cpanm' quelle sono parti interne di Docker e Non vorrei scherzare con loro – Thomasleveil

risposta

173

Ogni finestra mobile esegue con successo un comando RUN da un Dockerfile, un nuovo livello nel il filesystem di immagine è impegnato. Convenientemente è possibile utilizzare gli id ​​dei livelli come immagini per avviare un nuovo contenitore.

Prendere la seguente Dockerfile:

FROM busybox 
RUN echo 'foo' > /tmp/foo.txt 
RUN echo 'bar' >> /tmp/foo.txt 

e costruirlo:

$ docker build -t so-2622957 . 
Sending build context to Docker daemon 47.62 kB 
Step 1/3 : FROM busybox 
---> 00f017a8c2a6 
Step 2/3 : RUN echo 'foo' > /tmp/foo.txt 
---> Running in 4dbd01ebf27f 
---> 044e1532c690 
Removing intermediate container 4dbd01ebf27f 
Step 3/3 : RUN echo 'bar' >> /tmp/foo.txt 
---> Running in 74d81cb9d2b1 
---> 5bd8172529c1 
Removing intermediate container 74d81cb9d2b1 
Successfully built 5bd8172529c1 

È ora possibile iniziare un nuovo contenitore da 00f017a8c2a6, 044e1532c690 e 5bd8172529c1:

$ docker run --rm 00f017a8c2a6 cat /tmp/foo.txt 
cat: /tmp/foo.txt: No such file or directory 

$ docker run --rm 044e1532c690 cat /tmp/foo.txt 
foo 

$ docker run --rm 5bd8172529c1 cat /tmp/foo.txt 
foo 
bar 

naturalmente potresti voler avviare una shell in exp LORE il file system e provare comandi:

$ docker run --rm -it 044e1532c690 sh  
/# ls -l /tmp 
total 4 
-rw-r--r-- 1 root  root    4 Mar 9 19:09 foo.txt 
/# cat /tmp/foo.txt 
foo 

Quando uno del comando Dockerfile non riesce, quello che dobbiamo fare è di cercare il id dello strato precedente ed eseguire un guscio in un contenitore creato da quel ID:

docker run --rm -it <id_last_working_layer> bash -il 

volta nel contenitore:

  • prova il comando che non è riuscita, e riprodurre il problema
  • poi fissare il comando e testarlo
  • finalmente aggiornare il Dockerfile con il comando fissa

Se si ha realmente bisogno di sperimentare nello strato reale che fallito invece di lavorare dall'ultimo livello di lavoro, vedere Drew's answer.

+0

Un sacco di --rm in questo - non rimuoverà i contenitori dopo aver fatto queste cose? – Altreus

+0

sì, sì. Non ha senso tenere contenitori che servono solo a eseguire il debug del Dockerfile quando è possibile ricrearli a piacimento. – Thomasleveil

+0

OK questo è stato davvero super utile, ma ho il problema dove se un build del contenitore fallisce, non posso usare questo trucco con l'hash del contenitore in cui diceva che stava funzionando. Nessuna immagine viene creata se il RUN fallisce . Posso collegarmi al contenitore intermedio che non è mai stato pulito? – Altreus

2

Quello che vorrei fare è commentare la Dockerfile qui sotto e compresa la linea incriminata. Quindi è possibile eseguire il contenitore ed eseguire i comandi della finestra mobile manualmente e osservare i registri nel modo consueto. Per esempio. se il Dockerfile è

RUN foo 
RUN bar 
RUN baz 

e sta morendo al bar farei

RUN foo 
# RUN bar 
# RUN baz 

Poi

$ docker build -t foo . 
$ docker run -it foo bash 
container# bar 
...grep logs... 
+0

Questo è quello che avrei fatto anche io prima di trovare questo thread. Ci sono modi migliori però che non richiedono il rieseguire la build. –

+0

@'Aaron. Grazie per avermi ricordato di questa risposta. Non l'ho guardato a lungo. Potresti spiegare perché la risposta accettata è migliore di questa da un punto di vista pratico. Ho sicuramente capito perché la risposta di Drew è migliore. Sembra che la risposta accettata richieda ancora una ripetizione. – seanmcl

+0

In realtà ho votato per la risposta di Drew e non per l'accettata. Entrambi funzionano senza rieseguire la build. Nella risposta accettata puoi saltare in una shell appena prima del comando non riuscito (puoi eseguirlo di nuovo per vedere l'errore se è veloce). Oppure con la risposta di Drew è possibile ottenere una shell dopo che il comando fallito è stato eseguito (nel suo caso il comando fallito era di lunga durata e lo stato sinistro dietro potrebbe essere ispezionato). –

3

Docker caches the entire filesystem state dopo ogni riga riuscita RUN.

Sapendo che:

  • per esaminare l'ultimo stato prima della mancanza di RUN comando, commentarla nel Dockerfile (così come ogni successiva RUN comandi), quindi eseguire docker build e docker run di nuovo.
  • per esaminare lo stato dopo il il comando in errore RUN, è sufficiente aggiungere || true ad esso per forzarlo a riuscire; quindi procedere come sopra (mantenere ogni successiva RUN comandi commentate, eseguire docker build e docker run)

Tada, senza bisogno di pasticciare con interni finestra mobile o ID strato, e come bonus Docker minimizza automaticamente la quantità di lavoro che deve essere rifatto.

+0

Questa è una buona idea. – anonymous

83

La risposta in alto funziona nel caso in cui si desideri esaminare lo stato immediatamente prima del comando non riuscito.

Tuttavia, la domanda chiede come esaminare lo stato del contenitore guasto stesso. Nella mia situazione, il comando fallito è una build che impiega diverse ore, quindi riavvolgere prima del comando non riuscito ed eseguirlo di nuovo richiede molto tempo e non è molto utile.

La soluzione qui è quello di trovare il contenitore che non è riuscito:

$ docker ps -a 
CONTAINER ID  IMAGE    COMMAND     CREATED    STATUS       PORTS    NAMES 
6934ada98de6  42e0228751b3  "/bin/sh -c './utils/" 24 minutes ago  Exited (1) About a minute ago      sleepy_bell 

commetterlo ad un'immagine:

$ docker commit 6934ada98de6 
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83 

e quindi eseguire l'immagine [se necessario, bash in esecuzione]:

Ora stai guardando lo stato della build nel momento in cui è fallito, invece che prima di eseguire il comando che ha causato l'errore.

+6

Questa dovrebbe essere una risposta accettata! Grazie! – qbolec

0

Il debug dei guasti del gradino di costruzione è davvero molto fastidioso.

La soluzione migliore che ho trovato è assicurarsi che ogni passo che fa il vero lavoro abbia esito positivo e aggiungere un controllo dopo quelli che falliscono. In questo modo si ottiene un livello impegnato che contiene gli output del passaggio non riuscito che è possibile esaminare.

Un Dockerfile, con un esempio dopo la linea # Run DB2 silent installer:.

# 
# DB2 10.5 Client Dockerfile (Part 1) 
# 
# Requires 
# - DB2 10.5 Client for 64bit Linux ibm_data_server_runtime_client_linuxx64_v10.5.tar.gz 
# - Response file for DB2 10.5 Client for 64bit Linux db2rtcl_nr.rsp 
# 
# 
# Using Ubuntu 14.04 base image as the starting point. 
FROM ubuntu:14.04 

MAINTAINER David Carew <[email protected]> 

# DB2 prereqs (also installing sharutils package as we use the utility uuencode to generate password - all others are required for the DB2 Client) 
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y sharutils binutils libstdc++6:i386 libpam0g:i386 && ln -s /lib/i386-linux-gnu/libpam.so.0 /lib/libpam.so.0 
RUN apt-get install -y libxml2 


# Create user db2clnt 
# Generate strong random password and allow sudo to root w/o password 
# 
RUN \ 
    adduser --quiet --disabled-password -shell /bin/bash -home /home/db2clnt --gecos "DB2 Client" db2clnt && \ 
    echo db2clnt:`dd if=/dev/urandom bs=16 count=1 2>/dev/null | uuencode -| head -n 2 | grep -v begin | cut -b 2-10` | chgpasswd && \ 
    adduser db2clnt sudo && \ 
    echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 

# Install DB2 
RUN mkdir /install 
# Copy DB2 tarball - ADD command will expand it automatically 
ADD v10.5fp9_linuxx64_rtcl.tar.gz /install/ 
# Copy response file 
COPY db2rtcl_nr.rsp /install/ 
# Run DB2 silent installer 
RUN mkdir /logs 
RUN (/install/rtcl/db2setup -t /logs/trace -l /logs/log -u /install/db2rtcl_nr.rsp && touch /install/done) || /bin/true 
RUN test -f /install/done || (echo ERROR-------; echo install failed, see files in container /logs directory of the last container layer; echo run docker run '<last image id>' /bin/cat /logs/trace; echo ----------) 
RUN test -f /install/done 

# Clean up unwanted files 
RUN rm -fr /install/rtcl 

# Login as db2clnt user 
CMD su - db2clnt