Gymterview
junior

Зачем использовать минимальные базовые образы и какие варианты существуют?

Минимальный базовый образ – это образ контейнера, содержащий только компоненты, необходимые для запуска приложения, что сокращает поверхность атаки (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 как продвинутый приём – это выделит вас среди кандидатов.