Docker, spring boot, quarkus
Docker is alom tegenwoordig. Het is een no-brainer om bij de moderne architecturen te kijken naar docker…. Orchestration engines zoals kubernetes zijn te vinden op de 3 grote cloud platformen AWS, GCP en Azure.
Dockers starten snel op en zijn dus interessant als je moet rekening houden met zaken die moeten dynamisch moeten schalen. Dockers zijn efficiënt in resource gebruik al durft dit wel eens een trade-off te zijn, het meest interessante vind ik persoonlijk dat je efficiënt container images kan uitwisselen en hergebruiken.
Dit maakt concepten als DevOps en meer bepaald CD/CI een pak gemakkelijker te implementeren. Zeker als je gebruik maakt van immutable container images. Met de source code verpakt in de container, zodat je een vorm van voorspelbaarheid krijgt.
Op welke engine je de specifieke docker container ook zal runnen. Hij zal altijd dezelfde manier reageren. Om eerlijk te zijn, ik zou niet weten waarom je het anders zou willen.
Bovendien als fan van de 12 factor app principes (https://12factor.net) heb je met docker een aantal vliegen in 1 klap:
- Nummer 8 - Scale out via the process model: is by design ingebakken. Containers zijn immers met dit concept ontwikkeld, ze zijn stateless en kunnen gemakkelijk meerdere instanties gelijk draaien hebben.
- Nummer 10 - Keep development, staging and production as similar as possible: is door het gebruik te maken van immutable images eenvoudig ingewilligd. De source code zit ingebakken in de
Spring boot is voor mezelf ook een no-brainer. Ik moet zeggen dat ik bij het verschijnen van de eerste release toch maar sceptisch was. Maar aangezien ik al jaren gelukkige spring core/integration gebruiker was, volgde ik het op de voet. Ik meen mij ook te herinneren dat op JavaOne ‘15 in San Francisco er nogal lacherig mee omgegaan werd toen ik sprak met enkele mensen dat ik spring boot wel genegen was. Java EE was gelijktijdig ook een mooi revolutie aan het doormaken.
Spring boot was echter niet helemaal ontworpen met Docker in het achterhoofd. In de wereld van microservices durft spring boot al eens met een overhead komen immers onze docker image is immutable, dus een aantal dynamische zaken zijn niet echt nodig, maar zo ingebakken in spring boot. Eveneens kan het deployable artificact dat uit een spring boot project rolt nogal eens groot uitvallen qua binary size.
Een direct nadeel hiervan is dat spring boot nogal gekend is voor zijn trage opstart snelheid.
Er zijn verschilllende methodes om dit sneller te laten verlopen, maar het is toch steeds maar een proces van constant tunen en monitoren. Als deze startup tijd echt een probleem wordt, is het interessant om naar bijvoorbeeld GraalVM te kijken zoals een reactie op bovenstaande artikel suggereert en meer specifiek de native-image piste waarbij je code op voorhand gecompileerd werd. De oplossing zit dus eigenlijk in de manier van compileren, waarbij een JIT (Just In Time) compiler het moet afleggen tov een AOT (Ahead Of Time) compiler.
JIT-compilatie heeft eigenlijk het grote voordeel dat het is gemakkelijker te verdelen over verschillende platformen en zo zijn er nog verschillende argumenten voor JIT compilatie. Echter als we rekening houden met wat ik verteld heb over, wat ik, immutable container images noem, is JIT-compilatie eigenlijk overhead. Immers ondervangt de docker image al de voordelen van een programma dat teert op JIT-compilatie.
Dus terug naar onze startup tijd. Aangezien we ons platform eigenlijk “meenemen” in onze docker image zouden we zeker gebruik kunnen maken van een AOT compilatie van onze binary. En nu is het tijd om met Quarkus op de proppen komen!
Quarkus is namelijk een java framework specifiek toegespitst op JVM talen en dus Java native te compileren. Hierdoor is Quarkus ook interessant voor bijvoorbeeld een serverless setup, iets waar ik tot voor kort sowieso voor Python koos.
Een docker builden voor Quarkus neemt misschien wat tijd in beslag, maar via AOT compiling zal de startup kost van deze docker peanuts zijn vergeleken met hoe een een spring boot docker zou opstarten.
Laten we de 2 eens tegenover elkaar zetten in een typisch Hello world scenario!
Ik bouw een eenvoudig spring boot applicatie die een endpoint exposed die “Hello + name“ returned bij een GET-Request.
Bij Spring boot applicaties ben ik best fan van google jib om de docker image te bouwen.
Bij quarkus geeft dit een heel ander effect. Bij het opteren om een native jar te bouwen, zal hij een runnable jar bouwen specifiek voor een bepaald platform. In dit geval voor linux, ik zal de jar dus lokaal niet kunnen runnen, daarvoor moet ik een specifieke dockerfile gebruiken. Deze is mooi bij de init package geleverd.
Na deze docker gebouwd te hebben, kan ik eindelijk vergelijken met spring. En de opstart is mindblowing tov van die opstarttijd van spring.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.5.RELEASE)
2020-11-18 12:43:24.142 INFO 1 --- [ main] be.one16.springhello.HelloApplication : Starting HelloApplication on a13a65c945f5 with PID 1 (/app/classes started by root in /)
2020-11-18 12:43:28.012 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-18 12:43:28.046 INFO 1 --- [ main] be.one16.springhello.HelloApplication : Started HelloApplication in 5.271 seconds (JVM running for 6.616)
One16-MacBook-Pro:quarkushello one16$ docker run -i --rm -p 8080:8080 quarkus/quarkushello
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-11-18 13:31:46,019 INFO [io.quarkus] (main) quarkushello 1.0.0-SNAPSHOT native (powered by Quarkus 1.9.2.Final) started in 0.118s. Listening on: http://0.0.0.0:8080
2020-11-18 13:31:46,019 INFO [io.quarkus] (main) Profile prod activated.
2020-11-18 13:31:46,019 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]