Образы и слои
Что это такое
Образ собирается выполнением последовательности инструкций, каждая из которых создаёт слой файловой системы. Слои адресуются по содержимому и разделяются между образами: два образа с одинаковым базовым слоем хранят его один раз. Понимание слоёв объясняет почти всё остальное в Docker — почему порядок сборки меняет время сборки, почему образы неожиданно растут и почему один и тот же образ мал на машине одной команды и огромен на машине другой.
Почему это важно
Образ — не только способ поставки, но и ключ кэша и поверхность безопасности. Каждый добавленный слой — это данные, которые нужно хранить, передавать и проверять сканером, а каждый оставленный в итоговом образе пакет — код, у которого может быть уязвимость. Образы, растущие без предела, медленно отправляются и медленно запускаются, а разница между хорошо собранным образом и небрежным обычно состоит из нескольких решений о порядке инструкций, а не из другого инструмента.
Ключевые идеи
Каждая инструкция создаёт слой, а слои кэшируются
Docker переиспользует слой, входные данные которого не изменились. Поскольку кэш сбрасывается с первой изменённой инструкции и дальше, порядок инструкций — это решение о производительности: сначала скопируйте манифест зависимостей и установите зависимости, потом копируйте исходный код. Тогда изменение файла исходников пересоберёт лишь последние слои вместо полной переустановки.
COPY package.json ./ <- меняется редко, кэшируется
RUN install dependencies <- меняется редко, кэшируется
COPY src ./src <- меняется часто, дёшево
Файловая система контейнера — стопка слоёв только для чтения плюс один записываемый
Запуск образа добавляет сверху тонкий записываемый слой. Записывать туда дёшево на старте и дорого на дистанции, и эти данные исчезают вместе с контейнером. Поэтому база данных внутри контейнера по умолчанию не хранится постоянно, и поэтому лечится это томом, а не контейнером, который живёт дольше.
Контекст сборки важнее инструкций
Перед началом сборки движку отправляется каталог с файлами; большой контекст замедляет каждую сборку и может утащить секреты в слои. Файл исключений, убирающий результаты сборки, локальные зависимости и метаданные системы контроля версий, держит контекст маленьким, а образ честным.
Многоэтапная сборка сохраняет образ выполнения маленьким
Этап сборки может компилировать, тестировать и собирать пакет полным инструментарием, а следующий этап копирует в минимальную основу только готовый артефакт. В результате в образе нет ни компиляторов, ни зависимостей сборки, что уменьшает и размер, и число вещей, у которых может быть уязвимость.
этап 1: полный инструментарий -> сборка и тесты
этап 2: минимальная основа -> скопировать артефакт, задать точку входа
В итоговом образе — один процесс и никаких секретов
Образ, содержащий целую операционную систему, менеджер пакетов и несколько служб, сложнее понять и сложнее защитить. Секреты никогда нельзя запекать в слой: слои читаемы для любого, кто может скачать образ, и удаление файла в следующем слое не убирает его из предыдущего.
Практика
Упорядочьте сборку ради переиспользования кэша
Напишите определение контейнера для небольшого сервиса, который устанавливает зависимости до копирования исходного кода. Измените один файл исходников, пересоберите и посмотрите, какие шаги взялись из кэша.
Готово, когда: пересборка завершается без переустановки зависимостей, и вы можете указать инструкцию, которая это нарушила бы.
Уменьшите образ многоэтапной сборкой
Возьмите сборку, которая компилирует приложение, и разделите инструментарий сборки и итоговый образ на два этапа.
Готово, когда: в итоговом образе нет ни компилятора, ни менеджера пакетов, а приложение всё равно запускается.
Частые ошибки
- Скопировать всё до установки зависимостей — каждое изменение исходников запускает полную переустановку; исправить: копируйте манифест, ставьте зависимости, затем копируйте исходники.
- Хранить данные в записываемом слое контейнера — они исчезают вместе с контейнером; исправить: подключите том или используйте управляемое хранилище.
- Запекать секреты в слой — после удаления они остаются читаемыми в истории образа; исправить: передавайте секреты во время запуска или собирайте через механизм секретов платформы.
- Отправлять инструментарий сборки в образ выполнения — образ большой и несёт уязвимости, которыми никогда не пользуется; исправить: используйте многоэтапную сборку.
- Отправлять огромный контекст сборки — сборка медленная, а лишние файлы протекают в слои; исправить: исключите результаты сборки и локальные зависимости файлом игнорирования.
Что читать дальше
- Docker (06-cloud-devops/06-docker) — модель упаковки, единицей которой являются эти слои, поэтому читайте её первой.
- Запуск контейнеров и сохранение данных (06-cloud-devops/06-docker/02-running-containers-and-persisting-data) — что происходит после запуска образа и когда записываемого слоя уже не хватает.