Зачем Quarkus Native в Kubernetes
Классический JVM-сервис на Spring Boot / Quarkus JVM часто стартует секунды и просит сотни мегабайт heap. В Kubernetes это бьёт по scale-out, rolling update и scale-to-zero: пока Pod не проходит readiness — трафик не идёт.
Quarkus + GraalVM Native Image даёт другой профиль: cold start часто ~50–300 ms для небольшого REST-сервиса, RSS порядка 30–80 MB, контейнер — десятки МБ вместо сотен. Для K8s это значит быстрее probes, ниже requests/limits и плотнее упаковка на ноде.
Реальный каркас сервиса
mvn io.quarkus.platform:quarkus-maven-plugin:3.17.5:create \
-DprojectGroupId=by.example \
-DprojectArtifactId=hello-native \
-Dextensions="rest,rest-jackson,hibernate-validator,smallrye-health,micrometer-registry-prometheus"
cd hello-native
// GreetingResource.java
@Path("/api/hello")
public class GreetingResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> hello(@QueryParam("name") @DefaultValue("world") String name) {
return Map.of("message", "Hello, " + name);
}
}
# application.properties
quarkus.application.name=hello-native
quarkus.http.port=8080
quarkus.native.container-build=true
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21
quarkus.package.jar.enabled=false
Сборка native (два рабочих способа)
1. В контейнере (удобно в CI, без локального GraalVM):
./mvnw package -Dnative -DskipTests
# артефакт: target/hello-native-1.0.0-SNAPSHOT-runner
ls -lh target/*-runner
2. Локальный Mandrel/GraalVM 21:
export GRAALVM_HOME=/opt/mandrel-java21
./mvnw package -Dnative -Dquarkus.native.container-build=false
Типичные цифры на простом REST (ориентир, зависит от CPU CI): build 3–8 минут, binary ~40–70 MB, startup <0.2 s на тёплом CPU.
Минимальный Docker-образ
Официальный подход Quarkus — multi-stage: builder собирает native, runtime — micro/UBI без JDK.
# src/main/docker/Dockerfile.native-micro
FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work /work/application \
&& chown 1001 /work \
&& chown 1001 /work/application
EXPOSE 8080
USER 1001
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
docker build -f src/main/docker/Dockerfile.native-micro -t registry.example/hello-native:1.0.0 .
docker images registry.example/hello-native:1.0.0
# часто ~60–90 MB вместо 250–400+ MB JVM-образа
Ещё жёстче — distroless/static, если бинарь собран с нужными libc-флагами; для старта берите ubi9-quarkus-micro-image — меньше сюрпризов с DNS/certs в K8s.
Деплой в Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-native
spec:
replicas: 2
selector:
matchLabels:
app: hello-native
template:
metadata:
labels:
app: hello-native
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: /q/metrics
prometheus.io/port: "8080"
spec:
containers:
- name: app
image: registry.example/hello-native:1.0.0
ports:
- containerPort: 8080
env:
- name: QUARKUS_HTTP_PORT
value: "8080"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
startupProbe:
httpGet:
path: /q/health/started
port: 8080
failureThreshold: 10
periodSeconds: 1
readinessProbe:
httpGet:
path: /q/health/ready
port: 8080
periodSeconds: 5
livenessProbe:
httpGet:
path: /q/health/live
port: 8080
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: hello-native
spec:
selector:
app: hello-native
ports:
- port: 80
targetPort: 8080
Из-за быстрого старта startupProbe можно держать коротким — Pod быстрее станет Ready при rolling update и HPA.
Сравнение «на пальцах» (типичный micro-REST)
- Spring Boot JVM: образ ~250–400 MB, старт 2–8 s, RSS 200–400 MB.
- Quarkus JVM: образ ~150–250 MB, старт 0.5–2 s, RSS 100–200 MB.
- Quarkus Native: образ ~60–100 MB, старт <0.3 s, RSS 40–80 MB.
Именно native выигрывает на burst-scale и serverless-подобных сценариях (Knative, KEDA, частые рестарты).
Подводные камни native
- Рефлексия/JNI/динамические прокси — нужны
@RegisterForReflectionили extension-поддержка. - Дольше CI: кэшируйте Mandrel builder layer в registry.
- Не всё из экосистемы Spring «просто переносится» — берите Quarkus extensions (REST, Hibernate ORM Panache, Kafka, Redis).
- Отладка сложнее JVM: держите JVM-профиль для локальной разработки (
quarkusDev), native — для staging/prod-образа.
# локально быстро
./mvnw quarkus:dev
# в CI — native image
./mvnw package -Dnative -DskipTests
CI-фрагмент (GitLab)
build-native:
image: quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21
stage: build
script:
- ./mvnw -B package -Dnative -DskipTests
- docker build -f src/main/docker/Dockerfile.native-micro
-t $CI_REGISTRY_IMAGE/hello-native:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE/hello-native:$CI_COMMIT_SHORT_SHA
artifacts:
paths:
- target/*-runner
Вывод
Quarkus Native — практичный способ получить для Kubernetes быстрый cold start и маленькие образы без отказа от Java. Соберите runner через Mandrel, упакуйте в micro-UBI, выставьте скромные resources и короткие probes. Для API-gateway, BFF и worker'ов с редким трафиком выигрыш в плотности и скорости выката обычно окупает более длинную native-сборку в CI.
