
Долгое время я создавал приложение с использованием Docker с промежуточным контейнером, например, я создавал приложение, myapp:base
в котором скопированы все основные файлы:
FROM ubuntu:17.10
ADD app /app
Теперь основной контейнер myapp:release
можно создать просто с помощью этого
FROM myapp:base
ADD app /app
В каталоге можно изменить несколько файлов app
, и в результате получится небольшой дополнительный слой.
Однако недавно этот финал ADD
привел к появлению гораздо большего слоя, и я подозреваю, что это связано с обновлением Docker (я использую Docker версии 18.02.0-ce, сборка fc4de44)
Изменилось ли что-то в том, как Docker определяет, что отличается? Как мне вернуться к эффективной сборке, которая просто имеет небольшое количество обновленных файлов в слое?
Действия по воспроизведению:
# make a dummy dir with a 64kb file in it
mkdir -p files
truncate -s 64k files/64k.file
# base container has a copy of the files
cat << EOF > Dockerfile.base
FROM ubuntu:16.04
COPY files/ /root/
EOF
# derived container should just have any updates
cat << EOF > Dockerfile.derived
FROM q899941:base
COPY files/ /root/
EOF
# build them....
docker build --file Dockerfile.base -t q899941:base .
docker build --file Dockerfile.derived -t q899941:derived .
# now let's review the layers
docker history q899941:derived
Я получаю этот результат
IMAGE CREATED CREATED BY SIZE
4e1eb5168d55 Less than a second ago /bin/sh -c #(nop) COPY dir:8e20ede288278c71e… 65.5kB
022626ac5cf0 Less than a second ago /bin/sh -c #(nop) COPY dir:8e20ede288278c71e… 65.5kB
0458a4468cbc 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 5 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB
<missing> 5 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 5 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 5 weeks ago /bin/sh -c #(nop) ADD file:a3344b835ea6fdc56… 112MB
Верхний слой занимает 64 КБ, хотя файлы не изменялись.
решение1
Кажется, этоизвестная проблема, но не тот, который бы волновал достаточное количество людей!
- Есть ли способ добавить только измененные файлы в образ Docker в качестве нового слоя, не прибегая к Docker Commit?— вопрос на StackOverflow, который касается той же темы.
- Похоже, ожидаемое мной поведение работало с драйвером хранилища оверлея, но после некоторых обновлений системы я начал использовать overlay2, который копирует все файлы в слой, а не только измененные.
- Хотя одним из решений был бы возврат к наложению, на данный момент оно, похоже, уже не рекомендуется.Альтернативный метод построения «разностных» слоев подробно описан здесь.
В итоге я сделал простую вещь, упомянутую в одном изКомментариив отчете об ошибке:
#clean up any previous attempt...
docker rm -f uniquename 2> /dev/null
# now take your base container, mount the updated dir as /src
# then rsync from /src to the target dir - only updated files will
# actually be written, and we use --delete to ensure removed files are
# taken out...
docker run --name uniquename \
-v ~/repo/mycode:/src \
${REPO}/${IMAGE}:${BASE} \
rsync -ar --no-owner --no-group \
--exclude-from '/src/.dockerignore' --delete \
/src/ /app/
# we can commit that updated container with a tag
docker commit uniquename ${REPO}/${IMAGE}:${NEW_TAG}
Еще одна вещь - это решение, похоже, стирает существующую, CMD
которая предоставляла моим контейнерам команду запуска по умолчанию. Поэтому после добавления этого слоя diff у меня был еще один Dockerfile для создания контейнера из сборки 'diff' выше с добавленным CMD
обратно.