Docker и Kotlin: контейнеризация приложений от А до Я
Контейнеризация стала стандартом де-факто для современной разработки. Она решает проблему "на моей машине работает" и упрощает CI/CD. В этой статье мы разберем, как эффективно контейнеризировать Kotlin-приложения с помощью Docker — от создания простого Dockerfile до продакшн-оптимизаций и оркестрации.
1. Основы: Dockerfile для Kotlin-приложения
Сердце контейнеризации — Dockerfile. Для Kotlin-приложений, которые компилируются в JVM байт-код, мы используем многоступенчатую сборку (multi-stage build). Это позволяет отделить среду сборки от среды выполнения, уменьшая размер финального образа.
Рассмотрим типовое приложение на Kotlin с использованием Gradle (файл build.gradle.kts упрощен):
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.0"
application
id("com.google.cloud.tools.jib") version "3.4.0" // опционально
}
application {
mainClass.set("com.example.MainKt")
}
Создадим Dockerfile с двумя стадиями:
# Стадия 1: Сборка
FROM gradle:8.5-jdk21 AS builder
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
COPY src ./src
RUN gradle build --no-daemon
# Стадия 2: Запуск
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/my-app-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Команды для сборки и запуска:
docker build -t my-kotlin-app .
docker run -p 8080:8080 my-kotlin-app
Рекомендации:
- Используйте
eclipse-temurin(OpenJDK) вместо полного JDK — образ меньше. - Кэшируйте зависимости: сначала копируйте
build.gradle.kts, затемsrc. - Для Spring Boot приложений можно использовать
layersдля инкрементальной сборки.
2. Оптимизация с помощью Jib и Gradle
Ручное написание Dockerfile — это классика, но есть инструменты, которые автоматизируют процесс. Jib (от Google) интегрируется с Maven/Gradle и собирает оптимизированные Docker-образы без Docker daemon.
Добавьте плагин Jib в build.gradle.kts:
plugins {
id("com.google.cloud.tools.jib") version "3.4.0"
}
jib {
from {
image = "eclipse-temurin:21-jre-alpine"
}
to {
image = "my-registry/my-kotlin-app:latest"
}
container {
ports = listOf("8080")
jvmFlags = listOf("-Xms512m", "-Xmx1024m")
}
}
Сборка одной командой:
./gradlew jibDockerBuild
Преимущества Jib:
- Автоматическое разделение на слои (зависимости, ресурсы, классы).
- Не требует Dockerfile.
- Работает без Docker daemon — идеально для CI/CD.
Пример Kotlin-приложения (простой HTTP-сервер):
// src/main/kotlin/com/example/Main.kt
package com.example
import com.sun.net.httpserver.HttpServer
import java.net.InetSocketAddress
fun main() {
val server = HttpServer.create(InetSocketAddress(8080), 0)
server.createContext("/") { exchange ->
val response = "Hello from Kotlin in Docker!"
exchange.sendResponseHeaders(200, response.length.toLong())
exchange.responseBody.write(response.toByteArray())
exchange.close()
}
server.start()
println("Server started on port 8080")
}
3. Docker Compose: многоконтейнерные приложения
Реальные проекты редко состоят из одного сервиса. Docker Compose позволяет описать и запустить несколько контейнеров (например, приложение + база данных + Redis).
Создадим docker-compose.yml для Kotlin-приложения с PostgreSQL:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=mydb
- DB_USER=user
- DB_PASSWORD=pass
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
Запуск одной командой:
docker compose up --build
В Kotlin-коде можно использовать библиотеку Exposed для работы с БД:
// Пример подключения к PostgreSQL через Exposed
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
fun initDb() {
Database.connect(
url = "jdbc:postgresql://${System.getenv("DB_HOST")}:${System.getenv("DB_PORT")}/${System.getenv("DB_NAME")}",
driver = "org.postgresql.Driver",
user = System.getenv("DB_USER"),
password = System.getenv("DB_PASSWORD")
)
transaction {
// Создание таблиц и миграции
}
}
4. Best Practices и продакшн-настройки
Чтобы контейнеры работали стабильно и безопасно, следуйте этим рекомендациям:
- Не запускайте от root. Создайте пользователя в Dockerfile:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser - Минимизируйте слои. Объединяйте команды
RUN(например,RUN apt-get update && apt-get install -y --no-install-recommends ... && rm -rf /var/lib/apt/lists/*). - Используйте .dockerignore. И