Як включити файли поза контекстом збірки Docker?


461

Як я можу включати файли за межами контексту збирання Docker за допомогою команди "ADD" у файлі Docker?

З документації на Докер:

Шлях повинен знаходитися всередині контексту складання; Ви не можете додати ../something/something, тому що перший крок побудови докера - це надіслати контекстну каталог (та підкаталоги) до демон-докера.

Я не хочу реструктурувати весь свій проект лише для того, щоб пристосувати Докер до цього питання. Я хочу зберігати всі мої файли Docker в одному підкаталозі.

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

Єдине, що я можу придумати, - це включити крок попереднього збирання, щоб скопіювати файли в контекст збірки Docker (і налаштувати мій контроль версій, щоб ігнорувати ці файли). Чи є для цього кращий спосіб вирішення?


94
Це має стати найгіршим у Докера. З моєї точки зору, немає такого поняття, як "проект Докера". Docker призначений для доставки проектів. Це просто інструмент. Мені не хочеться перебудовувати весь свій проект для розміщення докера, додавання .dockerignore тощо. Зрештою, хто знає, як довго триватиме Docker? Було б чудово мати розділення між кодом (тобто кутовим проектом) і будь-якими засобами для його розгортання (тобто докерів). Зрештою, насправді немає користі мати файл докер поруч із усім іншим. Його просто підключити для того, щоб створити образ :(
TigerBear

3
Так, це великий спад. Я зіткнувся з тією ж проблемою, і у мене є бінарний файл більшого розміру (вже стиснутий), який я не хочу копіювати в кожен контекст збірки Docker. Я вважаю за краще джерело його з поточного місця розташування (поза контекстом побудови Docker). І я не хочу відображати томи під час виконання, тому що я намагаюся КОПУВАТИ / ДОБАВИТИ файл під час збирання та розпаковувати та робити все, що мені потрібно, щоб певні двійкові файли були записані у зображення. Таким чином, закручування контейнерів відбувається швидко.
боб з трикотажу

Я знайшов гарну структуру і пояснюю деталі на stackoverflow.com/a/53298446/433814
Marcello de Sales

1
проблема з докерними побудовами - це складена концепція "контексту". Dockerfiles недостатньо для визначення збірки, якщо вони не розміщені під стратегічним каталогом (він же контекст), тобто "/" як крайній, тому ви можете отримати доступ до будь-якого шляху (зауважте, що це не правильно робити в розумному проекті або ... плюс це робить Docker будувати дуже повільно, оскільки докер сканує весь контекст на початку). Ви можете розглянути можливість створення зображення докера з усіма необхідними файлами та використовуватись FROMдля продовження звідти. Я б не змінював структуру проекту для розміщення Docker (або будь-яких інструментів побудови).
Девіс Л.

Відповіді:


412

Найкращий спосіб подолати це - вказати 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 .

8
@Ro. ви використовуєте dockerfile:властивість у build:розділі у файлі Compose docs.docker.com/compose/compose-file/#/compose-file-reference
Emerson

3
Я отримую "Докерфайл повинен знаходитись в контексті збірки" - я дуже хотів би мати один Dockerfile, який може знаходитися нижче поточного контексту збірки. У вашому прикладі ви маєте Dockerfile в / під поточним контекстом збірки, що, звичайно, працює.
Олександр Міллз

3
Так, я просто хочу спільний Dockerfile, який відповідає декільком підкаталогам. Усі це "побудувати контексти"
Alexander Mills

50
Чи вирішує це проблема ОП щодо пошуку 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, облікові дані користувачів тощо. Значення змінних часу збирання видно будь-якому користувачеві зображення за допомогою команди історії докера.


6
Це погана порада без величезного попередження. З документації Докера: "Попередження: Не рекомендується використовувати змінні часу збирання для передачі секретів, таких як ключі github, облікові дані користувачів тощо. Значення змінних часу збірки видимі будь-якому користувачеві зображення за допомогою команди історії докера." [1] Іншими словами, приклад, наведений у цьому прикладі, розкриває приватний ключ SSH на зображенні докера. У деяких контекстах це може бути добре. docs.docker.com/engine/reference/builder/#arg
sheldonh

3
Нарешті, для подолання цього питання безпеки можна скористатись такими методами, як сквош або багатостадійність: vsupalov.com/build-docker-image-clone-private-repo-ssh-key
Jojo

46

На Linux ви можете монтувати інші каталоги замість того, щоб їх пов'язувати

mount --bind olddir newdir

Дивіться /superuser/842642 для отримання більш детальної інформації.

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


2
Лише корінь може пов'язувати каталоги
jjcf89

28

Я добре провів час, намагаючись з’ясувати вдалий зразок і як краще пояснити, що відбувається з цією підтримкою функції. Я зрозумів, що найкращий спосіб пояснити це наступним чином ...

  • Докерфайл: бачитиме файли лише під власним відносним шляхом
  • Контекст: місце в "просторі", куди будуть скопійовані файли, якими ви хочете поділитися, та ваш Dockerfile

Отже, з урахуванням сказаного, ось приклад Dockerfile, який потребує повторного використання файлу під назвою start.sh

Докерфайл

Він ALWAYSзавантажиться зі свого відносного шляху, маючи поточний dir себе як localпосилання на вказані вами шляхи.

COPY start.sh /runtime/start.sh

Файли

Враховуючи цю ідею, ми можемо подумати про наявність декількох копій для створення Dockerfiles конкретних речей, але всі вони потребують доступу до start.sh.

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

Враховуючи цю структуру та файли, наведені вище, ось docker-compose.yml

docker-compose.yaml

  • У цьому прикладі ваш sharedконтекстний реж є runtimedir.
    • Ця ж ментальна модель тут, подумайте, що всі файли в цьому режимі переміщені до т. Зв context.
    • Аналогічно, просто вкажіть Dockerfile, який ви хочете скопіювати в той самий dir. Ви можете вказати це за допомогою 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. Отже, як зазначено у прийнятій відповіді: з a: docker build -f a/b/c/Dockerfile . означає, що в Dockerfile .тепер папкаa
β.εηοιτ.βε

1
Цитуючи з документів Dockerfile: шляхи файлів і каталогів будуть інтерпретуватися як відносні до джерела контексту збірки.
Нішант Джордж Агрвал

18

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

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

принципово, ні .... - SvenDowideit, Docker Inc

Лише моя думка, але я думаю, вам слід реструктуризувати, щоб розділити код і сховища докерів. Таким чином контейнери можуть бути універсальними та перетягувати будь-яку версію коду під час виконання, а не час збирання.

Крім того, використовуйте docker як основний артефакт розгортання коду, а потім ви кладете dockerfile у корінь сховища коду. якщо ви йдете цим маршрутом, напевно, має сенс мати батьківський контейнер докера для отримання більш загальних даних про рівень системи та дочірній контейнер для налаштування, специфічного для вашого коду.


Навіщо взагалі використовувати докер?
lscoughlin

11

Я вважаю, що більш простим рішенням було б змінити сам "контекст".

Так, наприклад, замість того, щоб давати:

docker build -t hello-demo-app .

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

docker build -t hello-demo-app ..

6
Я думаю, що це перерва .dockerignore: - \
NullVoxPopuli

Я відмовився від .dockerignore і замість цього зробив Makefile керовану докерну папку, яка містить лише файли, необхідні для контексту побудови ... Мені потрібно лише зателефонувати, make buildі вона витягує всі необхідні файли, якщо вони були оновлені, а потім він викликає відповідну збірку докера ... Мені потрібно займатися додатковою роботою, але це працює бездоганно, бо я повністю контролюю.
Sahsahae

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.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. Один створює основний додаток без потрібних мені матеріалів поза контейнером і публікує це для внутрішнього репо. Потім другий dockerfile витягує це зображення і додає дані та створює нове зображення, яке потім розгортається і ніколи не зберігається ніде. Не ідеально, але це працювало для моїх цілей, щоб утримати конфіденційну інформацію поза репо.


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занадто пізно.
користувач3735633

1

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

Не дозволяється включати файли за межами каталогу збірки, тому це призводить до повідомлення "Заборонений шлях". "


0

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

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

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

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

Створіть скрипт sh (або ps1), ./Code/Repo2щоб скопіювати потрібні файли та викликати потрібні команди докер, наприклад:

#!/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

У файлі докер-композиції просто встановіть контекст як Repo2root та використовуйте вміст ./db/schemaкаталогу у вашому dockerfile, не турбуючись про шлях. Майте на увазі, що ви ризикуєте випадково скористатися цим каталогом для контролю джерела, але сценарій чищення дій повинен бути досить простим.


0

У моєму випадку мій Dockerfile написаний як шаблон, що містить заповнювачі, які я замінюю на реальне значення, використовуючи мій файл конфігурації.

Тому я не міг вказати цей файл безпосередньо, але передайте його в докерну збірку так:

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

Але через трубу COPYкоманда не працювала. Але вищезгаданий спосіб вирішує це -f -(явно кажучи, що файл не надається). Робити тільки -без -fпрапора, контекст і Dockerfile не передбачені , який є застереженням.


0

Хитрість полягає в тому, щоб визначити, що ви можете вказати контекст у команді збірки для включення файлів з батьківського каталогу, якщо ви вкажете докерський шлях, я змінив би свій 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 ..
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.