Введение: почему Docker важен для Java-разработчика?
Современная Java-разработка редко обходится без контейнеризации. Docker позволяет упаковать приложение вместе со всеми зависимостями, JVM и конфигурацией в изолированный контейнер. Это решает проблему «работает на моей машине» и упрощает CI/CD. В этой статье мы разберем полный цикл: от установки Docker до оптимизации образов для продакшена.
1. Основы Docker для Java
1.1 Что такое Docker и зачем он Java-проектам?
Docker — это платформа для создания, доставки и запуска приложений в контейнерах. Для Java это особенно актуально:
- Изоляция версий JVM — можно использовать Java 11 для одного сервиса и Java 17 для другого на одном хосте.
- Воспроизводимость — образ содержит точную версию JDK, библиотек и системных утилит.
- Масштабирование — легко запускать несколько экземпляров через Kubernetes или Docker Swarm.
1.2 Установка Docker
Для начала установите Docker Desktop (Windows/Mac) или Docker Engine (Linux). Проверьте установку:
docker --version
docker info
2. Создание Dockerfile для Java-приложения
Рассмотрим простое Spring Boot приложение. Создадим файл Dockerfile в корне проекта.
2.1 Базовый Dockerfile
# Используем официальный образ OpenJDK
FROM openjdk:17-jdk-alpine
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем JAR-файл
COPY target/myapp.jar app.jar
# Открываем порт
EXPOSE 8080
# Команда запуска
ENTRYPOINT ["java", "-jar", "app.jar"]
2.2 Сборка и запуск контейнера
# Сборка образа
docker build -t my-java-app .
# Запуск контейнера
docker run -p 8080:8080 my-java-app
После запуска приложение будет доступно по адресу http://localhost:8080.
3. Оптимизация Docker-образов для Java
Большие образы замедляют развертывание. Рассмотрим ключевые техники оптимизации.
3.1 Многоступенчатая сборка (Multi-stage builds)
Позволяет разделить этап сборки и этап выполнения. В итоговый образ попадает только JAR, без Maven/Gradle и исходников.
# Этап сборки
FROM maven:3.8.5-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# Этап выполнения
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=build /app/target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
3.2 Оптимизация слоев
Docker кэширует слои. Разделите копирование данных: сначала зависимости (меняются редко), потом код (меняется часто).
FROM maven:3.8.5-openjdk-17 AS build
WORKDIR /app
# Копируем только POM и скачиваем зависимости
COPY pom.xml .
RUN mvn dependency:go-offline
# Копируем исходники и собираем
COPY src ./src
RUN mvn package -DskipTests
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
3.3 Использование jlink для создания минимальной JRE
Если вы используете Java 9+, можно создать кастомную JRE только с нужными модулями.
FROM openjdk:17-slim AS jre-build
RUN jlink --add-modules java.base,java.logging,java.sql \
--output /jre \
--strip-debug \
--no-man-pages \
--no-header-files
FROM alpine:latest
COPY --from=jre-build /jre /jre
COPY target/app.jar /app.jar
ENTRYPOINT ["/jre/bin/java", "-jar", "/app.jar"]
Такой образ может весить менее 50 МБ вместо 300+ МБ.
4. Docker Compose для Java-микросервисов
Если у вас несколько сервисов (например, Spring Boot + PostgreSQL), используйте docker-compose.yml.
4.1 Пример docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mydb
- SPRING_DATASOURCE_USERNAME=user
- SPRING_DATASOURCE_PASSWORD=pass
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
4.2 Запуск всех сервисов
docker-compose up --build
Это автоматически соберет образ Java-приложения, запустит PostgreSQL и свяжет их через внутреннюю сеть Docker.
5. Best practices для Java-контейнеров
- Не используйте root-пользователя — добавьте непривилегированного пользователя в Dockerfile:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
- Настройте JVM для контейнера — используйте флаги
-XX:+UseContainerSupportи-XX:MaxRAMPercentage=75.0(Java 10+). - Добавьте healthcheck:
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
- Используйте .dockerignore — исключите
target/,.git/,node_modules/.
Заключение
Контейнеризация Java-приложений с Docker — это не просто тренд, а необходимость для современной разработки. Мы рассмотрели