Jak mogę sprawdzić system plików nieudanej kompilacji dokera?

272

Próbuję zbudować nowy obraz Dockera dla naszego procesu programowania, używając cpanmdo zainstalowania kilku modułów Perla jako obrazu podstawowego dla różnych projektów.

Podczas opracowywania pliku Docker cpanmzwraca kod błędu, ponieważ niektóre moduły nie zostały poprawnie zainstalowane.

Jestem całkiem pewien, że muszę aptzainstalować więcej rzeczy.

Moje pytanie brzmi: gdzie mogę znaleźć /.cpanm/workkatalog cytowany w danych wyjściowych, aby sprawdzić dzienniki? W ogólnym przypadku, jak mogę sprawdzić system plików nieudanego docker buildpolecenia?

Poranna edycja Po odgryzieniu kuli i uruchomieniu findodkryłem

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

Czy to jest niezawodne, czy może lepiej jest budować „goły” pojemnik i uruchamiać rzeczy ręcznie, dopóki nie będę mieć wszystkich potrzebnych rzeczy?

Altreus
źródło
o /var/lib/docker/aufs/diff/3afa404e[...]/.cpanmtych są wnętrzności Dockera i nie chciałbym z nimi zadzierać
Thomasleveil

Odpowiedzi:

355

Za każdym razem, gdy doker wykonuje RUNpolecenie z pliku Docker, zatwierdzana jest nowa warstwa w systemie plików obrazu . Dogodnie możesz użyć tych identyfikatorów warstw jako obrazów, aby rozpocząć nowy kontener.

Weź następujący plik Dockerfile:

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

i zbuduj to:

$ 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

Teraz można rozpocząć nowy pojemnik z 00f017a8c2a6, 044e1532c690i 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

oczywiście możesz chcieć uruchomić powłokę w celu eksploracji systemu plików i wypróbowania poleceń:

$ 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

Gdy jedno z poleceń Dockerfile nie powiedzie się, musisz poszukać identyfikatora poprzedniej warstwy i uruchomić powłokę w kontenerze utworzonym z tego identyfikatora:

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

Raz w pojemniku:

  • spróbuj wykonać polecenie, które się nie powiodło, i odtwórz problem
  • następnie napraw polecenie i przetestuj je
  • na koniec zaktualizuj plik Docker za pomocą stałego polecenia

Jeśli naprawdę musisz eksperymentować na rzeczywistej warstwie, która nie powiodła się, zamiast pracować z ostatnią warstwą roboczą, zobacz odpowiedź Drew .

Thomasleveil
źródło
2
tak. Nie ma sensu utrzymywanie kontenerów, które mają po prostu debugować plik Docker, gdy można je ponownie utworzyć w dowolnym momencie.
Thomasleveil,
1
OK, to faktycznie było bardzo przydatne, ale mam problem polegający na tym, że jeśli kompilacja kontenera zawiedzie, nie mogę użyć tej sztuczki z hashem kontenera, w którym działał. Nie powstaje obraz, jeśli RUN się nie powiedzie. Czy mogę dołączyć do pojemnika pośredniego, który nigdy nie był czyszczony?
Altreus,
6
gdy jedno z poleceń Dockerfile nie powiedzie się, musisz poszukać identyfikatora poprzedniej warstwy i uruchomić kontener z powłoką o tym identyfikatorze: docker run --rm -it <id_last_working_layer> bash -ila raz w kontenerze wypróbuj polecenie, które nie odtworzyło problemu, a następnie napraw polecenie i przetestuj je, w końcu zaktualizuj plik Docker za pomocą stałego polecenia.
Thomasleveil,
2
Możesz również docker diff <container>uzyskać dokładną listę zmian w systemie plików dokonanych na tej konkretnej warstwie (pliki dodane, usunięte lub zmienione w całym systemie plików dla tego obrazu).
L0j1k,
14
Myślałem, że to nie działa, bo tak mówiłem Unable to find image 'd5219f1ffda9:latest' locally. Byłem jednak zdezorientowany wieloma rodzajami identyfikatorów. Okazuje się, że musisz użyć identyfikatorów umieszczonych bezpośrednio za strzałkami, a nie tych, które mówią „Wbiegam ...”.
rspeer
201

Najlepsza odpowiedź działa w przypadku, gdy chcesz sprawdzić stan bezpośrednio przed nieudanym poleceniem.

Pytanie dotyczy jednak sposobu sprawdzenia stanu samego uszkodzonego kontenera. W mojej sytuacji nieudane polecenie to kompilacja, która zajmuje kilka godzin, więc przewinięcie przed nieudanym poleceniem i ponowne uruchomienie zajmuje dużo czasu i nie jest bardzo pomocne.

Rozwiązaniem jest znalezienie kontenera, który zawiódł:

$ 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

Zatwierdź to do obrazu:

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

A następnie uruchom obraz [w razie potrzeby, uruchamiając bash]:

$ docker run -it 7015687976a4 [bash -il]

Teraz patrzysz na stan kompilacji w momencie jej niepowodzenia, a nie w momencie przed uruchomieniem polecenia, które spowodowało awarię.

Rysował
źródło
Z braku zainteresowania, dlaczego miałbyś chcieć stworzyć nowy obraz z kontenera? Dlaczego nie po prostu uruchomić kontenera? Jeśli obraz utworzony z uszkodzonego kontenera jest w stanie działać, to z pewnością również zatrzymany / uszkodzony kontener może być uruchomiony? A może coś mi brakuje?
nmh
@nmh Ponieważ umożliwia przechwytywanie i sprawdzanie kontenera w stanie awarii bez konieczności ponownego uruchamiania komendy niepowodzenia. Czasami wykonanie komendy zakończonej niepowodzeniem zajmuje kilka minut lub dłużej, więc jest to wygodny sposób na oznaczenie stanu awarii. Na przykład obecnie używam tego podejścia do sprawdzania dzienników nieudanej kompilacji biblioteki C ++, która zajmuje kilka minut. Edycja - Właśnie zauważyłem, że Drew powiedział, że w [jego] sytuacji nieudane polecenie jest kompilacją, która zajmuje kilka godzin, więc przewinięcie przed nieudanym poleceniem i jego ponowne uruchomienie zajmuje dużo czasu i nie jest bardzo pomocne.
Jaime Soto,
@nmh Myślę, że problem z próbą uruchomienia uszkodzonego kontenera polega na tym, że komenda start kontenera zwykle wymaga zmiany, aby była użyteczna. Jeśli spróbujesz ponownie uruchomić uszkodzony kontener, uruchomi on komendę, która zakończyła się niepowodzeniem, i wrócisz tam, gdzie zacząłeś. Tworząc obraz, możesz uruchomić kontener innym poleceniem start.
Centimane,
2
To nie działa, jeśli używasz DOCKER_BUILDKIT=1do budowyDockerfile
Clintm,
Do punktu @ nmh - nie musisz zatwierdzać obrazu, jeśli jesteś tuż po zakończeniu kompilacji. Za pomocą kontenera dokującego cp można wyodrębnić wyniki pliku z kontenera, którego kompilacja zakończyła się niepowodzeniem.
whoisthemachine
7

Docker buforuje cały stan systemu plików po każdej udanej RUNlinii.

Wiedząc to:

  • zbadanie aktualnego stanu przed swoim upadającego RUNpolecenia, skomentować go w Dockerfile (jak również wszelkich i wszystkich kolejnych RUNpoleceń), a następnie uruchomić docker buildi docker runznowu.
  • aby zbadać stan po nieudanym RUNpoleceniu, po prostu dodaj || truedo niego, aby zmusić go do sukcesu; następnie postępuj jak wyżej (pozostaw wszystkie i wszystkie kolejne RUNpolecenia komentowane, uruchom docker buildi docker run)

Tada, nie musisz zadzierać z wewnętrznymi elementami Docker lub identyfikatorami warstw, a jako dodatkowy Docker automatycznie minimalizuje ilość pracy, którą należy ponownie wykonać.

DomQ
źródło
1
Jest to szczególnie pomocna odpowiedź podczas korzystania z DOCKER_BUILDKIT, ponieważ buildkit nie obsługuje tych samych rozwiązań, co wymienione powyżej.
M. Anthony Aiello
3

Debugowanie błędów kroku kompilacji jest w rzeczywistości bardzo denerwujące.

Najlepszym rozwiązaniem, jakie znalazłem, jest upewnienie się, że każdy krok, który wykonuje prawdziwą pracę, zakończy się sukcesem, i dodanie sprawdzenia po tych, które się nie powiodły. W ten sposób otrzymujesz zatwierdzoną warstwę, która zawiera wyniki nieudanego kroku, które możesz sprawdzić.

Plik Docker, z przykładem po # Run DB2 silent installerwierszu:

#
# 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
mikaraento
źródło
0

Chciałbym skomentować poniższy plik Docker, włączając w to linię obrażającą. Następnie możesz uruchomić kontener i ręcznie uruchomić polecenia dokera i przeglądać dzienniki w zwykły sposób. Np. Jeśli plik Docker to

RUN foo
RUN bar
RUN baz

i umiera w barze, zrobiłbym to

RUN foo
# RUN bar
# RUN baz

Następnie

$ docker build -t foo .
$ docker run -it foo bash
container# bar
...grep logs...
seanmcl
źródło
To samo zrobiłbym przed znalezieniem tego wątku. Są jednak lepsze sposoby, które nie wymagają ponownego uruchamiania kompilacji.
Aaron McMillin
@Aaron. Dzięki za przypomnienie mi tej odpowiedzi. Dawno nie patrzyłem na to. Czy możesz wyjaśnić, dlaczego zaakceptowana odpowiedź jest lepsza od tej z praktycznego punktu widzenia. Zdecydowanie rozumiem, dlaczego odpowiedź Drew jest lepsza. Wygląda na to, że zaakceptowana odpowiedź nadal wymaga ponownego uruchomienia.
seanmcl
Właściwie głosowałem za odpowiedzią Drew, a nie za przyjętą. Oba działają bez ponownego uruchamiania kompilacji. W przyjętej odpowiedzi możesz wskoczyć do powłoki tuż przed nieudanym poleceniem (możesz uruchomić ją ponownie, aby zobaczyć błąd, jeśli jest szybki). Lub z odpowiedzią Drew możesz dostać powłokę po uruchomieniu nieudanego polecenia (w jego przypadku nieudane polecenie było długo działające i pozostawiono stan, który można sprawdzić).
Aaron McMillin