В прошлом я уже говорил о создании миниатюрных Docker-образов, но теперь, когда у Docker появилась многоступенчатая сборка, пришла пора вернуться к этому вопросу. Раньше нам приходилось создавать бинарный файл на одном шаге и собирать docker-образ на другом. Это было немного неудобно и требовало некоторых ухищрений. Давайте посмотрим, как многоступенчатые сборки улучшили ситуацию.
Замечание. Для этого нужен Docker 17.05 или более поздний.
Начнем с простой программы на Go:
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
Соберем ее с помощью образа golang:alpine одноступенчатымспособом (single-stage build). Вот Dockerfile:
FROM golang:alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp
ENTRYPOINT ./goapp
Теперь соберем образ и запустим контейнер:
docker build -t treeder/hello .
docker run --rm treeder/hello
Все работает, но давайте посмотрим на размер с помощью docker images | grep treeder/hello
.
258 МБ — многовато для крошечного бинарного файла. Теперь давайте попробуем многоступенчатуюсборку (multi-stage build) с использованием нового Dockerfile:
# стадия сборки
FROM golang:alpine AS build-env
ADD . /src
RUN cd /src && go build -o goapp
# финальная стадия
FROM alpine
WORKDIR /app
COPY --from=build-env /src/goapp /app/
ENTRYPOINT ./goapp
Соберем и запустим снова:
docker build -t treeder/hello .
docker run --rm treeder/hello
Проверим размер:
6,35 МБ — намного лучше.
Что это означает? Что многоступенчатые сборки — отличная вещь. Ими стоит пользоваться практически в любом случае!