Как монтировать тома хоста в контейнеры докеров в Dockerfile во время сборки

Оригинальный вопрос: Как использовать инструкцию VOLUME в Dockerfile?

Фактический вопрос, который я хочу решить, - как смонтировать тома хоста в докер-контейнеры в Dockerfile во время сборки, т.е. С помощью того, чтобы docker run -v/export: /export возможность во время docker build.

Причиной этого для меня является то, что при сборке вещей в Docker я не хочу, чтобы эти (apt-get install) кэши были заблокированы в одном докере, а чтобы делиться/повторно использовать их. Вот основная причина, я задаю этот вопрос.

Последнее обновление:

До docker v18.09 правильный ответ должен быть таким, который начинается с:

Существует способ монтировать том во время сборки, но он не включает Dockerfiles.

Однако это был плохо сформулированный, организованный и подтвержденный ответ. Когда я переустанавливал свой докер, я наткнулся на следующую статью:

Докеризация службы apt-cacher-ng
https://docs.docker.com/engine/examples/apt-cacher-ng/

Это докер решение этого/моего вопроса, не прямо, а косвенно. Это нам предлагает делать ортодоксальный способ докера. И я признаю, что это лучше, чем тот, который я пытался спросить здесь.

Другой способ, недавно принятый ответ, например, Buildkit в v18.09.

Выберите тот, который подходит вам.


Был: было решение - рокер, который был не от Docker, но теперь, когда рокер снят с производства, я снова возвращаю ответ на "Не возможно" снова.


Старое обновление: так что ответ "Не возможно". Я могу принять его как ответ, так как знаю, что этот вопрос широко обсуждался на https://github.com/docker/docker/issues/3156. Я могу понять, что переносимость является первостепенной проблемой для разработчика докеров; но как пользователь докера, я должен сказать, что очень разочарован этой отсутствующей функцией. Позвольте мне завершить свой аргумент цитатой из вышеупомянутого обсуждения: "Я хотел бы использовать Gentoo в качестве базового изображения, но определенно не хочу, чтобы после создания изображения было более 1 ГБ данных дерева Portage в любом из слоев. могли бы иметь хорошие компактные контейнеры, если бы не гигантское дерево портежей, которое должно появиться на изображении во время установки ". Да, я могу использовать wget или curl для загрузки всего, что мне нужно, но тот факт, что из соображений переносимости теперь я вынужден загружать> 1 ГБ дерева Portage каждый раз, когда я создаю базовый образ Gentoo, не эффективен и не удобен для пользователя. Более того, хранилище пакетов ВСЕГДА будет находиться в /usr/portage, поэтому ВСЕГДА ПОРТАТИВНО в Gentoo. Опять же, я уважаю это решение, но, пожалуйста, позвольте мне также выразить свое разочарование в то же время. Благодарю.


Оригинальный вопрос в деталях:

От

Поделиться каталогами через тома
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/

в нем говорится, что функция томов данных "была доступна с версии 1 Docker Remote API". Мой докер версии 1.2.0, но я обнаружил, что приведенный выше пример не работает:

# BUILD-USING:        docker build -t data .
# RUN-USING:          docker run -name DATA data
FROM          busybox
VOLUME        ["/var/volume1", "/var/volume2"]
CMD           ["/usr/bin/true"]

Как правильно в Dockerfile монтировать тома, смонтированные на хосте, в контейнеры Docker с помощью команды VOLUME?

$ apt-cache policy lxc-docker
lxc-docker:
  Installed: 1.2.0
  Candidate: 1.2.0
  Version table:
 *** 1.2.0 0
        500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
        100 /var/lib/dpkg/status

$ cat Dockerfile 
FROM          debian:sid

VOLUME        ["/export"]
RUN ls -l /export
CMD ls -l /export

$ docker build -t data .
Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon 
Step 0 : FROM          debian:sid
 ---> 77e97a48ce6a
Step 1 : VOLUME        ["/export"]
 ---> Using cache
 ---> 59b69b65a074
Step 2 : RUN ls -l /export
 ---> Running in df43c78d74be
total 0
 ---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
 ---> Running in 8e4916d3e390
 ---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551

$ docker run data
total 0

$ ls -l /export | wc 
     20     162    1131

$ docker -v
Docker version 1.2.0, build fa7b24f

Ответ 1

Во-первых, чтобы ответить "почему не работает VOLUME?" Когда вы определяете VOLUME в Dockerfile, вы можете определить только цель, а не источник тома. Во время сборки вы получите только анонимный том от этого. Этот анонимный том будет подключаться к каждой команде RUN, предварительно заполняться содержимым образа и затем удаляться в конце команды RUN. Сохраняются только изменения в контейнере, а не изменения в объеме.


Поскольку этот вопрос был задан, было выпущено несколько функций, которые могут помочь. Во-первых, это многоэтапные сборки, позволяющие вам создать неэффективное место на диске на первом этапе и скопировать только необходимый вывод на последний этап, который вы отправляете. И вторая особенность - это Buildkit, которая кардинально меняет способ создания образов и добавления новых возможностей в сборку.

Для многоступенчатой сборки у вас должно быть несколько строк FROM, каждая из которых начинает создание отдельного образа. Только последнее изображение помечено по умолчанию, но вы можете копировать файлы с предыдущих этапов. Стандартное использование состоит в том, чтобы иметь среду компилятора для создания бинарного или другого артефакта приложения и среду выполнения в качестве второго этапа копирования этого артефакта. Вы могли бы иметь:

FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin

FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]

Это приведет к сборке, содержащей только получившийся двоичный файл, а не каталог full/export.


Buildkit выходит из экспериментальной версии 18.09. Это полный редизайн процесса сборки, включая возможность изменения синтаксического анализатора внешнего интерфейса. В одном из этих изменений парсера реализована опция RUN --mount которая позволяет вам смонтировать каталог кэша для ваших команд запуска. Например, один, который монтирует некоторые каталоги debian (с переконфигурированием образа debian это может ускорить переустановку пакетов):

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache \
    --mount=target=/var/cache/apt,type=cache \
    apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      git

Вы должны настроить каталог кэша для любого кэша приложения, например, $ HOME/.m2 для maven или /root/.cache для golang.


TL; DR: Ответ здесь: с этим синтаксисом RUN --mount вы также можете связать каталоги монтирования только для чтения из контекста сборки. Папка должна существовать в контексте сборки, и она не отображается обратно на хост или клиент сборки:

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...

Обратите внимание: поскольку каталог монтируется из контекста, он также монтируется только для чтения, и вы не можете отправить изменения обратно на хост или клиент. При сборке вы захотите установить export DOCKER_BUILDKIT=1 18.09 или новее и включить export DOCKER_BUILDKIT=1 комплект с export DOCKER_BUILDKIT=1.

Ответ 2

Невозможно использовать команду VOLUME, чтобы сообщить docker , что для монтирования. Это серьезно нарушит переносимость. Эта инструкция сообщает докере, что содержимое в этих каталогах не входит в изображения и может быть доступно из других контейнеров, используя параметр командной строки --volumes-from. Вы должны запустить контейнер с помощью -v /path/on/host:/path/in/container для доступа к каталогам с хоста.

Установка томов хоста во время сборки невозможна. Нет привилегированной сборки, и установка хоста также серьезно ухудшит переносимость. Вы можете попробовать использовать wget или curl для загрузки всего, что вам нужно для сборки, и поместить его на место.

Ответ 3

ОБНОВЛЕНИЕ: Кто-то просто не примет "нет" как ответ, и мне это очень нравится, особенно на этот конкретный вопрос.

Хорошие новости, есть способ -

Решение Rocker: https://github.com/grammarly/rocker

Джон Яни сказал: "ИМО, он решает все слабые места Dockerfile, делая его пригодным для разработки".

коромысло

https://github.com/grammarly/rocker

Вводя новые команды, Rocker стремится решить следующие варианты использования, которые болезненны для простого Docker:

  1. Монтируйте повторно используемые тома на этапе сборки, чтобы инструменты управления зависимостями могли использовать кеш между сборками.
  2. Делитесь ключами ssh со сборкой (для извлечения приватных репозиториев и т.д.), Не оставляя их в результирующем образе.
  3. Создавайте и запускайте приложения на разных изображениях, уметь легко передавать артефакт из одного изображения в другое, в идеале иметь эту логику в одном Dockerfile.
  4. Помечать/отправлять изображения прямо из Dockerfiles.
  5. Передайте переменные из команды сборки оболочки, чтобы они могли быть заменены в Dockerfile.

И больше. Это наиболее важные проблемы, которые мешали нам принять Docker в Grammarly.

Обновление: Rocker был прекращен, согласно официальному репо проекта на Github

По состоянию на начало 2018 года контейнерная экосистема стала более зрелой, чем три года назад, когда был начат этот проект. Теперь некоторые важные и выдающиеся функции рокера могут быть легко раскрыты с помощью сборки докера или других хорошо поддерживаемых инструментов, хотя некоторые функции остаются уникальными для рокера. См. Https://github.com/grammarly/rocker/issues/199 для получения более подробной информации.

Ответ 4

Существует способ монтировать том во время сборки, но он не включает Dockerfiles.

Техника будет состоять в том, чтобы создать контейнер из любой базы, которую вы хотите использовать (смонтировать ваш том в контейнере с -v), запустить скрипт оболочки, чтобы выполнить работу по созданию образа, а затем зафиксировать контейнер как изображение, когда сделано.

Это не только исключит лишние файлы, которые вам не нужны (это также хорошо для защищенных файлов, таких как файлы SSH), но также создаст одно изображение. У этого есть недостатки: команда commit не поддерживает все инструкции Dockerfile, и она не позволяет вам забрать, когда вы остановились, если вам нужно отредактировать ваш скрипт сборки.

ОБНОВИТЬ:

Например,

CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c '/bin/sh /build.sh'
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID

Ответ 5

Когда вы запускаете контейнер, каталог на вашем хосте создается и монтируется в контейнер. Вы можете узнать, что это каталог с

$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]

Если вы хотите установить каталог с вашего хоста внутри вашего контейнера, вы должны использовать параметр -v и указать каталог. В вашем случае это будет:

docker run -v /export:/export data

SO, вы должны использовать папку hosts внутри вашего контейнера.

Ответ 6

Я думаю, что вы можете делать то, что хотите, запустив сборку с помощью команды docker, которая сама запускается внутри контейнера докеров. См. Теперь Docker может работать в Docker | Блог Докера. Подобная методика, но которая фактически обращалась к внешнему докеру с помощью контейнера, использовалась, например, при изучении Создать наименьший возможный контейнер Docker | Блог Xebia.

Другая соответствующая статья Оптимизация изображений докеров | CenturyLink Labs, в котором объясняется, что, если вы загружаете материал во время сборки, вы можете избежать потери пространства в финальном изображении, загрузив, создав и удалив загрузку всего на одном шаге RUN.

Ответ 7

Это некрасиво, но я достиг подобия этого примерно так:

Dockerfile:

FROM foo
COPY ./m2/ /root/.m2
RUN stuff

imageBuild.sh:

docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"

У меня есть Java-сборка, которая загружает юниверс в /root/.m2, и делал это каждый раз. imageBuild.sh копирует содержимое этой папки на хост после сборки, а Dockerfile копирует их обратно в образ для следующей сборки.

Это что-то вроде того, как будет работать том (т.е. он сохраняется между сборками).

Ответ 8

Вот упрощенная версия двухэтапного подхода с использованием build и commit без сценариев оболочки. Это включает в себя:

  1. Построение изображения частично, без объемов
  2. Запуск контейнера с томами, внесение изменений, затем фиксация результата, замена исходного имени изображения.

С относительно небольшими изменениями дополнительный шаг добавляет всего несколько секунд ко времени сборки.

В принципе:

docker build -t image-name . # your normal docker build

# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command

# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)   
docker commit --change="CMD bash" temp-container image-name 

# Delete the temporary container:
docker rm temp-container

В моем случае я хочу предварительно сгенерировать файл maven toolchains.xml, но многие мои установки JDK находятся на томе, который недоступен до времени выполнения. Некоторые из моих изображений несовместимы со всеми JDKS, поэтому мне нужно проверить совместимость во время сборки и условно заполнить toolchains.xml. Обратите внимание, что мне не нужно, чтобы изображение было переносимым, я не публикую его в Docker Hub.