Як встановити томи хостів у контейнери докера в 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.

Виберіть те, що вам підходить.


Було: Було рішення - рокер, який був не з Докера, але тепер, коли рокер припинено, я знову повертаю відповідь до "Неможливо" .


Старе оновлення: Отже, відповідь "Неможливо". Я можу прийняти це як відповідь, оскільки мені відомо, що це питання широко обговорювалося на веб-сторінці https://github.com/docker/docker/isissue/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 API віддаленого доступу Docker". Мій докер має версію 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 монтувати встановлені хостом томи в контейнери докера за допомогою команди 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

Мабуть, більш актуальний запит на функцію (не те, що я очікую, що він буде реалізований, але про всяк випадок): docker / docker # 14080
Jesse Glick

Дійсно, існує велика дискусія про те, що не слід дозволити зв'язувати каталог хостів і каталог контейнерів під час збирання, тобто щось подібне VOLUME ~/host_dir ~/container_dir. Дискусія досить обширна, то є короткий спосіб узагальнити, в чому причина?
Чарлі Паркер

Відповіді:


34

По-перше, щоб відповісти "чому не VOLUMEпрацює?" Під час визначення VOLUMEDockerfile ви можете визначити лише ціль, а не джерело гучності. Під час збирання ви отримаєте лише анонімний том від цього. Цей анонімний том буде встановлений у кожній 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"]

Це призведе до збірки, яка містить лише отриманий бінарний файл, а не повний / експортний каталог.


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 працюватиме лише у тому випадку, якщо у вашій установці докера вбудована підтримка buildkit, для якої потрібна версія 18.09 або новіша версія від Docker, як на клієнті, так і на сервері.


2
На жаль, у Windows Buildkit ще не підтримується версія 18.09
Wesley

1
Схоже, що "armhf" також не підтримує "mount".
Майк

2
Я отримую "Відповідь на помилку від демон: Dockerfile синтаксичний аналіз синтаксичного розбору xx: Невідомий прапор: mount" на OSX
ChristoKiwi

1
Підтримка docker-compose ще не існує, але для створення зображень вам не потрібна композиція. Випуск для відстеження: github.com/moby/buildkit/isissue/685
BMitch


116

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

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


2
Дякую. Питання переглянуто. Справжнє питання, яке я хочу вирішити, - як встановити томи хостів у контейнери докера в Dockerfile під час збирання. Дякую.
xpt

2
Неможливо. Дивіться переглянутий відповідь.
Андреас Стеффан

3
Я можу оцінити "потенційні" погані побічні ефекти переносимості, але є також дійсний приклад використання для цього. У моєму випадку я б хотів сказати користувачам "Перейти до каталогу та запустити команду" запустити докер ", маючи $ (PWD), встановлену в якомусь контейнері. $ (PWD) забезпечує збереження портативності. Хоча це може бути кутовим випадком, це допоможе мені надзвичайно де я поширюю середовища виконання для наданих користувачем сценаріїв.
ntwrkguru

64

ОНОВЛЕННЯ: Хтось просто не сприймає не як відповідь, і мені це дуже подобається, особливо в цьому конкретному питанні.

ДОБРІ НОВИНИ. Зараз є спосіб -

Рішення - Rocker: https://github.com/grammarly/rocker

Джон Яні сказав : "IMO, це вирішує всі слабкі місця Dockerfile, роблячи його придатним для розвитку".

Рокер

https://github.com/grammarly/rocker

Вводячи нові команди, Рокер прагне вирішити такі випадки використання, які болісні для простого Докера:

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

І більше. Це найважливіші проблеми, які блокували наше прийняття Докера в Grammarly.

Оновлення: Rocker було припинено, згідно офіційного репортажу проекту на Github

На початок 2018 року екосистема контейнерів набагато зріліша, ніж це було три роки тому, коли цей проект був ініційований. Тепер деякі критичні та видатні особливості рокера можна легко покрити складанням докера або іншими добре підтримуваними інструментами, хоча деякі функції залишаються унікальними для рокера. Дивіться https://github.com/grammarly/rocker/isissue/199 для отримання більш детальної інформації.


Я намагаюся використовувати Rocker для вирішення проблеми № 1, але команда mount не працюватиме, а створене зображення не містить папки хоста. Моя команда кріплення Dockerfile виглядає приблизно так - MOUNT ~/code/docker-app-dev/new-editor/:/src/і моя команда побудови Rocker - це така - rocker build -f Dockerfile .. Що я маю неправильно?
Ярон Ідан

Можливо, спробуйте використовувати справжній шлях до хоста? ~- метахарактер оболонки Борна.
Джессі Глік

Rocker buildне дозволяє параметри docker runкомандного рядка, тому наразі не дозволяє подібні речі --privileged.
Monty Wild

Привіт @xpt, чи можемо ми отримати ще одне оновлення, оскільки рокер зараз припинено
Shardj

Тепер, коли рокер припинено, я знову повертаю відповідь до "Неможливо". Див. ОП та обрану відповідь.
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 у вашому контейнері.


1
Дякую. Питання переглянуто. Справжнє питання, яке я хочу вирішити, - як встановити томи хостів у контейнери докера в Dockerfile під час збирання. Дякую.
xpt

Будь ласка, не переглядайте свої запитання настільки різко . Це робить моє питання недійсним, хоча воно було абсолютно дійсним перед вашими правками. Подумайте замість цього.
Бехе

11
Оригінальне запитання : Як користуватися інструкцією VOLUME в Dockerfile? Це ще на самому початку питання навіть сьогодні. Ваша відповідь була на час запуску , і моє запитання завжди стосувалося часу збирання , саме для цього Dockerfile.
xpt

4

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

Інша відповідна стаття - Оптимізація Docker Images | CenturyLink Labs , в якій пояснюється, що якщо ви закінчите завантажувати речі під час збирання, ви можете уникнути втрати ним місця на остаточному зображенні, завантаживши, склавши та видаливши завантаження все за один крок RUN.


3

Це некрасиво, але я досяг такого подібного вигляду:

Докерфайл:

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копіює їх назад у зображення для наступної збірки.

Це щось на зразок того, як би працював об'єм (тобто він зберігається між складами).


Це життєздатне рішення для безперервної інтеграції на основі Докера aka CI. Налаштуйте бібліотеки та компілятори і запустіть make за допомогою команд Dockerfile, запустіть зображення тривіально просто для створення контейнера і, нарешті, скопіюйте потрібний артефакт на зразок .deb. Здається, працює, дякую за повідомлення про це.
chrisinmtown

Це рішення залишає вам зображення із ВСІМ файлами у ./m2/ - той, який вам потрібен, і той, який вам не потрібен - і це може призвести до ВЕЛИЧЕЗНИХ виробничих зображень, що не бажано! При монтажі до каталогу зовнішніх залежностей копіюватимуться лише потрібні файли.
Marko

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

1

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

  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.


1

Як уже багато хто відповів, встановити об’єми хостів під час збирання неможливо. Я просто хотів би додати docker-composeспосіб, я думаю, це буде непогано мати, в основному, для використання / тестування використання

Докерфайл

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

docker-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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.