Is there a way (or several ways) to retrieve from the image, the contents of file.txt that was added then removed in an intermediate layer?
Да!
Does the answer depend on the choice of the WORKDIR?
Нет. WORKDIR
не делает ничего, кроме изменения текущего рабочего каталога.
Когда вы создаете образ из Dockerfile, каждая директива в Dockerfile создает новый слой. «Образ» — это просто набор слоев, которые объединяются для формирования файловой системы контейнера при запуске контейнера. Каждый из этих слоев можно найти отдельно на вашем диске в /var/lib/docker
. Например, предположим, что я создаю образ, используя этот Dockerfile:
FROM debian:9
COPY file.txt /root/file.txt
RUN rm -f /root/file.txt
В этом каталоге у меня есть файл с именем file.txt
, который содержит текст:
hello world
Если я побегу docker build -t erikmd.
, я увижу:
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM debian:9
---> d508d16c64cd
Step 2/3 : COPY file.txt /root/file.txt
---> Using cache
---> 6f06029c1cca
Step 3/3 : RUN rm -f /root/file.txt
---> Using cache
---> a2dc62c823c9
Successfully built a2dc62c823c9
Successfully tagged erikmd:latest
Каждый шаг в процессе сборки создает новый слой, и он предоставляет вам идентификатор изображения, представляющий промежуточное изображение, которое является результатом всех команд Dockerfile до этого момента. Учитывая выход,Я могу бежать:
$ docker run --rm 6f06029c1cca cat /root/file.txt
И посмотреть содержимое файла:
hello world
Но что, если я не просто построил образ? В этом случае я бы начал с использования команды docker image inspect
для просмотра списка слоев, составляющих изображение :
$ docker image inspect erikmd | jq '.[0].RootFS.Layers'
[
"sha256:13d5529fd232cacdd8cd561148560e0bf5d65dbc1149faf0c68240985607c303",
"sha256:41494b03ef195ce6db527bd68b89cbebdace66210b4c142e95f8553fcb0bf51e",
"sha256:1948a4bd00b6f1712667bb2c68d1fe6eb60fbbcdf8bad62653208c23bf2602a5"
]
В приведенном выше примере jq
— это просто инструмент для запроса данных JSON. Вы можете просто визуально проверить вывод docker image inspect
для получения той же информации, если у вас нет под рукой jq
.
Предполагая, что конфигурация Docker по умолчанию использует драйвер хранилища overlay2
, вы найдете эти идентификаторы в /var/lib/docker/image/overlay2/layerdb/sha256/*/diff
. Так, например:
# grep -l 13d5529fd232cacdd8cd561148560e0bf5d65dbc1149faf0c68240985607c303 \
/var/lib/docker/image/overlay2/layerdb/sha256/*/diff
/var/lib/docker/image/overlay2/layerdb/sha256/13d5529fd232cacdd8cd561148560e0bf5d65dbc1149faf0c68240985607c303/diff
Этот первый слой является изображением debian:9
. Мы можем подтвердить это, запустив:
$ docker image inspect debian:9 | jq '.[0].RootFS.Layers'
[
"sha256:13d5529fd232cacdd8cd561148560e0bf5d65dbc1149faf0c68240985607c303"
]
...так что проигнорируем. Найдем второй слой:
# grep -l 41494b03ef195ce6db527bd68b89cbebdace66210b4c142e95f8553fcb0bf51e \
/var/lib/docker/image/overlay2/layerdb/sha256/*/diff
/var/lib/docker/image/overlay2/layerdb/sha256/14347a192896a59fdf5c1a9ffcac2f93025433c66136d3531d7bbb3aec53efc7/diff
Внутри того же каталога, что и этот файл diff
, мы найдем файл с именемcache-id
:
# cat image/overlay2/layerdb/sha256/14347a192896a59fdf5c1a9ffcac2f93025433c66136d3531d7bbb3aec53efc7/cache-id
118b1e4a401873e1db8849c0821d0280b4cf9ef621ccb70cf14fe672dc74ef75
Что cache-id
идентифицирует каталог, в который был извлечен слой; мы можем найти его под/var/lib/docker/overlay2/<id>
:
# ls /var/lib/docker/overlay2/118b1e4a401873e1db8849c0821d0280b4cf9ef621ccb70cf14fe672dc74ef75
diff/ link lower work/
Нас интересует содержимое каталога diff/
:
# find /var/lib/docker/overlay2/118b1e4a401873e1db8849c0821d0280b4cf9ef6
21ccb70cf14fe672dc74ef75/diff/
/var/lib/docker/overlay2/118b1e4a401873e1db8849c0821d0280b4cf9ef621ccb70cf14fe672dc74ef75/diff/
/var/lib/docker/overlay2/118b1e4a401873e1db8849c0821d0280b4cf9ef621ccb70cf14fe672dc74ef75/diff/root
/var/lib/docker/overlay2/118b1e4a401873e1db8849c0821d0280b4cf9ef621ccb70cf14fe672dc74ef75/diff/root/file.txt
Вот оно!
Примечание. Все вышеперечисленное предполагает, что вы используете overlay2
драйвер хранилища (, который в настоящее время используется по умолчанию на большинстве, если не на всех платформах ). Если вы используете другой драйвер, расположение на диске будет другим.
— это всего лишь один из многих типов файлов. Другие типы включают символическую ссылку , fifo , устройство , обычный , сокет ...
В выводе ls -n
первый символ указывает на тип. d
для каталога, -
для обычного , l
для символической ссылки , так что вы можете сделать:
LC_ALL=C ls -Aqn | LC_ALL=C grep -c '^-' # regular files only
LC_ALL=C ls -Aqn | LC_ALL=C grep -vc '^d' # anything but directory
(удалите опцию -A
, если вы не хотите считать скрытые файлы ).
Вы также можете выполнить проверку после разрешения символической ссылки ,
LC_ALL=C ls -LAqn | LC_ALL=C grep -c '^-' # regular files or symlinks to regulars
LC_ALL=C ls -LAqn | LC_ALL=C grep -vc '^d' # anything but directory and symlinks to dirs.
В оболочке zsh
это также можно сделать с помощью подстановки:
(){print $#} *(NDoN.) # regular
(){print $#} *(NDoN^/) # non-directories
После разрешения символических ссылок (добавлен квалификатор -
glob):
(){print $#} *(NDoN-.) # regular
(){print $#} *(NDoN-^/) # non-directories
(удалите квалификатор D
, если вы не хотите подсчитывать скрытые файлы ).
oN
— это отключить сортировку списка файлов в качестве оптимизации, поскольку нам не важен порядок. Реализация GNU ls
имеет для этого опцию -U
.
В любом случае ваш ls | wc -l
неверен, поскольку он подсчитывает количество символов новой строки, используемых ls
для разграничения имен файлов (, по одному на файл ), а также символы новой строки в именах. файла (он также опускает скрытые файлы ). Использование параметра -q
помогает обойти это, так как символы новой строки в именах файлов отображаются как ?
.
Обычно я использую:
find. -maxdepth 1 -type f | wc -l
Остерегайтесь этого:
.
Я пока не могу оставлять комментарии, но вот решение для bash -. Это не так лаконично, но может казаться чище без использования grep
.
Поскольку вы использовали только ls
, я предполагаю, что вам не нужны скрытые файлы.
i=0
for f in *
do
[[ -f "$f" ]] && let "i++"
done
echo $i
Или объединены в один -вкладыш,i=0; for f in *; do [[ -f "$f" ]] && let "i++"; done; echo $i