← Назад к вопросам

Почему в рамках докеризации удобно получать jar?

2.0 Middle🔥 231 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Почему в докеризации удобно получать JAR

Краткий ответ

JAR файл в Docker удобен потому что:

  1. Fat JAR/Uber JAR - одного файла всё нужное (зависимости, код, ресурсы)
  2. Упрощённый Dockerfile - просто копируем и запускаем JAR
  3. Isolation - JVM изолирована в контейнере, не конфликтует с хостом
  4. Production-ready - Spring Boot build создаёт готовый к запуску артефакт
  5. Stateless deployment - легко масштабировать и обновлять

Что такое Fat JAR (Uber JAR)

Обычный JAR:
┌─────────────────────┐
│ my-app-1.0.jar      │
├─────────────────────┤
│ com/example/*.class │
│ application.yml     │
│ MANIFEST.MF         │
└─────────────────────┘
  └─ Требует: maven-jar-plugin
  └─ ПРОБЛЕМА: зависимости не включены!
  └─ ПРОБЛЕМА: нужно добавлять .jar'ы в classpath

Fat JAR (Uber JAR):
┌──────────────────────────────────────┐
│ my-app-1.0-SNAPSHOT.jar              │
├──────────────────────────────────────┤
│ com/example/*.class                  │ (мой код)
│ org/springframework/boot/*.class      │ (Spring Boot)
│ org/apache/commons/*.class           │ (Apache Commons)
│ com/fasterxml/jackson/*.class        │ (Jackson)
│ ... (все зависимости)                │
│ BOOT-INF/lib/*.jar                   │ (JAR зависимостей)
│ BOOT-INF/classes/*                   │ (ресурсы)
│ META-INF/MANIFEST.MF                 │ (главный класс)
└──────────────────────────────────────┘
  └─ РЕШЕНИЕ: все зависимости внутри!
  └─ ВСЁ в одном файле - готово к запуску!
  └─ Требует: maven-shade-plugin или spring-boot-maven-plugin

Почему Fat JAR идеален для Docker

1. Простой Dockerfile

# ❌ БЕЗ Fat JAR (сложно)
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y java-17-openjdk

# Копируем исходный код
COPY src /app/src
COPY pom.xml /app/

# Собираем приложение ВНУТРИ контейнера (медленно!)
RUN cd /app && mvn clean package

# Копируем все JAR зависимостей
COPY target/dependency/*.jar /app/lib/
COPY target/my-app.jar /app/

WORKDIR /app
CMD ["java", "-cp", "lib/*:my-app.jar", "com.example.Main"]

# Размер образа: 1+ GB (много слоёв, много файлов)
# ✅ С Fat JAR (просто и быстро)
FROM eclipse-temurin:17-jre-alpine

# Копируем готовый Fat JAR
COPY target/my-app-1.0.jar app.jar

# Запускаем
ENTRYPOINT ["java", "-jar", "app.jar"]

# Размер образа: 200-300 MB (компактный, быстро собирается)

2. Build процесс

# БЕЗ Fat JAR
mvn clean package

# Результат:
# target/my-app-1.0.jar (только мой код)
# target/dependency/*.jar (все зависимости отдельно)
# ~ 50+ файлов в target/

# С Docker:
# Нужно при сборке образа:
# - Копировать все 50+ файлов
# - Собирать classpath с каждым файлом
# - Это медленно и сложно


# С Fat JAR
mvn clean package

# Результат:
# target/my-app-1.0-SNAPSHOT.jar (один файл, всё внутри)
# ~ 50 MB, готов к запуску

# С Docker:
# - Копируем 1 файл
# - Простой ENTRYPOINT
# - Быстро и понятно

Как создать Fat JAR

1. Spring Boot Maven Plugin (рекомендуется)

<!-- pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0</version>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <mainClass>com.example.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>  <!-- Создаёт Fat JAR!
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

# Сборка:
mvn clean package

# Результат:
# target/my-app-1.0.jar (Fat JAR!)
# target/my-app-1.0.jar.original (исходный JAR)

2. Maven Shade Plugin (для non-Spring проектов)

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.4.1</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

3. Gradle (для Gradle-проектов)

plugins {
    id 'org.springframework.boot' version '3.0.0'
}

bootJar {
    mainClass = 'com.example.Application'
}

// Сборка:
// gradle bootJar

// Результат:
// build/libs/my-app-1.0.jar (Fat JAR)

Структура Fat JAR

my-app-1.0.jar
├── BOOT-INF/
│   ├── classes/
│   │   ├── com/example/Application.class
│   │   ├── application.yml
│   │   └── ...
│   ├── lib/
│   │   ├── spring-core-6.0.0.jar
│   │   ├── spring-web-6.0.0.jar
│   │   ├── jackson-databind-2.14.0.jar
│   │   ├── commons-lang3-3.12.0.jar
│   │   └── ... (все зависимости)
│   └── classpath.idx
├── META-INF/
│   ├── MANIFEST.MF
│   │   Main-Class: org.springframework.boot.loader.JarLauncher
│   │   Start-Class: com.example.Application
│   │   Implementation-Version: 1.0
│   └── ...
└── org/springframework/boot/loader/
    ├── JarLauncher.class (Boot loader)
    └── ... (классы для запуска)

Fat JAR в Docker: полный пример

# Dockerfile
# Stage 1: Build (сборка приложения)
FROM maven:3.9-eclipse-temurin-17 AS builder

WORKDIR /build
COPY pom.xml .
COPY src ./src

# Собираем Fat JAR
RUN mvn clean package -DskipTests

# Stage 2: Runtime (выполнение)
FROM eclipse-temurin:17-jre-alpine

# Копируем только готовый JAR из Stage 1
COPY --from=builder /build/target/my-app-*.jar app.jar

# Параметры JVM для Docker
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

# Порт
EXPOSE 8080

# Запуск
ENTRYPOINT ["java"]
CMD ["-jar", "app.jar"]
# Сборка образа
docker build -t my-app:1.0 .

# Запуск контейнера
docker run -p 8080:8080 my-app:1.0

# Проверка
curl http://localhost:8080/health

Преимущества Fat JAR в Docker

АспектПлюсы
СборкаОдин файл = быстро копируется в образ
Размер образаКомпактный (200-300 MB вместо 1+ GB)
Скорость запускаБыстрый (зависимости уже распакованы)
StatelessЛегко масштабировать горизонтально
ВоспроизводимостьОдинаковое поведение везде (dev/staging/prod)
ОбновленияПросто пересобрать образ с новым JAR
TestingМожно запустить локально: java -jar app.jar

Недостатки и как их решать

1. Размер JAR может быть большим

# Проблема: Fat JAR 100+ MB
# Решение 1: Multi-stage build (как выше)
# Решение 2: Используйте слои

docker build --target app:1.0 .

# Решение 3: Минимизируйте зависимости
# Проверьте pom.xml на лишние зависимости

2. JVM требует памяти в контейнере

FROM eclipse-temurin:17-jre-alpine

COPY target/my-app-1.0.jar app.jar

# Установите лимит памяти
ENTRYPOINT ["java", "-Xmx256m", "-Xms256m", "-jar", "app.jar"]

3. Native Image (GraalVM) для ещё меньшего размера

<!-- pom.xml для Spring Native -->
<dependency>
    <groupId>org.springframework.native</groupId>
    <artifactId>spring-native</artifactId>
    <version>0.12.1</version>
</dependency>

<!-- Сборка native image -->
mvn clean package -Pnative

# Результат: исполняемый файл (не JAR), ~100 MB образ

Сравнение подходов

┌──────────────┬─────────────┬──────────────┬──────────────┐
│ Подход       │ Размер      │ Память       │ Скорость     │
├──────────────┼─────────────┼──────────────┼──────────────┤
│ Fat JAR      │ 50-150 MB   │ 256-512 MB   │ 2-3 сек      │
│ War + Tomcat │ 200+ MB     │ 512 MB+      │ 3-5 сек      │
│ Native Image │ 80-100 MB   │ 64-128 MB    │ <500 ms      │
└──────────────┴─────────────┴──────────────┴──────────────┘

Вывод

Fat JAR идеален для Docker потому что:

  • ✅ Один файл = всё нужное внутри
  • ✅ Простой Dockerfile
  • ✅ Быстрая сборка образа
  • ✅ Компактный размер
  • ✅ Легко развёртывать и масштабировать
  • ✅ Spring Boot делает это по-умолчанию
  • ✅ Стателесс deployment = идеально для Kubernetes