sbt-assembly vs sbt-native-image: Выбор для DiceChess ботов
Цель: Объяснить, почему в некоторых репозиториях DiceChess используется
sbt-assembly, а в других —sbt-native-image, и помочь выбрать правильный подход для новых проектов.
📌 Краткое резюме
| Платформа | Рекомендуемый плагин | Тип артефакта | Причина |
|---|---|---|---|
| Google Cloud Run | sbt-assembly | Fat JAR | Простая интеграция, полная совместимость с JVM, автоматическая сборка образа Cloud Run. |
| Azure Functions | sbt-native-image | Native binary | Требуется для кастомных хендлеров (custom handlers). |
| Cloudflare Workers | — | — | Используется WebAssembly или JavaScript (не применимо для JVM-ботов). |
| Локальный запуск | sbt-assembly | Fat JAR | Простота запуска (java -jar), отладка, совместимость. |
🔍 Подробное сравнение
1. sbt-assembly (Fat JAR)
Что это?
Плагин собирает один JAR-файл, который включает:
- Ваш скомпилированный код.
- Все зависимости (включая transitive dependencies).
- Манифест с указанием главного класса (
Main-Class).
Плюсы
✅ Простота развёртывания:
- Один файл для запуска:
java -jar my-bot.jar. - Cloud Run автоматически собирает Docker-образ из JAR.
✅ Полная совместимость:
- Работает на любой JVM (OpenJDK, GraalVM, и т.д.).
- Нет проблем с библиотеками, использующими рефлексию или динамическую загрузку классов.
✅ Быстрая сборка:
- Не требует компиляции в native код (как в GraalVM Native Image).
✅ Легкая отладка:
- Можно запускать локально с отладочными флагами JVM (
-Xdebug,-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005).
✅ Поддержка всех библиотек:
- Нет ограничений на используемые зависимости.
Минусы
❌ Больший размер:
- Fat JAR может весить 10-50 МБ (в зависимости от зависимостей).
❌ Дольше запуск (JVM startup):
- Требуется время на запуск JVM и загрузку классов.
❌ Больше потребление памяти:
- JVM + heap может занимать больше памяти, чем native binary.
Пример использования
// project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.4.1")
// build.sbt
lazy val root = (project in file("."))
.settings(
// Настройки для sbt-assembly
assemblyMergeStrategy in assembly := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
)Запуск:
sbt assembly
java -jar target/scala-3.8.4/my-bot-assembly.jar2. sbt-native-image (GraalVM Native Image)
Что это?
Плагин собирает native binary (исполняемый файл) с помощью GraalVM Native Image. Результат:
- Один бинарный файл (например,
my-bot). - Не требует JVM для запуска.
Плюсы
✅ Мгновенный запуск:
- Нет задержки на запуск JVM (cold start быстрее).
✅ Меньше потребление памяти:
- Native код потребляет меньше RAM, чем JVM.
✅ Меньший размер артефакта:
- Бинарник может быть меньше, чем Fat JAR (если оптимизирован).
✅ Простота развёртывания (для некоторых платформ):
- Один бинарник для запуска (например, в Docker-контейнере).
Минусы
❌ Дольше сборка:
- Компиляция в native код занимает больше времени и ресурсов.
❌ Ограниченная совместимость:
- Не все библиотеки совместимы с GraalVM (проблемы с рефлексией, динамической загрузкой классов, JNI).
- Может потребоваться настройка
reflect-config.jsonилиresource-config.json.
❌ Сложнее отладка:
- Отладка native binary сложнее, чем JAR.
❌ Требует GraalVM:
- Нужно устанавливать GraalVM отдельно (не работает с обычной JDK).
Пример использования
// project/plugins.sbt
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.5.0")
// build.sbt
lazy val root = (project in file("."))
.enablePlugins(NativeImagePlugin)
.settings(
nativeImageInstalled := true,
nativeImageOptions ++= List("--no-fallback", "--install-exit-handlers"),
nativeImageOutput := target.value / "native-image" / "my-bot"
)Запуск:
sbt nativeImage
./target/native-image/my-bot🎯 Почему в DiceChess используются разные подходы?
1. dicechess-bot-gcp и dicechess-bot-gcp-onnx → sbt-assembly
- Платформа: Google Cloud Run.
- Причина:
- Cloud Run оптимизирован для работы с JAR-файлами.
- Автоматически собирает Docker-образ из JAR (не нужно настраивать Dockerfile).
- Проще развёртывать и отлаживать.
- Нет проблем с совместимостью библиотек.
- Cold start в Cloud Run не так критичен (боты не требуют мгновенного отклика).
2. dicechess-bot-scala и dicechess-bot-azure → sbt-native-image
- Платформа: Azure Functions.
- Причина:
- Azure Functions требует native binary для кастомных хендлеров (custom handlers).
- JAR не поддерживается для кастомных хендлеров (только для HTTP-триггеров с ограничениями).
- Native binary проще интегрировать с Azure Functions.
📊 Сравнительная таблица
| Критерий | sbt-assembly (Fat JAR) | sbt-native-image (Native Binary) |
|---|---|---|
| Тип артефакта | JAR | Native binary |
| Запуск | java -jar | ./binary |
| Требует JVM | ✅ Да | ❌ Нет |
| Время запуска | Среднее (JVM startup) | Мгновенный |
| Размер артефакта | Большой (10-50 МБ) | Средний (5-30 МБ) |
| Время сборки | Быстрое | Медленное |
| Совместимость библиотек | ✅ Полная | ⚠️ Ограниченная |
| Отладка | ✅ Легкая | ❌ Сложная |
| Требует GraalVM | ❌ Нет | ✅ Да |
| Cloud Run | ✅ Оптимально | ⚠️ Требует Docker |
| Azure Functions | ❌ Не поддерживается | ✅ Оптимально |
🔧 Рекомендации для новых проектов
Используйте sbt-assembly, если:
- Целевая платформа — Google Cloud Run.
- Нужна полная совместимость с JVM (например, если используются библиотеки с рефлексией).
- Важна простота развёртывания и отладки.
- Cold start не критичен (например, для ботов, которые не требуют мгновенного отклика).
Используйте sbt-native-image, если:
- Целевая платформа — Azure Functions (кастомные хендлеры).
- Cold start критичен (например, для HTTP-сервисов с высокой нагрузкой).
- Нужно минимизировать использование памяти.
- Проект использует мало зависимостей (чтобы избежать проблем с совместимостью GraalVM).
Альтернативы
- Docker + Fat JAR: Если нужно развёртывать в Kubernetes или других платформах, можно использовать Docker с Fat JAR.
- Docker + Native Image: Если нужно использовать Native Image в Cloud Run или Kubernetes, можно собирать Docker-контейнер с бинарником.
🛠️ Примеры настройки для разных платформ
1. Google Cloud Run + sbt-assembly
// project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.4.1")
// build.sbt
lazy val root = (project in file("."))
.settings(
name := "my-bot",
Compile / mainClass := Some("com.example.Main"),
assemblyMergeStrategy in assembly := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
)Dockerfile (опционально, если нужно кастомизировать образ):
FROM eclipse-temurin:21-jre
COPY target/scala-3.8.4/my-bot-assembly.jar /app/my-bot.jar
CMD ["java", "-jar", "/app/my-bot.jar"]Развёртывание в Cloud Run:
gcloud run deploy my-bot --source . --region us-central12. Azure Functions + sbt-native-image
// project/plugins.sbt
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.5.0")
// build.sbt
lazy val root = (project in file("."))
.enablePlugins(NativeImagePlugin)
.settings(
name := "my-bot",
Compile / mainClass := Some("com.example.Main"),
nativeImageInstalled := true,
nativeImageOptions ++= List("--no-fallback", "--install-exit-handlers"),
nativeImageOutput := target.value / "native-image" / "my-bot"
)host.json (для Azure Functions):
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "my-bot"
},
"enableForwardingHttpRequest": true
}
}Развёртывание в Azure Functions:
sbt nativeImage
func azure functionapp publish my-bot-function📚 Полезные ресурсы
- sbt-assembly
- sbt-native-image
- GraalVM Native Image
- Google Cloud Run + JAR
- Azure Functions Custom Handlers
✅ Чеклист выбора
- Определите целевую платформу (Cloud Run, Azure Functions, и т.д.).
- Проверьте, поддерживает ли платформа JAR или требует native binary.
- Оцените важность cold start для вашего проекта.
- Проверьте совместимость зависимостей с GraalVM (если выбираете
sbt-native-image). - Выберите плагин на основе анализа выше.
💡 Совет: Если не уверены, начните с
sbt-assembly. Это более универсальный и простой вариант, который работает везде, где поддерживается JVM.