Як я можу перевірити файлову систему помилкової збірки докера?


272

Я намагаюся створити новий образ Докера для нашого процесу розробки, використовуючи cpanmдля установки купу модулів Perl як базове зображення для різних проектів.

Під час розробки Dockerfile cpanmповертає код відмови, оскільки деякі з модулів не встановлені належним чином.

Я досить впевнений, що мені потрібно aptвстановити ще кілька речей.

Моє запитання полягає в тому, де я можу знайти /.cpanm/workкаталог, який цитується у висновку, щоб перевірити журнали? Як у загальному випадку я можу перевірити файлову систему невдалої docker buildкоманди?

Вранці редагування Після укусу кулі та запуску findя виявив

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

Це надійно, чи мені краще створити «голий» контейнер і запускати речі вручну, поки не знайду всі необхідні речі?


про /var/lib/docker/aufs/diff/3afa404e[...]/.cpanmце - внутрішні документи Докера, і я б не
возився

Відповіді:


355

Кожен раз, коли докер успішно виконує RUNкоманду з Dockerfile, здійснюється новий шар у файловій системі зображень . Зручно ви можете використовувати ці ідентифікатори шарів як зображення для запуску нового контейнера.

Візьміть такий Dockerfile:

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

і побудувати його:

$ 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

Тепер ви можете створити новий контейнер з 00f017a8c2a6, 044e1532c690і 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

звичайно, ви можете запустити оболонку для вивчення файлової системи та спробувати команди:

$ 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

Коли одна з команд Dockerfile виходить з ладу, вам потрібно зробити пошук ідентифікатора попереднього шару і запустити оболонку в контейнер, створений з цього ідентифікатора:

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

Потрапивши в контейнер:

  • спробуйте команду, яка не вдалася, і відтворіть проблему
  • потім виправте команду і протестуйте її
  • нарешті оновіть свій Dockerfile за допомогою фіксованої команди

Якщо вам дійсно потрібно експериментувати на фактичному шарі, який не вдався замість того, щоб працювати з останнього робочого шару, дивіться відповідь Дрю .


2
так. Немає сенсу зберігати контейнери, які призначені для налагодження Dockerfile, коли ви зможете їх відтворити за бажанням.
Thomasleveil

1
Гаразд, це насправді було дуже корисно, але у мене є проблема, коли якщо збірка контейнера не вдасться, я не можу використовувати цей трюк з хешем контейнера, за яким він сказав, що він працює. Ні зображення не створюється, якщо RUN не працює. Чи можу я приєднати до проміжного контейнера, який ніколи не був очищений?
Альтрей

6
коли одна з команд Dockerfile виходить з ладу, вам потрібно зробити пошук ідентифікатора попереднього шару та запустити контейнер із оболонкою цього ідентифікатора: docker run --rm -it <id_last_working_layer> bash -ilі, опинившись у контейнері, спробуйте команду, яка не змогла відтворити проблему, тоді виправте команду і протестуйте її, нарешті оновіть свій Dockerfile фіксованою командою.
Thomasleveil

2
Крім того, ви можете docker diff <container>отримати ретельний перелік конкретних змін файлової системи, внесених на цьому конкретному шарі (файли додаються, видаляються або змінюються у всій файловій системі для цього зображення).
L0j1k

14
Я думав, що це не працює, тому що воно сказало Unable to find image 'd5219f1ffda9:latest' locally. Однак мене збентежило безліч видів посвідчень. Виявляється, вам доведеться використовувати ідентифікатори, що знаходяться безпосередньо після стрілок, а не ті, які говорять "Running in ...".
rspeer

201

Верхня відповідь працює в тому випадку, якщо ви хочете перевірити стан безпосередньо перед невдалою командою.

Однак питання задає питання, як перевірити стан вийшов з ладу контейнера. У моїй ситуації збій команди - це збірка, яка займає кілька годин, тому перемотування перед невдалою командою та її повторне виконання займає багато часу і не дуже корисно.

Рішення тут полягає в тому, щоб знайти контейнер, який не вдався:

$ 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

Закріпіть його на зображенні:

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

А потім запустіть зображення [при необхідності запуск bash]:

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

Тепер ви насправді дивитесь на стан збірки в той момент, коли він не вдався, а не на час до запуску команди, яка викликала збій.


З інтересу, чому вам потрібно створити нове зображення з контейнера? Чому б просто не запустити контейнер? Якщо зображення, створене з невдалого контейнера, здатне запустити, то, безумовно, контейнер, що зупинився / не працює, також може працювати? Або я щось пропускаю?
nmh

@nmh Тому що це дозволяє захопити та перевірити контейнер у невдалому стані без необхідності запускати команду, що відмовляється знову. Іноді для відмови команди потрібно кілька хвилин або більше часу, тому це зручний спосіб тегування невдалого стану. Наприклад, зараз я використовую такий підхід для перевірки журналів невдалої збірки бібліотеки C ++, яка займає кілька хвилин. Редагувати - Щойно помітив, що Дрю сказав, що в [його] ситуації невдала команда - це збірка, яка займає кілька годин, тому перемотування перед невдалою командою та її повторне виконання займає багато часу і не дуже корисно.
Хайме Сото

@nmh Я думаю, що проблема спроби запуску невдалого контейнера полягає в тому, що команду запуску контейнера зазвичай потрібно змінити, щоб бути корисною. Якщо ви спробували запустити невдалий контейнер ще раз, він запустив би команду, яка знову не вдалася, і ви повернетесь туди, де ви запустили. Створюючи зображення, ви можете запустити контейнер з іншою командою запуску.
Centimane

2
Це не працює, якщо ви використовуєте DOCKER_BUILDKIT=1для створення свогоDockerfile
Клінтм

До пункту @ nmh - вам не потрібно робити зображення, якщо ви тільки після виводу збірки. Ви можете використовувати докер-контейнер cp для отримання результатів файлу з невдалого контейнера збірки.
whoisthemachine

7

Докер кешує всю стан файлової системи після кожного успішного RUNрядка.

Знаючи це:

  • щоб перевірити останнє стан перед вашою невдалою RUNкомандою, прокоментуйте це в Dockerfile (а також будь-які та всі наступні RUNкоманди), потім запустіть docker buildі docker runзнову.
  • вивчити стан після невдалої RUNкоманди, просто додати || trueдо нього, щоб змусити його досягти успіху; потім продовжуйте як вище (зберігайте будь-які та всі наступні RUNкоманди, коментуючи їх, виконайте docker buildтаdocker run )

Тада, не потрібно возитися з внутрішніми документами Docker або ідентифікаторами шару, і як бонус Docker автоматично мінімізує обсяг роботи, яку потрібно повторити.


1
Це особливо корисна відповідь при використанні DOCKER_BUILDKIT, оскільки здається, що buildkit не підтримує ті ж рішення, що і перелічені вище.
М. Ентоні Айелло

3

Налагодження помилок на етапі побудови насправді дуже дратує.

Найкраще рішення, яке я знайшов, - переконатися, що кожен крок, який робить справжню роботу, є успішним, і додавати перевірку після тих, що не вдаються. Таким чином ви отримуєте докладений шар, який містить результати невдалого кроку, який ви можете перевірити.

Докерфайл із прикладом після # 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 <carew@us.ibm.com>

# 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

0

Що я хотів би зробити, це прокоментувати Dockerfile нижче та включити рядок порушень. Потім ви можете запустити контейнер і запустити команди докер вручну, і подивитися на журнали звичайним способом. Наприклад, якщо є Dockerfile

RUN foo
RUN bar
RUN baz

і це вмирає в барі, я б робив

RUN foo
# RUN bar
# RUN baz

Тоді

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

Це я теж зробив, перш ніж знайти цю тему. Однак є і кращі способи, які не потребують повторного запуску збірки.
Аарон Мак-Міллін

@Aaron Дякую, що нагадали мені цю відповідь. Я давно не дивився на це. Чи можете ви поясніть, чому прийнята відповідь краща за цю з практичної точки зору. Я точно розумію, чому відповідь Дрю краща. Здається, прийнята відповідь все ж потребує повторного запуску.
seanmcl

Я фактично проголосував за відповідь Дрю, а не за прийняту. Вони обидва працюють без повторного запуску збірки. У прийнятій відповіді ви можете перескочити в оболонку безпосередньо перед невдалою командою (Ви можете запустити її ще раз, щоб побачити помилку, якщо вона швидка). Або за допомогою відповіді Дрю ви можете отримати оболонку після запущеної команди, яка не відбулася (У його випадку команда, яка вийшла з ладу, довго працювала і залишила стан позаду, яке можна було б перевірити).
Аарон Макміллін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.