1. Обзор
В общем, команда сборки
Docker ограничивает источники файлов, которые мы можем использовать в наших образах Docker. Мы указываем контекст сборки, который является корнем, из которого должны быть найдены как Dockerfile
, так и все его зависимые файлы.
Однако иногда нам может понадобиться использовать Dockerfile
из одной части нашей файловой системы с файлами из другой.
В этом кратком руководстве мы рассмотрим несколько способов преодоления этого ограничения, а также их плюсы и минусы.
2. Сборка Docker и ее контекст
2.1. Обычная сборка Docker
Начнем с примера — простого приложения nginx
, которое имеет один HTML-файл и Dockerfile
. Структура каталогов:
projects
├ <some other projects>...
└── sample-site
├── html
│ └── index.html
└── Dockerfile
Также у нас есть небольшой Dockerfile
:
FROM nginx:latest
COPY html/* /etc/nginx/html/
Теперь, чтобы создать образ для этого приложения, запустим:
$ docker build -t sample-site:latest .
Здесь контекст сборки устанавливается в текущий каталог через «.» аргумент.
Обычной практикой является хранение файла Dockerfile
в корневом каталоге проекта. Команда по умолчанию ожидает, что Dockerfile
присутствует там. Все файлы, которые мы хотим включить в образ, должны существовать где-то внутри этого контекста.
2.2. Более сложные сценарии сборки
Иногда традиционный подход может не работать для нас.
Например, у нас могут быть разные файлы Docker
или docker-compose
в зависимости от среды. Или мы можем добавить наши контейнеры как отдельное действие от нашей разработки.
В таких ситуациях имеет смысл переместить файлы, связанные с Docker, в отдельный каталог. Точно так же в некоторых случаях мы можем хранить файлы конфигурации для наших изображений за пределами корневого каталога нашего проекта.
К сожалению, Docker не позволяет нам добавлять файлы из произвольных частей файловой системы, поскольку это может открыть брешь в безопасности. Однако есть несколько обходных путей:
- Создавайте с более широким контекстом, чтобы включить все необходимое
- Создайте базовый образ с файлами, находящимися вне контекста, а затем расширьте базовый образ.
- Скопируйте все необходимые файлы, чтобы создать временный контекст и построить образ из него.
Давайте проверим их один за другим.
3. Создавайте с большим контекстом
Предположим, мы хотим переместить Dockerfile
в отдельный каталог с именем docker
. Мы также хотим переопределить стандартную конфигурацию nginx
с помощью пользовательского файла конфигурации, который находится в каталоге конфигурации за пределами корневого
примера сайта
проекта . Новая структура каталогов:
projects
├ <some other projects>...
├── sample-site
│ ├── html
│ │ └── index.html
│ └── docker
│ └── Dockerfile
└── config
└── nginx.conf
В этом случае ни прежний Dockerfile
, ни команда docker build
больше не работают. Чтобы заставить его снова работать, мы должны создать его с большим контекстом — каталогом проектов
.
3.1. Изменить Dockerfile
Теперь, когда наш контекст изменился, нам нужно изменить наш Dockerfile
:
FROM nginx:latest
COPY sample-site/html/* /etc/nginx/html/
COPY config/nginx.conf /etc/nginx/nginx.conf
Мы изменили путь к каталогу html
относительно нового контекста. Мы также включили файл nginx.conf
из его местоположения.
3.2. Создайте образ
Теперь давайте перейдем в каталог проектов
и запустим команду для сборки образа:
$ cd projects
$ docker build -f sample-site/docker/Dockerfile -t sample-site:latest .
Здесь снова мы используем «.» в качестве контекста, поскольку мы запускаем команду из каталога проектов .
Это помещает и Dockerfile,
и nginx.conf
в текущий контекст сборки. Поскольку файл Dockerfile
не находится в корне каталога контекста, мы указываем его путь с помощью параметра -f
.
Проблема с этим подходом заключается в том, что клиент Docker отправляет копию контекста сборки — весь каталог проектов
— демону Docker. Каталог может содержать много других несвязанных файлов и каталогов. Таким образом, это может потребовать от Docker сканирования большого количества ресурсов, что может замедлить процесс сборки.
4. Создайте базовое изображение с внешними файлами
Другой подход заключается в создании базового образа с внешними файлами и последующем его расширении . Мы будем повторно использовать ту же структуру, что и в предыдущем примере:
projects
├ <some other projects>...
├── sample-site
│ ├── html
│ │ └── index.html
│ └── docker
│ └── Dockerfile
└── config
└── nginx.conf
4.1. Напишите Dockerfile
для базы
Для начала напишем Dockerfile
с конфигом:
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
Мы помещаем файл в каталог проектов/config
.
4.2. Построить базу
Следующим шагом будет запуск команды сборки в файле tools/config
для создания базового образа:
$ docker build -t sample-site-base:latest .
Теперь у нас есть образ Docker с именем
sample-site-base:latest
, содержащий сервер nginx
и файлы конфигурации.
4.3. Напишите Dockerfile
для ребенка
Далее давайте напишем Dockerfile
сайта- образца
для расширения базы-образца-сайта
:
FROM sample-site-base:latest
COPY html/* /etc/nginx/html/
4.4. Построить ребенка
Наконец, давайте запустим команду в проектах/sample-site
, чтобы создать образ нашего приложения:
$ docker build -f docker/Dockerfile -t sample-site:latest .
Здесь мы разделили сборку Docker на два отдельных этапа, каждый из которых относится к другому дереву каталогов в нашем проекте.
Этот подход повторно использует общую часть файла Dockerfile
в его дочерних образах. Эта структура относительно проста в обслуживании. Если в базовом образе есть какие-либо изменения, нам нужно перестроить дочерние образы, и одно и то же изменение отразится на всех них.
Следует отметить, что такой подход увеличивает количество слоев в нашем окончательном образе Docker .
5. Создайте временный контекст
Наш последний вариант — создать специальный временный контекст для создания образа . Это использует некоторые сценарии вокруг нашей команды сборки docker
, чтобы поместить необходимые файлы в удобную для Docker структуру каталогов.
5.1. Создать временный каталог
Во-первых, давайте создадим временный каталог и скопируем все необходимые ресурсы:
$ mkdir tmp-context
$ cp -R ../html tmp-context/
$ cp -R ../../config tmp-context/
Это будет наш контекст сборки.
5.2. Создайте Dockerfile
Теперь давайте напишем Dockerfile
относительно этого контекста:
FROM nginx:latest
COPY html/* /etc/nginx/html/
COPY config/nginx.conf /etc/nginx/nginx.conf
Мы уже поместили необходимые файлы в tmp-context
, поэтому нам не нужно упоминать здесь какие-либо внешние пути.
5.3. Создайте образ
Запустим команду для сборки образа:
$ cd tmp-context
$ docker build -t sample-site:latest .
Эта команда использует tmp-context
в качестве контекста сборки. Он находит внутри каталога все, что ему нужно, и, таким образом, без проблем создает образ.
5.4. Очистить
Наконец, давайте очистим временный каталог:
$ rm -rf tmp-context
Это самый простой способ добавить файлы из любого места в образ Docker. Здесь у нас есть полный контроль над тем, как мы создаем наш контекст. Мы можем создать сценарий bash со всеми вышеперечисленными командами, чтобы упростить весь процесс.
Однако мы должны понимать, что некоторые файлы могут быть большими. Их копирование в контекст может занять много времени, что будет происходить при каждой сборке.
6. Заключение
В этой статье мы увидели, как Docker обычно рассчитывает собирать образы из файлов в том же каталоге, что и Dockerfile
. Мы рассмотрели некоторые решения для добавления файлов вне обычного контекста сборки.
Мы также изучили преимущества и недостатки каждого решения.
Как всегда, пример кода, связанный с этой статьей, доступен на GitHub .