Ответ
Сокращение размера Docker-образа ускоряет сборку, развертывание и повышает безопасность. Основные методы:
-
Использование минимальных базовых образов:
- Вместо
ubuntu:latestилиnode:latestиспользуйтеalpine-варианты (node:alpine) или дистрибутивы типаscratch. - Почему: Alpine Linux основан на musl libc и BusyBox, что значительно уменьшает размер.
- Вместо
-
Многоэтапная сборка (Multi-stage builds):
- Отделите этап сборки приложения от этапа создания финального образа.
# Этап сборки FROM golang:alpine AS builder WORKDIR /app COPY . . RUN go build -o myapp .
Финальный этап
FROM alpine:latest WORKDIR /root/ COPY --from=builder /app/myapp . CMD ["./myapp"]
* **Почему:** В финальный образ попадают только скомпилированный бинарник и его минимальные зависимости, без компилятора, исходного кода и промежуточных артефактов. - Отделите этап сборки приложения от этапа создания финального образа.
-
Оптимизация Dockerfile-инструкций:
- Объединяйте команды RUN в одну цепочку с
&&и очищайте кэш пакетного менеджера в том же слое.# Плохо: создает несколько слоев RUN apt-get update RUN apt-get install -y package RUN rm -rf /var/lib/apt/lists/*
Хорошо: один слой, меньше места
RUN apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/*
* **Используйте `.dockerignore`**, чтобы не копировать в контекст сборки ненужные файлы (`.git`, `node_modules`, логи, временные файлы). - Объединяйте команды RUN в одну цепочку с
-
Уменьшение количества слоев:
- Копируйте все необходимые файлы одной инструкцией
COPY, если это не мешает кэшированию. - Почему: Каждая инструкция в Dockerfile создает новый слой. Меньше слоев — меньше накладных расходов.
- Копируйте все необходимые файлы одной инструкцией
-
Динамическое связывание библиотек:
- При компиляции из исходного кода (C/C++, Go) используйте флаги для статической линковки только там, где это необходимо, чтобы не тащить все библиотеки в образ.
Практический результат: Применение этих методов может уменьшить образ с ~1.5 ГБ до ~150 МБ и менее.
Ответ 18+ 🔞
А, слушай, вот это реально больная тема, про которую все орут, а толком не объясняют, зачем это надо. Ну представь: у тебя образ, как твой старый комп, забитый всяким цифровым хламом — 1.5 гига. Каждый раз его на сервер таскать — ебать колотить, интернет плачет, место на диске кончается, да и злоумышленнику внутри такого сарая раздолье — спрятаться есть где. Наша задача — превратить этот сарай в стерильную, голую будку. Вот как это делается.
Первое и главное — не бери в базу жирную свинью.
Вместо того чтобы тянуть ubuntu:latest (который, блядь, как чемодан без ручки), смотри в сторону alpine. Это такой линукс-микроскоп, на котором можно запустить почти всё, но весит он, как перышко. Нужен Node.js? Бери node:alpine. Go? golang:alpine. Сразу минус гигабайт схуяля. Если совсем упороться — есть образ scratch, вообще пустой. Но это для сильных духом, когда твоё приложение — один статический бинарник.
Второе — волшебная многоэтапная сборка. Это просто гениальная вещь, ёпта.
Смысл в чём: ты на первом этаже (в одном образе) устраиваешь бардак — компилируешь, скачиваешь зависимости, пачкаешь всё. А потом из этого бардака забираешь только готовый, чистый артефакт (бинарник, jar-ник, что угодно) и кладёшь его в новый, девственно чистый образ. Всё остальное — компиляторы, исходники, временные файлы — остаются за бортом. Как будто ты построил машину в гараже, а потом выкатил из гаража только готовый автомобиль, а все верстаки, обрезки металла и пустые банки из-под пива — нахуй, в утиль.
Смотри, как это выглядит на практике:
# Этап-свинарник, где всё компилируется
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# Финальный этап — стерильная палата
FROM alpine:latest
WORKDIR /root/
# Копируем ТОЛЬКО бинарник из предыдущего этапа. Ничего лишнего!
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Видишь? В итоговом образе только алпайн и твоя программа. Ни Go, ни исходников, ни пакетов для сборки. Красота.
Третье — не будь распиздяем в Dockerfile.
Каждая команда RUN, COPY, ADD создаёт новый слой, который навсегда остаётся в истории. Если пишех хуйню вроде:
RUN apt-get update
RUN apt-get install -y какой-то-пакет
RUN rm -rf /var/lib/apt/lists/*
Ты получаешь три слоя, и удаление кэша в последнем — это пиздец какой обман, потому что предыдущие два слоя с данными уже намертво запечатаны. Они просто помечены как удалённые, но место-то едят! Правильно — делать всё в одной команде, чтобы мусор не успел осесть в отдельном слое:
RUN apt-get update && apt-get install -y какой-то-пакет
&& rm -rf /var/lib/apt/lists/*
Один слой, в конце уборка. Идеально.
Четвёртое — файл .dockerignore, блядь, создай!
Это же просто пиздец, как многие его игнорят. Ты же не хочешь, чтобы в образ попали твои локальные node_modules, .git-папка весом в гигабайт, логи, кэши IDE или, не дай бог, файл .env с паролями? Так перечисли в .dockerignore всё, что не нужно, и Docker при копировании проигнорирует эту хуйню. Контекст сборки станет меньше, и сама сборка ускорится.
Итог: если не халявить, то образ в 1.5 ГБ легко превращается в 100-150 МБ. А то и меньше. Это быстрее качается, быстрее запускается, безопаснее и вообще — приятно глазу. Не будь свиньёй, оптимизируй.