Многоступенчатая сборка Docker-образов

В прошлом я уже говорил о создании миниатюрных 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 МБ — намного лучше.

Что это означает? Что многоступенчатые сборки — отличная вещь. Ими стоит пользоваться практически в любом случае!