Quarkus i Micronaut běží na obyčejném Java serveru jako normální jar a na malém tarifu se do něj vejdou snáz než jiné frameworky: dependency injection řeší už při buildu, takže startují za sekundu nebo dvě a mají menší heap než srovnatelná služba na Spring Bootu. Na tři věci lidé narážejí nejčastěji: rozložení artefaktu (Quarkus ve výchozím stavu nevyrobí jediný soubor), limit paměti (JVM si klidně řekne o víc, než platíš) a cesty health endpointů, které nejsou ty, které používá Spring. Tento článek pokrývá všechny tři a navíc otázku, kterou si každý položí jako první: stavět native executable, nebo prostě běžet na JVM?
Je to záměrně doplněk k článku nasazení Spring Boot aplikace do produkce, který popisuje obecnou cestu pro fat jar - verze Javy, externalizovanou konfiguraci, HikariCP, hlavičky proxy. Tady zůstáváme u toho, v čem se Quarkus a Micronaut liší, a opakujeme jen to, co potřebuješ, abys to dokázal sledovat.
Čím se liší od fat jaru ve Spring Bootu#
Build Spring Bootu ti dá jeden spustitelný soubor. Quarkus a Micronaut ti dají totéž v jiných podobách a podoba rozhoduje o tom, co nahraješ a jaký bude startovací příkaz.
| Framework | Výchozí výstup buildu | Spuštění |
|---|---|---|
| Quarkus | adresář target/quarkus-app/ | java -jar target/quarkus-app/quarkus-run.jar |
| Quarkus (uber-jar) | target/*-runner.jar | java -jar target/*-runner.jar |
| Micronaut (Gradle) | build/libs/*-all.jar | java -jar build/libs/app-0.1-all.jar |
| Micronaut (Maven) | target/*.jar | java -jar target/app-0.1.jar |
Názvy u Micronautu závisejí na názvu a verzi projektu i na pluginech, které jsi použil, takže názvy souborů výše ber jako příklady a po buildu se podívej do adresáře. U Micronautu s Gradlem pochází -all.jar z pluginu Shadow a úlohy shadowJar; u Mavenu vygenerovaný projekt používá k vytvoření spustitelného archivu plugin Shade nebo Micronaut Maven.
Společné je tohle: oba frameworky naslouchají na portu, který si zvolíš, čtou konfiguraci ze souborů a proměnných prostředí a potřebují Java runtime alespoň v té verzi, pro kterou byly zkompilované. Java řada RE:NODE spouští libovolný jar na Javě 17, 21 nebo 25, což pokrývá Quarkus, Micronaut, Spring Boot i Ktor. Aktuální vydání Quarkusu 3 a Micronautu 4 vyžadují Javu 17 nebo novější; verzi svého frameworku si před výběrem ověř v poznámkách k vydání a kompiluj s přepínačem --release odpovídajícím nejstarší Javě, na které chceš běžet. Jar sestavený pro 17 běží na 21 i 25, nikdy ne naopak.
Balení Quarkusu: fast-jar, uber-jar a ostatní#
Výchozí režim Quarkusu se jmenuje fast-jar. Není to jeden soubor. Build zapíše adresář:
target/quarkus-app/ quarkus-run.jar lib/ boot/ main/ app/ quarkus/quarkus-run.jar je drobný spouštěč. Závislosti leží v lib/ jako jary, tvé vlastní třídy v app/ a vygenerovaná data z buildu v quarkus/. Toto rozložení existuje z jediného důvodu: start je rychlejší, když runtime nemusí otevírat a procházet jeden obrovský archiv. Zlevňuje také nasazení, protože většina adresáře se mezi vydáními nemění - jen app/ a quarkus/ - takže inkrementální nahrání (nebo vrstvený image kontejneru) přenese pár set kilobajtů místo padesáti megabajtů.
Důsledek pro hosting je prostý, ale snadno se na něj zapomene: nahrát musíš celý adresář `quarkus-app`, ne jen `quarkus-run.jar`. Samotný spouštěč selže s chybou načítání tříd, protože vedle něj chybí lib/. V CI adresář zabal do archivu, nahraj archiv a rozbal ho na místě. Správce souborů v panelu rozbaluje archivy tam, kde leží, což je přesně to, co je potřeba.
$ ./mvnw -B clean package$ tar -czf quarkus-app.tar.gz -C target quarkus-app$ ls -lh quarkus-app.tar.gzTyp balení řídí jedna vlastnost, nastavená v application.properties nebo na příkazové řádce pomocí -D:
quarkus.package.jar.type | Výsledek |
|---|---|
fast-jar | Výchozí adresářové rozložení popsané výše |
uber-jar | Jediný spustitelný *-runner.jar se vším uvnitř |
legacy-jar | Starší jeden jar plus lib/, ponechaný kvůli kompatibilitě |
mutable-jar | Fast-jar, který lze při startu znovu augmentovat, pro vývojářské nástroje |
Ve starších vydáních Quarkusu (před 3.9) se vlastnost jmenovala quarkus.package.type a hodnoty byly stejná slova. Pokud build tvé nastavení ignoruje, čteš dokumentaci k nesprávné major verzi.
quarkus.package.jar.type=uber-jarKdy je uber-jar správná volba? Když tvoje cesta nasazení opravdu chce jeden soubor - přetažení jednoho jaru do správce souborů nebo startovací příkaz, na který nechceš sahat. Cenou je pomalejší start a větší jediný archiv, který se mění při každém vydání. Pokud zvládneš tarball, zůstaň u fast-jar. Pokud ne, uber-jar je v pořádku a řada malých služeb takhle běží bez potíží.
Micronaut žádnou takovou volbu nemá. Build ti dá spustitelný jar a přípona -all.jar (Gradle) prozradí, který soubor nahrát. Rychlý start mu dává předběžné zpracování v procesoru anotací, ne rozložení archivu.
Native image, nebo JVM: co spustit#
Oba frameworky umějí zkompilovat aplikaci do nativního spustitelného souboru pomocí GraalVM. Čísla, kvůli kterým to lidé chtějí, jsou skutečná: start v desítkách milisekund a mnohem menší rezidentní paměť. Skutečné jsou ale i náklady a u malé hostované služby obvykle převáží nad přínosem.
| Režim JVM | Native executable | |
|---|---|---|
| Start | Zhruba sekunda nebo dvě | Desítky milisekund |
| Paměť v klidu | Heap plus režie JVM | Znatelně nižší |
| Špičkový výkon | Vyšší po zahřátí | Často nižší, bez JIT |
| Doba buildu | Sekundy až minuta | Minuty |
| Paměť při buildu | Skromná | Několik gigabajtů |
| Reflexe, proxy | Prostě fungují | Musí být známé při buildu |
| Ladění, profilování | Plné nástroje JVM | Omezené |
| Přenositelnost | Libovolný stroj s Javou | Jeden OS a architektura CPU |
Příkazy pro úplnost:
# Quarkus: build a native executable (needs GraalVM or Mandrel installed)$ ./mvnw package -Dnative# Quarkus: build it inside a container, so you need no local GraalVM$ ./mvnw package -Dnative -Dquarkus.native.container-build=true# Micronaut, Gradle$ ./gradlew nativeCompile# Micronaut, Maven$ ./mvnw package -Dpackaging=native-imageQuarkus zapíše do target/ binárku *-runner. Běží jen na operačním systému a architektuře CPU, pro které vznikla, a proto existuje možnost buildu v kontejneru: dostaneš linuxovou binárku, i když vyvíjíš na něčem jiném.
Upřímné doporučení pro hostovaný aplikační server je režim JVM. Ze tří důvodů. Za prvé, Java řada na RE:NODE je postavená na spouštění jaru na Javě 17, 21 nebo 25; native executable je jiný druh souboru a dokumentace neuvádí, že by ho Java řada spouštěla, takže si to před plánováním ověř u podpory. Za druhé, native build chce několik gigabajtů paměti na minuty, což nepatří na produkční tarif, který se snažíš udržet malý - patří to do CI. Za třetí, režim JVM s rozumnými přepínači heapu je už sám dost malý: služba na Quarkusu nebo Micronautu se skromným stromem závislostí se vejde do tarifu s 1 GB, což je nejlevnější úroveň. Native se vyplatí, když škáluješ na nulu a často startuješ (funkce, úlohy), když provozuješ velmi mnoho drobných instancí nebo když je paměť omezujícím faktorem většího nasazení. Pro jednu službu na jednom tarifu je to náklad bez přínosu.
Pokud přesto půjdeš cestou native, pamatuj, že reflexe, načítání zdrojů a dynamické proxy se musejí deklarovat při buildu. Knihovny, které na nich stojí, potřebují konfiguraci nebo rozšíření. Oba frameworky dodávají rozšíření pro běžné z nich a chybějící registrace se projeví za běhu jako class-not-found nebo chybějící zdroj, který v režimu JVM fungoval bez problémů - proto spouštěj integrační testy proti native buildu, ne jen proti JVM. Quarkus má pro přesně tohle @QuarkusIntegrationTest.
Porty a bind#
Port, na kterém se naslouchá, je první věc, která se v jakémkoli frameworku rozbije, a oba tyto frameworky mají výchozí 8080. Tvůj server má přidělený port a aplikace na něm musí naslouchat, na všech rozhraních. Proměnné prostředí a tajné údaje popisují, kde je nastavit; klíče jsou níže.
quarkus.http.host=0.0.0.0quarkus.http.port=${SERVER_PORT:8080}micronaut: server: port: ${SERVER_PORT:8080}${SERVER_PORT:8080} znamená „použij proměnnou prostředí, pokud je nastavená, jinak 8080“, což funguje na tvém notebooku i v produkci bez úprav. Než se spolehneš na název SERVER_PORT, podívej se na záložkách Startup a Network, jakou proměnnou tvůj server skutečně poskytuje; panel odvozený od Pterodactylu obvykle vystavuje přidělený port pod tímto názvem, ale u svého serveru to ověř.
Oba frameworky také mapují proměnné prostředí na konfigurační klíče bez jediného řádku kódu: klíč napiš velkými písmeny a tečky i pomlčky změň na podtržítka.
| Klíč | Proměnná prostředí |
|---|---|
quarkus.http.port | QUARKUS_HTTP_PORT |
quarkus.datasource.jdbc.url | QUARKUS_DATASOURCE_JDBC_URL |
micronaut.server.port | MICRONAUT_SERVER_PORT |
datasources.default.url | DATASOURCES_DEFAULT_URL |
Výchozí adresa, na které Quarkus naslouchá, závisí na režimu: v dev režimu je to loopback, v běžném produkčním běhu všechna rozhraní. Výslovné nastavení quarkus.http.host=0.0.0.0 nic nestojí a odstraní pochybnosti. Služba navázaná na 127.0.0.1 uvnitř svého kontejneru je pro všechny nedostupná a proxy hlásí bad gateway, zatímco konzole tvrdí, že aplikace běží.
Profily fungují v obou stejně. V Quarkusu předpona %prod. omezuje klíč na produkční profil, který java -jar spouští, pokud neřekneš jinak; ostatní jsou %dev. a %test.. V Micronautu se prostředí pojmenovávají pomocí MICRONAUT_ENVIRONMENTS=prod, což načte application-prod.yml nad application.yml.
quarkus.http.port=8080%prod.quarkus.http.port=${SERVER_PORT:8080}%prod.quarkus.log.level=INFO%dev.quarkus.log.level=DEBUGPřepínače heapu pro malý limit paměti#
Tohle je nejdůležitější část. Kontejner má pevný limit paměti a JVM si samo rozhoduje, jak velký heap si vyžádá. Podrobný rozbor je v článku o Spring Bootu; funkční verze je tady.
Na RE:NODE se při dosažení limitu paměti kontejner zastaví jádrem a znovu se čistě nastartuje místo swapování. Původní Pterodactyl dělá opak. Pro službu je to lepší chování, ale mění to způsob, jak čteš selhání. Existují dvě dost odlišná:
- `java.lang.OutOfMemoryError: Java heap space` v konzoli. JVM si všimlo, že mu došel heap, a řeklo to. Heap je příliš malý pro data, nebo něco uniká.
- Restart bez jakékoli chyby Javy. Celý proces překročil limit kontejneru a byl zastaven zvenku. Heap byl na tarif příliš velký, nebo vyrostla paměť mimo heap.
To druhé je běžné, protože paměťová stopa JVM je víc než jeho heap. Obsahuje také metaspace pro metadata tříd, zásobníky vláken, code cache JIT, direct buffery a struktury garbage collectoru. Quarkus přidává Netty a Vert.x, které pro síťové I/O alokují direct (off-heap) buffery; to je část důvodu, proč je rezidentní velikost služby na Quarkusu větší než její -Xmx. Micronaut na Netty dělá totéž.
Heap nastav jako podíl limitu kontejneru, aby ho následoval, když změníš tarif:
$ java -XX:MaxRAMPercentage=60.0 -XX:InitialRAMPercentage=25.0 \ -XX:+ExitOnOutOfMemoryError \ -jar target/quarkus-app/quarkus-run.jarPokud nemůžeš upravit startovací příkaz, dej přepínače do prostředí; každé JVM, které se spustí, čte JAVA_TOOL_OPTIONS a na začátku konzole vypíše Picked up JAVA_TOOL_OPTIONS: ..., takže vidíš, že se to projevilo:
JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=60.0 -XX:+ExitOnOutOfMemoryErrorNepleť si to s JAVA_OPTS a JAVA_OPTS_APPEND. Ty čtou spouštěcí skripty uvnitř image kontejnerů Red Hat a Quarkusu, ne samotná binárka java, takže při obyčejném startu přes java -jar nedělají nic.
Výchozí hodnoty podle velikosti tarifu, což jsou odhady, které nahradí graf:
| Paměť tarifu | Nastavení heapu | Proč |
|---|---|---|
| 1 GB | -Xmx256m až -Xmx384m | Zhruba polovina jde na paměť mimo heap a OS |
| 2 GB | MaxRAMPercentage=60 | Asi 1,2 GB heap, zbytek na režii |
| 4 GB | MaxRAMPercentage=65 | Režie je menší podíl |
| 6 GB a víc | MaxRAMPercentage=70 | Nech místo pro buffery a špičky |
Rozumný způsob, jak najít vlastní číslo: nastav záměrně štědrý heap na tarifu s volnou pamětí, nech běžet reálný provoz celý den a sleduj graf paměti v konzoli proti limitu. Malá REST služba na kterémkoli z frameworků běžně potřebuje heap 128 až 256 MB; služba, která cachuje nebo zpracovává velké payloady, potřebuje víc. Heap nastav na to, co jsi naměřil, plus rezervu, ne na kulaté procento tarifu.
Několik přepínačů si na malých tarifech své místo zaslouží:
| Přepínač | Účinek |
|---|---|
-XX:+ExitOnOutOfMemoryError | Ukončí proces při OOM heapu, aby platforma nastartovala čistý proces |
-XX:MaxMetaspaceSize=128m | Omezí růst metadat tříd |
-XX:MaxDirectMemorySize=128m | Omezí direct buffery, které používá Netty |
-XX:+UseSerialGC | Collector s nejnižší režií pro 1 CPU nebo malý heap |
-Xss512k | Menší zásobníky vláken, pokud běží hodně vláken |
-XX:+ExitOnOutOfMemoryError je ten, který většina lidí vynechá. JVM, které v jednom vlákně vyhodilo OOM heapu, často žije, ale je poničené: část požadavků obslouží a jiné selžou. Ukončení ti dá čistý proces. Pozor, restarty se sledují. Hlídač pádů se ptá každé dvě minuty, zda uptime nešel zpět nebo zda server nepřešel offline; restarty, které sis vyžádal, se nepočítají, tři restarty za hodinu dají na stránku serveru varování a otevřou automatický tiket, šest znamená pozastavení. Služba uvězněná ve smyčce nedostatku paměti je přesně to, co hledá, takže dimenzování oprav, než ji necháš bez dozoru.
Záleží i na CPU. Je to tvrdé omezení na podíl, který sis koupil, a 100 % je pomalé, nikdy důvod k pozastavení. JVM odvozuje velikosti vláknových poolů z počtu CPU, které vidí, což může podíl půl jádra zaokrouhlit na jedno. Pokud je start pomalý nebo latence přichází ve výkyvech na nejmenší úrovni, vyplatí se zkusit -XX:ActiveProcessorCount=1. Obecná lekce z článku CPU vs RAM pro herní servery platí beze změny: zjisti, který zdroj je omezující, než koupíš víc toho druhého.
Vlákna, nejen heap
Oba frameworky běží na Netty a oba umějí spouštět blokující kód v poolu workerů. Quarkus nastavuje velikost toho poolu ve výchozím stavu podle počtu CPU, který zjistí, a klíč k jeho omezení je quarkus.thread-pool.max-threads. Každé vlákno stojí zásobník a trochu nativní paměti, takže pool 200 workerů u služby, která nikdy nemá víc než 10 požadavků současně, jen nafukuje stopu. V Micronautu je ekvivalent pod micronaut.executors, například micronaut.executors.io.type: fixed s micronaut.executors.io.nThreads: 16. Nastav to na to, co služba skutečně využívá.
Databázová spojení#
Údaje o spojení pocházejí z databáze, kterou připojíš. Aplikační servery mají databázové sloty vytvářené v panelu s vygenerovaným hostem, uživatelem a heslem; samostatný tarif hostingu databází dává PostgreSQL nebo MySQL 8.4 LTS s přihlašovacími údaji generovanými pro každý server. Hodnoty drž v proměnných prostředí, nikdy uvnitř jaru.
QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://db-host:5432/shopQUARKUS_DATASOURCE_USERNAME=shop_appQUARKUS_DATASOURCE_PASSWORD=change-mequarkus.datasource.db-kind=postgresqlquarkus.datasource.jdbc.max-size=8quarkus.datasource.jdbc.min-size=2Quarkus používá jako pool Agroal a quarkus.datasource.jdbc.max-size je jeho strop. U Micronautu se datasource konfiguruje pod datasources.default a výchozím poolem je HikariCP:
datasources: default: url: ${DATASOURCES_DEFAULT_URL} username: ${DATASOURCES_DEFAULT_USERNAME} password: ${DATASOURCES_DEFAULT_PASSWORD} maximum-pool-size: 8Ať je pool jakýkoli, aritmetika je stejná: každé spojení drží databáze, takže dvě instance s poolem po osmi plus migrační úloha se sčítají proti limitu samotné databáze. Pooly spojení a limity to rozebírají. Pool o šesti až osmi spojeních je pro službu na jednom nebo dvou jádrech víc než dost.
Health endpointy v Quarkusu a Micronautu#
Žádný z frameworků nepoužívá Spring /actuator/health. Oba ti dají health endpoint až po přidání závislosti a cesty se liší.
Quarkus: SmallRye Health
Přidej rozšíření:
$ ./mvnw quarkus:add-extension -Dextensions="smallrye-health"Dostaneš čtyři endpointy pod předponou /q:
| Cesta | Na jakou otázku odpovídá |
|---|---|
/q/health | Všechno dohromady: live, ready a started |
/q/health/live | Je proces ve stavu, kdy může pokračovat? |
/q/health/ready | Má tato instance dostávat provoz? |
/q/health/started | Skončil start? |
Odpověď je {"status":"UP","checks":[...]} s HTTP 200, nebo DOWN s 503. Rozšíření přidávají kontroly sama - například rozšíření datasource přidá kontrolu připravenosti databáze, takže chybějící databáze zčervená /q/health/ready, aniž bys cokoli psal. Tvoje vlastní kontroly jsou CDI beany:
@ApplicationScoped@Readinesspublic class CacheWarmCheck implements HealthCheck { @Override public HealthCheckResponse call() { return HealthCheckResponse.up("cache-warm"); }}Předpona /q je neaplikační kořen Quarkusu a lze ji změnit pomocí quarkus.http.non-application-root-path. Pokud ji přesuneš, uprav všechno, co službu monitoruje.
Micronaut: modul management
Přidej do buildu micronaut-management. Endpointy se zapínají konfigurací:
endpoints: health: enabled: true details-visible: NEVERZákladní cesta je /health s /health/liveness a /health/readiness pod ní. details-visible přijímá NEVER, AUTHENTICATED nebo ANONYMOUS a rozhoduje, kdo uvidí rozpis po jednotlivých indikátorech. Na veřejné adrese ho nech na NEVER: detaily mohou prozradit názvy hostů a verze. Chceš-li management endpointy úplně držet mimo veřejný port, přesune je na samostatný port endpoints.all.port.
Co dělá dobrá kontrola
Liveness by nikdy neměla záviset na databázi ani jiné službě. Pokud ano, výpadek databáze způsobí, že platforma restartuje naprosto zdravý proces, a tím to zhorší. Readiness je místo, kam závislosti patří. Každou kontrolu drž levnou: indikátor, který volá tři cizí API, mění každý ping monitoru v zátěž. Řádné ukončení a health checky to rozebírají podrobněji, včetně poloviny příběhu o ukončování. V Quarkusu quarkus.shutdown.timeout určuje, jak dlouho čeká na dokončení probíhajících požadavků, když stiskneš Restart.
Otestuj z konzole nebo ze svého stroje:
$ curl -s -o /dev/null -w "%{http_code}\n" https://api.example.com/q/health/ready200A na veřejné adrese nevystavuj nic víc než health. Metriky, dev UI Quarkusu a endpointy /env a /beans v Micronautu prozrazují konfiguraci. Dev UI Quarkusu je aktivní jen v dev režimu, ale dvakrát zkontroluj, že nic, co jsi přidal do application.properties, nezapíná management cesty v produkci.
Za proxy: TLS a skutečné adresy klientů#
TLS neukončuj uvnitř aplikace. Aplikační tarify mají slot reverzní proxy: nasměruj záznam A na zobrazenou adresu a certifikát se vystaví a obnovuje automaticky, v okně 21 dní. Aplikace obsluhuje prosté HTTP na svém přiděleném portu a skutečná adresa klienta přichází v X-Forwarded-For.
Žádný z frameworků ve výchozím stavu nevěří přeposílaným hlavičkám, z rozumného důvodu: poslat je může kterýkoli klient. Bez tohoto nastavení vidíš jako klienta adresu proxy a přesměrování sestavená z požadavku vycházejí jako http://.
quarkus.http.proxy.proxy-address-forwarding=truequarkus.http.proxy.allow-x-forwarded=truequarkus.http.proxy.enable-forwarded-host=trueU Micronautu je klíč, který pojmenovává hlavičku nesoucí adresu klienta, micronaut.server.client-address-header:
micronaut: server: client-address-header: X-Forwarded-ForZapni je, jen když jsi opravdu za proxy, protože kdokoli, kdo se dostane přímo na port aplikace, by pak mohl podvrhnout svou adresu. Po nastavení pošli požadavek a vyřešenou adresu klienta vypiš z ladicího endpointu, místo abys to předpokládal. Co dělá reverzní proxy vysvětluje celý řetězec.
Nasazení a spuštění na RE:NODE#
Cesta nasazení je stejná jako pro jakýkoli jar. Sestav v CI nebo lokálně a tam také spusť testy. Build Quarkusu nebo Micronautu je lehčí než native, ale pořád chce víc paměti, než by na něj měl utrácet malý produkční tarif, a build tam, kde testuješ, drží artefakt reprodukovatelný.
- Sestav artefakt:
./mvnw packagepro Quarkus (pak zabaltarget/quarkus-app), nebo./gradlew shadowJarpro Micronaut. - Nahraj ho přes správce souborů nebo SFTP. Obojí je na každém aplikačním serveru, s přihlašovacími údaji SFTP pro každý server. Správce souborů rozbaluje archivy na místě, což adresáři Quarkusu vyhovuje.
- Na záložce Startup nastav proměnné prostředí: port, hodnoty datasource,
JAVA_TOOL_OPTIONSa profil (QUARKUS_PROFILE=prodneboMICRONAUT_ENVIRONMENTS=prod). - Nasměruj startovací nastavení na správný soubor, pak nastartuj z konzole a sleduj řádek, kterým se framework ohlásí jako spuštěný.
Alternativou jsou nasazení z GitHubu. Panel to dělá přes GitHub App s krátkodobými tokeny, takže fungují i soukromé repozitáře, a se dvěma přepínači: stáhnout při každém startu a nasadit při push, což restartuje běžící server a nastartuje zastavený. Je to jen GitHub. Jde o cestu ze zdroje na server, takže se hodí, jen pokud bude server držet výstup buildu; praktický vzor je větev, která obsahuje pouze artefakt vydání. Před propojením pre-flight kontrola přečte repozitář a vypíše, co se rozbije, od nejhoršího, například navázání na špatný port nebo hosta.
Nový Java server začíná s malou funkční stránkou na své adrese, dokud nenasadíš kód, a ta stránka nikdy nepřepíše tvé soubory.
Řešení potíží#
`Error: Unable to access jarfile quarkus-run.jar` nebo chyba chybějícího `lib`. Nahrál jsi spouštěč bez zbytku adresáře quarkus-app. Nahraj a rozbal celý adresář, nebo přepni na uber-jar.
`no main manifest attribute, in app.jar`. Nahrál jsi tenký jar. U Micronautu je to obyčejný jar místo -all.jar; u Quarkusu je to špatný soubor v target/. Najdi ten spustitelný.
`UnsupportedClassVersionError`. Jar byl zkompilován pro novější Javu, než na jaké server běží. Kompiluj s --release 17, nebo pro server zvol Javu 21 či 25.
Server se restartuje bez stack trace. Proces překročil limit kontejneru. Sniž MaxRAMPercentage, omez direct paměť pomocí -XX:MaxDirectMemorySize a porovnej graf paměti s limitem. Pamatuj, že tři restarty za hodinu spustí varování.
`java.lang.OutOfMemoryError: Java heap space`. Heap je příliš malý, nebo se něco hromadí. Zvětši ho, pokud má tarif místo, nebo zachyť dump pomocí -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/container/dump.hprof a soubor potom smaž, protože je stejně velký jako heap.
`java.lang.OutOfMemoryError: Direct buffer memory`. Netty došla off-heap paměť, obvykle při záplavě velkých požadavků nebo odpovědí. Zvyš -XX:MaxDirectMemorySize, pokud je na tarifu místo, a hledej kód, který drží buffery.
Port je už používán, nebo bad gateway, zatímco aplikace běží. Aplikace naslouchá na 8080 místo na přiděleném portu, nebo na loopbacku. Nastav port z prostředí a quarkus.http.host=0.0.0.0.
`/q/health` vrací 404. Rozšíření smallrye-health není v buildu, nebo ho přesunulo quarkus.http.non-application-root-path.
`/health` vrací 404 u Micronautu. micronaut-management není na classpath, nebo je endpoints.health.enabled nastaveno na false.
Native image funguje lokálně, ale na serveru selhává. Byl sestaven pro jiný operační systém nebo architekturu CPU. Sestav ho v CI v linuxovém kontejneru, nebo se vrať k režimu JVM.
FAQ#
Je fast-jar lepší než uber-jar?
Pro start a inkrementální nasazení ano. Pro cestu nasazení, která potřebuje jediný soubor, ne. Fast-jar je výchozí z dobrého důvodu, ale uber-jar na malé službě není chyba: nastav quarkus.package.jar.type=uber-jar a nahraj jeden soubor.
Mám na malém tarifu použít native image?
Obvykle ne. Build potřebuje několik gigabajtů paměti a minuty CPU, Java řada je zdokumentovaná pro spouštění jarů a režim JVM s upraveným heapem se vejde do nejlevnější úrovně. Native se vyplatí pro škálování na nulu a pro velmi mnoho malých instancí, ne pro jednu službu.
Kolik paměti potřebuje služba na Quarkusu nebo Micronautu?
Malá REST služba s databází běžně běží s heapem 128 až 256 MB a procesem výrazně pod 1 GB, takže úroveň s 1 GB je použitelná. Měř grafem paměti v konzoli po celý den reálného provozu a nespoléhej na žádné pravidlo palce, včetně tohoto.
Potřebuji -Xmx, nebo MaxRAMPercentage?
MaxRAMPercentage použij, pokud můžeš měnit tarif, protože následuje limit. -Xmx použij pro pevnou, naměřenou hodnotu. Pokud jsou nastavené obě, vyhrává -Xmx.
Proč můj health endpoint odpovídá na jednom portu a na jiném ne?
Management rozhraní může být na vlastním portu. V Micronautu to dělá endpoints.all.port a Quarkus umí po nakonfigurování obsluhovat management na samostatném rozhraní. Tarif má porty, které uvádí, takže než cokoli přesuneš, zkontroluj záložku Network.
Dá se takhle spouštět i Ktor nebo Spring Boot?
Ano. Libovolný jar běží na Java řadě a přepínače heapu jsou totožné. Specifika Spring Bootu jsou v průvodci nasazením Spring Bootu.


