Как использовать Docker Multistage для создания крошечных образов (TS и Golang)

Вы когда-нибудь задумывались, как можно удалить лишние зависимости сборки из вашего образа docker?

Знаете ли вы, что ваш образ можно легко оптимизировать?

В этой статье мы не так много говорим о языке, и чтение всех примеров может быть полезным.

Пример TS

Например, возможно, вы всегда использовали такой Dockerfile для сборки и запуска вашего Typescript проекта:

FROM node:18-alpine3.15
WORKDIR /usr/app
COPY package.json .
RUN npm install --include=dev
COPY tsconfig.json .
COPY src src
RUN ["npm", "run", "build"]
CMD ["npm", "run", "start"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Просто, но его можно уменьшить, не потеряв ничего важного,
Потому что, в строке

RUN ["npm", "run", "build"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы конвертируем некоторые TS-коды в JS-коды с помощью некоторых инструментов (dev dependencies).
Что произойдет, если мы удалим эти инструменты после преобразования? Ничего!

Посмотрите на картинку ниже, мы построили наше изображение в три этапа, вместо одного, и сэкономили 35% места!

С помощью кода ниже:

FROM node:18-alpine3.15 as ts-compiler
WORKDIR /usr/app
COPY package*.json ./
COPY tsconfig.json ./
RUN npm install --include=dev
COPY src src
# Build the project (TS to JS conversion)
RUN ["npm", "run", "build"]

FROM node:18-alpine3.15 as ts-remover
WORKDIR /usr/app
# We need package.json again
COPY --from=ts-compiler /usr/app/package*.json ./
# Move built codes from last stage here
COPY --from=ts-compiler /usr/app/build ./
# We don't need dev dependencies anymore
RUN npm install --omit=dev

# Using google optimized containers can make it even smaller
FROM gcr.io/distroless/nodejs:18
WORKDIR /usr/app
COPY --from=ts-remover /usr/app ./
USER 1000
CMD ["index.js"]
Войти в полноэкранный режим Выйти из полноэкранного режима

Будьте осторожны с переменными окружения

Представьте, что вы используете специальный пакет, например, Puppeteer.
При установке Puppeteer загружает браузер chrome, но вы можете отключить его, если хотите, задав ENV.
Взгляните на изменение, которое мы сделали на первом этапе последнего кода:

FROM node:18-alpine3.15 as ts-compiler
WORKDIR /usr/app
COPY package.json .

# This line is added 👇🏻
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

RUN npm install --include=dev
COPY tsconfig.json .
COPY src src
RUN ["npm", "run", "build"]
Вход в полноэкранный режим Выход из полноэкранного режима

Если вы снова запустите многоэтапную сборку, вы увидите огромный прирост 😳.

Что происходит?
Дело в том, что вы должны использовать команды ENV и ARG для каждого этапа, мы устанавливаем зависимости дважды за два этапа, поэтому мы должны пропустить установку хрома дважды!
Ничто не говорит больше, чем кусок кода 😄:

FROM node:18-alpine3.15 as ts-compiler
WORKDIR /usr/app
COPY package.json .

# Define ENV once here
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
RUN npm install --include=dev
COPY tsconfig.json .
COPY src src
RUN ["npm", "run", "build"]

FROM node:18-alpine3.15 as ts-remover
WORKDIR /usr/app
COPY --from=ts-compiler /usr/app/package.json .
COPY --from=ts-compiler /usr/app/build .

# Define ENV once again!
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
RUN npm install --omit=dev

# We don't need that ENV in this stage, 
# because we're not installing anything more.
FROM gcr.io/distroless/nodejs:18
WORKDIR /usr/app
COPY --from=ts-remover /usr/app ./
USER 1000
CMD ["index.js"]
Вход в полноэкранный режим Выход из полноэкранного режима

Пример Go

Контейнеры Go (и любого другого компилируемого языка) могут быть оптимизированы еще больше. (гораздо больше 😄)
Поскольку время выполнения привязано к ним, вам не нужно устанавливать что-то вроде Nodejs, и вы можете отказаться от самого компилятора на втором этапе и просто использовать бинарный исполняемый файл.

FROM golang:1.18-alpine3.15 AS builder
WORKDIR /app
# Copy go.mod and go.sum first, because of caching reasons.
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
# Compile project
RUN CGO_ENABLED=0 GOOS=linux go build -a -o main .

# Use another clean image without Golang compiler.
FROM alpine:3.15 AS production
COPY --from=builder /app/main .
CMD ["./main"]
Вход в полноэкранный режим Выход из полноэкранного режима

Все верно! Мы сэкономили около 96% пространства!
Первый результат — без использования многоступенчатой техники,
Второй — при использовании alpine-3.15 в качестве production.
Третий — с использованием gcr.io/distroless/static-debian11, который является самым маленьким образом из всех существующих.

Включение пользовательского интерфейса

Вы даже можете добавить пользовательский интерфейс в ваш Dockerfile на другом этапе!
В данном случае у меня есть проект Svelte SPA рядом с моим проектом (в папке ui), и я обслуживаю его из Go Backend.

FROM golang:1.18-alpine3.15 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -o main .

# This stage convert svelte project to vanilla HTML, CSS, JS.
FROM node:18-alpine3.15 AS frontend
WORKDIR /ui
COPY ui .
RUN npm install
RUN npm run build

FROM alpine:3.15 AS production
COPY --from=builder /app/main .
COPY --from=frontend /ui/public /ui
CMD ["./main"]
Вход в полноэкранный режим Выход из полноэкранного режима

Этот код является частью проекта Blogo.
https://github.com/arshamalh/blogo

Надеюсь, вы узнали что-то новое от меня. 😃✌🏻
Любые предложения или исправления приветствуются.

Оцените статью
devanswers.ru
Добавить комментарий