Project Coqfoss: Van leeg project naar Docker image

Welkom bij weer een nieuwe blog over Coqfoss, ons interne project waarin we nieuwe dingen te leren door samen te werken. In deze reeks blogs nemen we je mee in de technieken die we gebruiken en delen we de ervaringen die we opdoen. In deze blogpost gaan we dieper in op het eerste deel dat we nodig hebben voor het realiseren van de walking skeleton; het maken van een simpele applicatie in een Docker image.

Om dat te doen starten we eerst met een lege Spring Boot applicatie. We gaan daar wat dingen aan wijzigen, zodat we in de toekomst makkelijker zaken kunnen toevoegen. Als dat goed is, gaan we proberen van deze functieloze applicatie een Docker image te bouwen. Vervolgens gaan we met behulp van een gitlab-ci.yml de pipeline definiëren.  

Zoals in de vorige blog is beschreven, gaan we ons project doen op basis van Maven, Spring Boot en Kotlin. Deze redelijk ‘vanille’ tech stack kiezen we, zodat we niet te veel nieuwe concepten en technologieën tegelijk introduceren. Als er gaandeweg wel een framework zo gaaf is dat we die willen proberen, hopen we dat we door een hexagonale/clean architectuur relatief eenvoudig van framework kunnen wisselen.  

Projectstructuur

Om dat te doen maken we wel gelijk al een aanpassing aan de projectstructuur. Standaard wordt een Spring Boot project (gemaakt via IntelliJ of start.spring.io) in één Maven module neergezet. Dat wijzigen we gelijk, zodat we een project hebben met daarin een root pom en daaronder een module, die we Web noemen. In de Web module zit nu alles wat met Spring te maken heeft. Het idee is dat als we beginnen met de features we een nieuwe module maken, zonder dependency op Spring. We beperken alle Spring magic zo tot een module (in clean architecture ook wel main genoemd) waardoor we onze business logica alleen de SDK nodig heeft. Daarnaast zetten we in de root pom van het project alle dependencies, waaronder de Spring bom, in een dependency management blok. Op die manier hebben we één plek met alle versies. Dat maakt het makkelijker voor onszelf om onze dependencies te managen in de toekomst.

Het bouwen van een Docker image

Nu de basisopzet van het project geregeld is, kunnen we een Docker image gaan maken. Klassiek bouw je een Docker image met een Dockerfile, maar tegenwoordig zijn er nog een aantal andere opties om te verkennen. We willen graag dat graag de volgende doelen bereiken in bij het bouwen van een image: 

Dockerfile

De eerste optie die we hebben is natuurlijk gewoon het gebruiken van een Dockerfile. Alhoewel dat wel de simpelste oplossing is, geeft een Dockerfile niet het gewenste resultaat omdat we onze hele applicatie in een laag stoppen. Dat is nu in eerste instantie niet zo´n probleem, maar in de toekomst als de applicatie groter wordt kan dat betekenen dat we elke keer als we pushen of pullen dat we de hele applicatie rondduwen. Het is beter om de veel veranderende delen, zoals onze eigen code, te scheiden van de weinig veranderende delen, zoals onze dependencies. Als we dat zelf willen doen levert dat een redelijk grote complexe Dockerfile op.  

Spring Boot Maven plugin

Gelukkig heeft VMWare ook bedacht, dat waarschijnlijk meer mensen dit willen doen met hun applicaties en hoeven we het wiel niet opnieuw uit te vinden. Ze hebben de Spring Boot Maven plugin een manier gegeven om images te bouwen.  Met een Maven command bouwen we dan een image:  

./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=lecoqfoss

Het resulterende image heeft er een net verdeelde lasagna van gemaakt. Er zijn aan deze route helaas twee nadelen gebleken. De buildtime voor een lege Spring Boot applicatie is boven de 3 minuten op mijn machine. Dat komt doordat de plugin onder water gebruik maakt van buildpacks van de CNCF. De source build doorloopt daar 4 fases mee en levert een erg geoptimaliseerd image. Het andere nadeel is dat de labels in de metadata van de image ‘vervuild’ raakt met labels als “org.springframework.boot.spring-configuration-metadata.json”, die in het geval van ons project 516379 karakters telt. Dat is ongeveer 30 keer zoveel als in ons artikel in Java Magazine! Wellicht dat dit een misconfiguratie aan onze kant is, maar voor nu lijkt het er op dat daarmee deze simpele route niet voldoet. Wel is bij een docker inspect duidelijk dat de image goed in layers is opgeknipt. 

JIB Maven plugin

De overgebleven optie is dan de JIB Maven plugin van Google. Als we die gebruiken en kijken naar het resultaat hebben we binnen 10 seconden al een build gedaan. Net als bij de hiervoor besproken buildpack variant is de applicatie opgeknipt in de resources, classes en de dependencies. Ook kunnen we met nu makkelijk labels toevoegen volgens de OCI standaard in een labels blok. We kunnen zo bijvoorbeeld met een URL aangeven waar de sources voor de image te vinden zijn, wie de maker is en wanneer de image gemaakt is.  

In de volgende blog gaan we aan de slag met het opzetten van de CI pipeline in Gitlab. We gaan dan zien hoe we een simpele pipeline met behulp van een gitlab-ci.yml opzetten, hoe we build dependencies cachen en hoe we met een webhook naar Teams notificaties over de pipeline inregelen. Tot de volgende keer! 

Geef een reactie

Je e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *