Как включить файлы вне контекста сборки Docker?

462

Как включить файлы из контекста сборки Docker, используя команду «ADD» в файле Docker?

Из документации Docker:

Путь должен быть внутри контекста сборки; Вы не можете ДОБАВИТЬ ../something/something, потому что первый шаг сборки докера - это отправить каталог контекста (и подкаталоги) демону докера.

Я не хочу реструктурировать весь свой проект, чтобы приспособить Docker к этому вопросу. Я хочу сохранить все мои файлы Docker в одном подкаталоге.

Кроме того, похоже, что Docker еще не поддерживает (и может не поддерживать) символические ссылки : команда Dockerfile ADD не следует по символическим ссылкам на хосте # 1676.

Единственное, о чем я могу подумать, это включить шаг перед сборкой, чтобы скопировать файлы в контекст сборки Docker (и настроить контроль версий для игнорирования этих файлов). Есть ли лучший обходной путь для этого?

ben_frankly
источник
94
Это должно быть худшее в Docker. С моей точки зрения, такого понятия, как «проект Docker», не существует. Докер для судоходных проектов. Это просто инструмент. Я не хочу перестраивать весь мой проект в соответствии с docker, добавляя .dockerignore и т. Д. В конце концов, кто знает, как долго продлится Docker? Было бы здорово иметь разделение между кодом (т. Е. Угловым проектом) и любыми средствами для его развертывания (т. Е. Докером). В конце концов, в действительности нет никакого преимущества иметь файл Docker рядом со всем остальным. Это просто разводка, чтобы создать образ :(
TigerBear
3
Да, это большой депрессант. Я столкнулся с той же проблемой, и у меня есть бинарный файл большего размера (уже сжатый), который я не хочу копировать в каждый контекст сборки Docker. Я бы предпочел получить его из текущего местоположения (вне контекста сборки Docker). И я не хочу отображать том во время выполнения, потому что я пытаюсь скопировать / ДОБАВИТЬ файл во время сборки и разархивировать и сделать то, что мне нужно, чтобы определенные двоичные файлы запекались в образе. Таким образом, раскручивание контейнеров происходит быстро.
Джерси бин
Я нашел хорошую структуру и подробно объясняю на stackoverflow.com/a/53298446/433814
Марчелло де Сэйлс
1
проблема со сборками докеров - это выдуманная концепция «контекста». Dockerfiles не достаточно для определения сборки, если только они не помещены в стратегический каталог (или контекст), то есть "/" как крайний, поэтому вы можете получить доступ к любому пути (обратите внимание, что это не правильно делать в нормальном проекте либо ..., плюс это делает сборку докера очень медленной, потому что докер сканирует весь контекст при запуске). Вы можете рассмотреть возможность создания образа докера со всеми необходимыми файлами и использовать его FROMдля продолжения. Я бы не стал менять структуру проекта, чтобы приспособить ее к Docker (или к каким-либо инструментам сборки).
Девис Л.

Ответы:

414

Лучший способ обойти это - указать Dockerfile независимо от контекста сборки, используя -f.

Например, эта команда предоставит команде ADD доступ ко всему вашему текущему каталогу.

docker build -f docker-files/Dockerfile .

Обновление : Docker теперь позволяет иметь Dockerfile вне контекста сборки (исправлено в 18.03.0-ce, https://github.com/docker/cli/pull/886 ). Так что вы также можете сделать что-то вроде

docker build -f ../Dockerfile .
Cyberwiz
источник
8
@Ro. вы используете dockerfile:свойство в build:разделе в файле Compose docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson Farrugia
3
Я получаю «Dockerfile должен быть в контексте сборки» - мне бы очень хотелось иметь один Dockerfile, который может находиться ниже текущего контекста сборки. В вашем примере у вас есть Dockerfile в / ниже текущего контекста сборки, что, конечно, работает.
Александр Миллс
3
Да, я просто хочу общий Dockerfile, который соответствует нескольким подкаталогам, все из которых являются "контекстами сборки"
Александр Миллс
51
Решает ли это проблему ОП, связанную с желанием ADDполучить файл, находящийся вне каталога контекста? Это то, что я пытаюсь сделать, но я не думаю, что использование -fделает внешние файлы добавляемыми.
Шридхар Сарнобат
18
Не может upvote это достаточно .. в моем докер-compose.yml у меня есть: build: context: .., dockerfile: dir/Dockerfile. Теперь мой контекст сборки - это родительский каталог!
Майк Глисон-младший Кутюрье
51

Я часто использую эту --build-argопцию для этой цели. Например, после добавления следующего в Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa

Вы можете просто сделать:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .

Но обратите внимание на следующее предупреждение из документации Docker :

Предупреждение: не рекомендуется использовать переменные времени сборки для передачи таких секретов, как ключи github, учетные данные пользователя и т. Д. Значения переменных времени сборки видны любому пользователю образа с помощью команды истории докера.

user2658308
источник
6
Это плохой совет без огромного предупреждения. Из документации Docker: «Предупреждение. Не рекомендуется использовать переменные времени сборки для передачи таких секретов, как ключи github, учетные данные пользователя и т. Д. Значения переменных времени сборки видны любому пользователю образа с помощью команды истории докера». [1] Другими словами, пример, приведенный в этом примере, раскрывает закрытый ключ SSH в образе докера. В некоторых случаях это может быть хорошо. docs.docker.com/engine/reference/builder/#arg
Шелдон
3
Наконец, чтобы преодолеть эту проблему безопасности, вы можете использовать такие методы, как сжатие или многоэтапное построение: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo
46

В Linux вы можете смонтировать другие каталоги вместо ссылки на них

mount --bind olddir newdir

См. Https://superuser.com/questions/842642 для получения более подробной информации.

Я не знаю, доступно ли что-то подобное для других ОС. Я также попытался использовать Samba для совместного использования папки и перемонтирования ее в контекст Docker, который также работал.

Гюнтер Цохбауэр
источник
2
Только root может связывать каталоги
jjcf89
28

Я провел хорошее время, пытаясь выяснить хороший шаблон и как лучше объяснить, что происходит с поддержкой этой функции. Я понял, что лучший способ объяснить это было следующим ...

  • Dockerfile: будет видеть файлы только по своему относительному пути
  • Контекст: место в «пространстве», куда будут скопированы файлы, которыми вы хотите поделиться, и ваш Dockerfile

Итак, с учетом сказанного, вот пример Dockerfile, который должен повторно использовать файл с именем start.sh

Dockerfile

Он ALWAYSбудет загружаться с относительного пути, имея текущий каталог dir в качестве localссылки на указанные вами пути.

COPY start.sh /runtime/start.sh

файлы

Учитывая эту идею, мы можем подумать о том, чтобы иметь несколько копий для файлов Docker, создающих определенные вещи, но им всем нужен доступ к start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

Учитывая эту структуру и файлы выше, вот docker-compose.yml

докер-compose.yaml

  • В этом примере ваш sharedконтекст dir - это runtimedir.
    • Та же ментальная модель здесь, думаю, что все файлы в этом каталоге перенесены в так называемые context.
    • Точно так же просто укажите файл Docker, который вы хотите скопировать в тот же каталог. Вы можете указать это, используя dockerfile.
  • каталог, в котором находится ваш основной контент, является фактическим контекстом, который нужно установить.

docker-compose.ymlВыглядит следующим образом

version: "3.3"
services:

  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
  • all-serviceустанавливается в качестве контекста, общий файл start.shкопируется туда, а также Dockerfile, указанный каждым dockerfile.
  • Каждый будет построен по-своему, поделившись стартовым файлом!

Ура!

Марчелло де Сэйлс
источник
1
Ваша точка зрения на Dockerfile не является полностью верной, как указано в принятом ответе, если вы находитесь в иерархии папок a/b/c, то да, вход docker build .в систему cне позволит вам получить доступ ../file-in-b. Но я думаю, что общее недоразумение в этом (или, по крайней мере, в моем) было то, что контекст определяется местоположением, указанным в первом аргументе команды сборки, а не местоположением файла Dockerfile. Итак, как указано в принятом ответе: from a: docker build -f a/b/c/Dockerfile . означает, что в Dockerfile .теперь находится папкаa
β.εηοιτ.βε
1
Цитирование из документов Dockerfile: пути к файлам и каталогам будут интерпретироваться как относящиеся к источнику контекста сборки.
Нишант Джордж Агвал
18

Если вы прочитаете обсуждение в выпуске 2745, не только Docker может никогда не поддерживать символические ссылки, но и может не поддерживать добавление файлов вне вашего контекста. Похоже, это философия дизайна, согласно которой файлы, которые входят в сборку Docker, должны быть явным образом частью его контекста или должны быть из URL, где он, по-видимому, также развернут с фиксированной версией, чтобы сборка повторялась с хорошо известными URL или файлами, поставляемыми с Докер контейнер.

Я предпочитаю собирать из источника с управлением версиями - то есть сборка докера -t вещи http://my.git.org/repo - в противном случае я собираю из некоторого случайного места со случайными файлами.

принципиально, нет .... - SvenDowideit, Docker Inc

Просто мое мнение, но я думаю, что вы должны реструктурировать, чтобы отделить код и Docker репозитории. Таким образом, контейнеры могут быть универсальными и загружать любую версию кода во время выполнения, а не во время сборки.

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

Усман Исмаил
источник
Зачем вообще использовать докер?
Ищуглин
11

Я считаю, что более простой обходной путь - изменить сам «контекст».

Так, например, вместо того, чтобы давать:

docker build -t hello-demo-app .

который устанавливает текущий каталог в качестве контекста, скажем, вы хотите, чтобы родительский каталог был контекстом, просто используйте:

docker build -t hello-demo-app ..
Аншуман Манрал
источник
6
Я думаю, что это ломает .dockerignore: - \
NullVoxPopuli
Я отказался от .dockerignore и вместо этого сделал управляемую папку док-станции Makefile, которая содержит только файлы, необходимые для контекста сборки ... Мне нужно только позвонить, make buildи он вытягивает все файлы, необходимые, если они были обновлены, а затем вызывает соответствующую сборку докера ... Мне нужно проделать дополнительную работу, но она работает без нареканий, потому что я полностью контролирую ситуацию.
Сахсахэ
5

Вы также можете создать архив того, что нужно изображению, и использовать его в качестве контекста.

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts

Лоран Пикет
источник
Отличный совет! Я обнаружил , вы даже можете кормить Docker строить тарболл как контекст на стандартный ввод: tar zc /dir1 /dir2 |docker build -. Это было очень полезно в моем случае.
Торе Олсен
3

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

Вам не нужно использовать docker-compose для этого, но это немного облегчает жизнь

# docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
# setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
Крис Ривера
источник
1

У меня была такая же проблема с проектом и некоторыми файлами данных, которые я не смог переместить в контекст репо по причинам HIPPA. В итоге я использовал 2 Dockerfiles. Один создает основное приложение без необходимых мне материалов вне контейнера и публикует их во внутреннем репо. Затем второй файл Docker извлекает это изображение, добавляет данные и создает новое изображение, которое затем развертывается и нигде не сохраняется. Не идеально, но это помогло мне сохранить конфиденциальную информацию в хранилище.

Аннулет Консалтинг
источник
1

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

пример:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name

для получения дополнительной информации см .: https://docs.docker.com/storage/volumes/

Бен Векс
источник
Обратите внимание, что это работает, только если том является зависимостью времени выполнения. Для зависимости времени сборки, docker runслишком поздно.
user3735633
1

Как описано в этом выпуске GitHub, сборка фактически происходит /tmp/docker-12345, поэтому относительный путь like ../relative-add/some-fileявляется относительным /tmp/docker-12345. Таким образом, он будет искать /tmp/relative-add/some-file, что также показано в сообщении об ошибке. *

Не разрешается включать файлы из-за пределов сборочного каталога, поэтому это приводит к сообщению «Запрещенный путь». "

Rocksn17
источник
0

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

./Code/Repo1
./Code/Repo2
...

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

Примером ситуации может быть то, что другая команда поддерживает схему базы данных, Repo1и код вашей команды Repo2зависит от этого. Вы хотите связать эту зависимость с некоторыми вашими исходными данными, не беспокоясь об изменениях схемы или загрязняя хранилище другой группы (в зависимости от того, какие изменения вам, возможно, все же придется изменить в своих сценариях исходных данных) Конечно, второй подход - хакерский, но обходит проблему длинных сборок:

Создайте скрипт sh (или ps1), ./Code/Repo2чтобы скопировать нужные вам файлы и вызвать нужные вам команды docker, например:

#!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build

В файле docker-compose просто установите контекст как Repo2root и используйте содержимое ./db/schemaкаталога в вашем файле docker, не беспокоясь о пути. Имейте в виду, что вы рискуете случайно передать этот каталог в систему контроля версий, но действия по очистке сценариев должны быть достаточно простыми.

user1007074
источник
0

В моем случае мой Dockerfile написан как шаблон, содержащий заполнители, которые я заменяю реальными значениями, используя мой файл конфигурации.

Так что я не мог указать этот файл напрямую, но направить его в сборку Docker следующим образом:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

Но из-за трубы COPYкоманда не сработала. Но вышеуказанный способ решает это путем -f -(явно говоря, файл не предоставлен). Делая только -без -fфлага, контекст И Dockerfile не предоставляются, что является предупреждением.

Даниэль Кац
источник
0

Хитрость заключается в том, чтобы понять, что вы можете указать контекст в команде сборки, чтобы включить файлы из родительского каталога, если вы укажете путь Docker, я бы изменил свой Dockerfile, чтобы он выглядел так:

...
COPY ./ /dest/
...

Тогда моя команда сборки может выглядеть так:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

Из каталога проекта

docker built -t username/project[:tag] -f ./docker/Dockerfile .

Из проекта / докера

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
daniekpo
источник