Зачем использовать минимальные базовые образы и какие варианты существуют?
Минимальный базовый образ – это образ контейнера, содержащий только компоненты, необходимые для запуска приложения, что сокращает поверхность атаки (attack surface), уменьшает размер и количество потенциальных уязвимостей.
Аналогия: если вы переезжаете в новую квартиру, вы берёте только нужные вещи. Оставлять в образе компилятор, отладчик и пакетный менеджер – всё равно что привезти с собой набор отмычек и оставить его на видном месте.
Сравнение базовых образов для Java
| Образ | Размер | Shell | Package Manager | Уязвимости* |
|---|---|---|---|---|
eclipse-temurin:21-jdk |
~470 MB | Да | apt | Много |
eclipse-temurin:21-jre |
~270 MB | Да | apt | Средне |
eclipse-temurin:21-jre-alpine |
~90 MB | Да (ash) | apk | Мало |
gcr.io/distroless/java21-debian12 |
~230 MB | Нет | Нет | Минимум |
* – примерная оценка количества потенциальных уязвимостей
Alpine-based образы
Пример
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app target/service.jar /app/app.jar
USER app
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Преимущества: маленький размер (~90 MB), musl libc, минимум пакетов. Недостатки: возможна несовместимость с некоторыми нативными библиотеками, зависящими от glibc (например, некоторые JDBC-драйверы или библиотеки с JNI).
Google Distroless
Пример multistage build с Distroless
# Этап сборки
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY . .
RUN ./mvnw clean package -DskipTests
# Финальный образ
FROM gcr.io/distroless/java21-debian12
COPY --from=builder /build/target/service.jar /app/app.jar
WORKDIR /app
# Distroless уже работает от nonroot (UID 65532)
USER nonroot:nonroot
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Distroless-образы не содержат shell, пакетного менеджера и прочих утилит. Атакующий, даже проникнув в контейнер, не сможет выполнить sh, bash, curl, wget. Для отладки существует debug-вариант gcr.io/distroless/java21-debian12:debug с busybox shell.
Собственный минимальный образ с jlink (Java 9+)
Пример Dockerfile с кастомным JRE через jlink
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY . .
RUN ./mvnw clean package -DskipTests
# Создаём кастомный JRE только с нужными модулями
RUN jlink \
--add-modules java.base,java.sql,java.naming,java.management,java.logging \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=zip-9 \
--output /custom-jre
FROM alpine:3.20
COPY --from=builder /custom-jre /opt/java
COPY --from=builder /build/target/service.jar /app/app.jar
RUN addgroup -S app && adduser -S app -G app
USER app
ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Результат: образ ~60 MB вместо 470 MB, содержит только нужные Java-модули.
Рекомендации для production
- Production:
distrolessилиalpine+ jlink. - Staging/QA:
alpine-варианты (есть shell для отладки). - Фиксация версии: никогда не использовать тег
latest– всегда указывать версию с SHA256-дайджестом:eclipse-temurin:21-jre-alpine@sha256:abc123...
Вывод
Выбор минимального базового образа – один из самых эффективных способов уменьшить количество уязвимостей. Distroless-образы дают наилучшую защиту для production, а jlink позволяет создать ещё более компактный образ с кастомным JRE.
На собеседовании: назовите 3-4 варианта образов (jdk, jre, alpine, distroless), объясните разницу в размере и безопасности. Упомяните jlink как продвинутый приём – это выделит вас среди кандидатов.