Изменения будут особенно полезны тем, кто собирает образы (images) на базе уже существующих и кому необходимо поддерживать их минимальный размер.
Каждый, кто собирал docker images знает, что практически каждая инструкция в Dockerfile добавляет отдельный слой и вам необходимо очистить этот слой от всех лишних артефактов, перед тем как добавить новый слой. Поэтому чтобы создать действительно эффективный Dockerfile раньше вам традиционно предлагали использовать скрипты и другую логику, чтобы поддерживать минимально возможный размер слоя. Обычной практикой было использовалось несколько Dockerfile в зависимости от целей сборки образа — один файл для DEVELOPMENT с определенным набором средства для отладки, профайлинга и всего остального и другой образ, гораздо меньшего размера для развертывания приложение на STAGING или PRODUCTION, с набором компонентов, необходимых для работы приложения.
Допустим у нас есть простой “hello world” HTTP-server, который нужно собраться и запустить тесты, а после собрать минимальные docker образ, которые содержит только исполняемые файлы.
Пример можно взять отсюда
Минимальный Dockerfile у нас будет выглядеть вот так.
Dockerfile:
FROM golang:latest
COPY . .
RUN go test && go build ./src/main.go
Давайте соберем и запустим образ:
docker image build -t hello_world:build .
Если посмотреть метаданные образа: docker image inspect hello_world:build то видно, что он состоит из 6 отдельный слоев и занимает около 800MB. И это только Hello World, а какой размер может быть у реального приложение можно только представить. Поэтому для PRODUCTION уже имеет смысл собрать образ только из исполняемых файлов.
В результате вы должны запустить вот такую последовательность команд:
Запустить базовый контейнер:
docker container run -it --name hello_world hello_world:build
Создать новый контейнер на базе уже существующего и скопировать бинарные файлы:
docker create --name extract hello_world:build
mkdir ./Production/
docker cp extract:/go/main ./Production/main
docker rm -f extract
docker rm -f hello_world
Создать PRODUCTION контейнер, содержащий только необходимые файлы для работы приложения:
docker build --no-cache -t hello_world:latest ./Production/
rm ./Production
Процесс может отличаться в зависимости от ваших требований, но в целом он будет так или иначе включать подобные шаги.
Так вот много-этапные (multi-stage builds) сборки позволяют значительно упростить этот процесс и описать его внутри Dockerfile. Каждая инструкция FROM может использовать индивидуальный базовый образ и каждая из них начинает новую стадию сборки docker образа. Но основное преимущество, что вы можете копировать необходимые артефакты из одной стадии в другую. В результате все вышеперечисленные шаги могут быть описаны вот так
Dockerfile:
FROM golang:latest as build
COPY . .
RUN go build ./src/main.go
FROM alpine:latest as production
COPY --from=build /go/main .
CMD ["./main"]
И все что вам остается, это выполнить команду:
docker image build -t hello_world:latest .
Note: отдельно стоит добавить, что к предыдущием стадия вы можете обращаться как по алиасу указанному в инструкции FROM golang:latest as build — как в примере выше COPY --from=build /go/main ., так и по индексу COPY --from=0 /go/main .
Ссылки:
Комментариев нет:
Отправить комментарий