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

236

Оригинальный вопрос: Как использовать инструкцию 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
XPT
источник
По-видимому, более актуальный запрос функций (не то, чтобы я ожидал, что он будет реализован, но на всякий случай): docker / docker # 14080
Джесси Глик
действительно, существует обширное обсуждение того, что нельзя позволять связывать каталог хоста и каталог контейнера во время сборки, то есть что-то вроде VOLUME ~/host_dir ~/container_dir. Дискуссия довольно обширная, если кратко изложить, в чем причина?
Чарли Паркер

Ответы:

34

Во-первых, чтобы ответить "почему не 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...

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

Если вы получаете сообщение об ошибке, что флаг монтирования не поддерживается, это означает, что вы либо не включили buildkit с помощью указанной выше переменной, либо что ранее вы не включали экспериментальный синтаксис со строкой синтаксиса в верхней части Dockerfile. любые другие строки, включая комментарии. Обратите внимание, что переменная для переключения buildkit будет работать только в том случае, если в вашей установке Docker встроена поддержка buildkit, для которой требуется версия 18.09 или новее из Docker, как на клиенте, так и на сервере.

BMitch
источник
2
К сожалению, в Windows Buildkit еще не поддерживается в версии 18.09
Wesley
1
Похоже, "armhf" также не поддерживает "mount".
Майк
2
Я получаю сообщение «Ошибка ответа от демона: строка ошибки синтаксического анализа Dockerfile xx: неизвестный флаг: монтирование» в OSX
ChristoKiwi
1
Поддержка docker-compose пока не существует, но вам не нужно создавать для создания образов. Отслеживаемая проблема: github.com/moby/buildkit/issues/685
BMitch
2
Документация по этому поводу: github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/…
Дрю ЛеСюр
116

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

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

Андреас Штеффан
источник
2
Спасибо. Вопрос пересмотрен. Фактический вопрос, который я хочу решить, - как смонтировать тома хоста в Docker-контейнеры в Dockerfile во время сборки. Спасибо.
xpt
2
Невозможно. Смотрите пересмотренный ответ.
Андреас Стеффан
3
Я могу оценить «потенциальные» побочные эффекты, связанные с переносимостью, но для этой опции есть и правильный вариант использования. В моем случае мне бы хотелось, чтобы пользователи могли «перейти в каталог и запустить команду« docker run »», установив $ (PWD) в некоторый каталог-контейнер. $ (PWD) обеспечивает переносимость. Несмотря на то, что это может быть ключевой случай, это очень поможет мне в том, где я распространяю среды выполнения для пользовательских сценариев.
ntwrkguru
64

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

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

Решение 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 для получения более подробной информации.

XPT
источник
Я пытаюсь использовать Rocker для решения проблемы № 1, но команда mount не будет работать, и созданный образ не содержит папку хоста. Моя команда монтирования в Dockerfile выглядит следующим образом - MOUNT ~/code/docker-app-dev/new-editor/:/src/а моя команда сборки Rocker - такая rocker build -f Dockerfile .. Что я делаю не так?
Ярон Идан
Может быть, попробовать использовать реальный путь к хосту? ~метасимвол оболочки Борна
Джесси Глик
Rocker buildне разрешает параметры docker runкомандной строки, поэтому в настоящее время не разрешает такие вещи, как --privileged.
Монти Уайлд
Привет @xpt, можем ли мы получить еще одно обновление, так как рокер больше не
выпускается
Теперь, когда рокер снят с производства, я снова возвращаю ответ на «Невозможно». Смотрите ОП и выбранный ответ.
xpt
14

Существует способ монтировать том во время сборки, но он не включает 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
Кит Мейсон
источник
6
+1 Не могли бы вы подробнее рассказать о инструкциях во втором абзаце. Например, если база debian:wheezyи сценарий оболочки build.sh, какие конкретные инструкции будут использовать?
Друкс
6

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

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

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

docker run -v /export:/export data

Так что вы бы использовали папку hosts внутри вашего контейнера.

Behe
источник
1
Спасибо. Вопрос пересмотрен. Фактический вопрос, который я хочу решить, - как смонтировать тома хоста в Docker-контейнеры в Dockerfile во время сборки. Спасибо.
xpt
Пожалуйста, не пересматривайте свои вопросы так радикально . Это делает мой вопрос недействительным, хотя он был совершенно действительным до ваших правок. Попробуйте вместо этого задать новый вопрос.
Бех
11
Оригинальный вопрос : Как использовать инструкцию VOLUME в Dockerfile? Это все еще в самом начале вопроса, даже на сегодняшний день. Ваш ответ был для времени выполнения , и мой вопрос всегда был о времени сборки , для чего и предназначен Dockerfile.
xpt
4

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

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

nealmcb
источник
3

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

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копирует их обратно в образ для следующей сборки.

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

MatrixManAtYrService
источник
Это жизнеспособное решение для непрерывной интеграции на основе Docker или CI. Настройте библиотеки и компиляторы и запустите make с помощью команд Dockerfile, тривиально запустите образ, чтобы создать контейнер, и, наконец, скопируйте нужный артефакт, например, .deb. Кажется, работает, спасибо за размещение этого.
chrisinmtown
Это решение оставляет вам изображение со ВСЕМИ файлами в ./m2/ - то, что вам нужно, и то, что вам не нужно - и это может привести к ОГРОМНЫМ производственным изображениям, что нежелательно! При подключении к каталогу внешних зависимостей в образ будут скопированы только необходимые файлы.
Марко Крайнц
Если вы намереваетесь опубликовать изображение, вероятно, лучше подождать и позволить maven каждый раз заново загружать свои собственные зависимости. Этот хак имеет смысл, только если вы ставите изображение для тестирования - изображение, с которым конечные пользователи никогда не соприкоснутся.
MatrixManAtYrService
1

Вот упрощенная версия двухэтапного подхода с использованием 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.

Akom
источник
1

Как уже отвечали многие, монтировать тома хоста во время сборки невозможно. Я просто хотел бы добавить docker-composeспособ, я думаю, что это будет хорошо, в основном для разработки / тестирования

Dockerfile

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999

докер-compose.yml

version: '3'
services:
  test-service:
    image: test/image
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./export:/app/export
      - ./build:/app/build

И запустить свой контейнер docker-compose up -d --build

Егор Заремба
источник