Ontwikkelomgevingen blijven vaak een onderwerp van discussie. Mag iedereen zijn eigen favoriete Integrated Development Environment (IDE) gebruiken? Hoelang duurt het voordat een nieuwe ontwikkelaar zijn ontwikkelomgeving volledig werkend heeft? Draai je de omgeving op je laptop, op een centrale server, gevirtualiseerd…? Hoe gaat de code vervolgens naar productie? Deploy je een war vanaf je laptop, of gebruik je Jenkins of iets dergelijks?
Dit artikel biedt een blik in de keuken bij een aantal bedrijven. De auteurs hebben ruime ervaring met een flink aantal verschillende oplossingen voor ontwikkelomgevingen. Hierna kun je lezen wat de ervaringen en de voor- en nadelen van de verschillende oplossingen zijn.
Ontwikkelomgeving in een Docker container
Containers en Docker zijn tegenwoordig erg hip, maar worden nog vooral gebruikt om niet grafische applicaties in te draaien. Dus men gebruikt containers om een applicatieserver in te draaien, maar niet om een IDE in te draaien. Bij ING/Info Support waren we benieuwd hoe goed het zou werken om een hele ontwikkelomgeving in Docker te draaien.
Er bleken drie opties te zijn om verbinding te maken naar de ontwikkelomgeving in Docker. De bekendste opties zijn Remote Desktop Protocol (RDP) en Virtual Network Computing (VNC). Je gebruikt een applicatie die RDP en/of VNC ondersteund en daarmee maak je verbinding naar de Docker container die al draait. Een interessant alternatief is het gebruik van het X Windows System (X11). Met X11 kun je ervoor zorgen dat als je een Docker container start dat je direct een IDE te zien krijgt. Dat zorgt ervoor dat je niet eerst een container hoeft te starten en daarna een verbinding moet maken naar de container zoals met RDP en VNC.
De ontwikkelomgeving bij onze ING teams stond ofwel op een centrale gedeelde Linux server waarop een heel team moest werken of op persoonlijke Windows machines. Aangezien Windows nog geen ondersteuning bood voor Docker maakten we gebruik van Virtualbox images met daarin de Docker containers. Om verbinding te maken gebruikten we RDP en VNC.
Overzicht van de omgeving
Voor meer informatie over Docker verwijs ik je naar het artikel in de links. Wat wel handig is om te weten is dat een container een draaiende instantie is van een image.
Met Docker kun je images stapelen, een soort van inheritance in Java. In eerste instantie gebruikten we een basis image en daarbovenop een team specifiek image. In het basis image zitten algemene dingen zoals Java, Maven, NPM, Tomcat en security configuratie. In het team image zitten team specifieke zaken zoals de applicaties en instellingen van dat team.
In het begin werkte dat prima, totdat we sommige onderdelen wilden delen met andere teams en bepaalde onderdelen niet wilden delen. In het figuur is te zien dat applicatie Elrond zowel voor Team X beschikbaar moet zijn als voor team Y. Nu zouden we de applicatie in het Shared image kunnen zetten. Echter zitten alle applicaties in Tomcat servers die automatisch opstarten tijdens het starten van het image. Dus als er applicaties in de container zitten die je eigenlijk niet nodig hebt, dan gebruiken ze nog wel resources.
De oplossing hiervoor is om de appplicaties in losse Docker containers te draaien. Naast het Shared en Team image is er ook een Tomcat image is met daarin alle Tomcat zaken die voor iedere applicatie nodig zijn. Bovenop het Tomcat image is er een Elrond image en een Gimli image. Beide images kunnen los gestart worden, waardoor de gebruiker zelf kan kiezen welke hij of zij wil gebruiken. Vervolgens kan het Team image gebruikmaken van de verschillende applicatie images.
Dit werkt al aardig, alleen moet je nu een hele set aan images starten. Daarnaast moet je op basis van IP adressen en poorten een verbinding maken vanuit het Team image naar de applicatie images. Ook dat kan makkelijker, namelijk met Docker compose. Hiermee kun je in een bestand definiëren welke images er gestart moeten worden en wat de onderlinge relaties tussen de containers zijn. In In het Docker compose bestand is te zien welke drie omgevingen gebouwd worden. Uiteindelijk zijn ze via de standaard RDP poort 3389 beschikbaar. Vanuit de development environment zijn er links naar de beide applicatie containers. Met het commando ‘docker-compose up’ worden alle beschreven acties in het Docker compose bestand uitgevoerd. Het resultaat is dat je omgeving draait en dat je vanuit je ontwikkelomgeving naar de Gimli applicatieserver kunt verbinden op http://gimli:8080. Dus in plaats van het IP adres kun je de naam gebruiken die in de Docker compose file is gedefinieerd. De verschillende images slaan we op een een private Docker registry (soort van Git voor images) zodat iedereen ze kan gebruiken.
tomcatgimli:
build: TomcatGimli
tomcatelrond:
build: TomcatElrond
developmentenvironment:
build: DevEnv
ports:
– "3389:3389"
links:
– tomcatgimli:gimli
– tomcatelrond:elrond
Nadat de code geschreven is wordt die door de ontwikkelaars in Git opgeslagen. Een commit zorgt ervoor dat een Jenkins build start en de code kwaliteit gecontroleerd wordt met SonarQube. Als de kwaliteit goed genoeg is wordt de artifact (ear/war) opgeslagen in Nexus. Bij ING gebruiken we Nolio om vervolgens iets te deployen op de OTAP straat, bij andere klanten gebeurd dat ook wel met Jenkins of andere software. Applicaties als Jenkins, SonarQube, Git en Nexus/Artifactory worden bij sommige klanten met de hand geïnstalleerd, maar op de meeste plekken gebeurd dat gelukkig geautomatiseerd met bijvoorbeeld Chef/Puppet en/of Docker containers.
Waarom hebben we voor deze omgeving gekozen?
Elke keer als er bij ING nieuwe collega’s kwamen of er omgevingen wijzigden dan koste het veel tijd voor de ontwikkelaars om hun omgeving bij te werken. Daarom wilden we graag een makkelijke manier hebben om ontwikkelomgevingen uit te rollen. We hadden ook kunnen kiezen voor virtual machines, echter zijn die wat lastiger te upgraden en vrij groot. Wel is het zo dat iedere klant en soms zelfs ieder project eigen wensen heeft. Daarom bieden we dit soort oplossingen aan, maar als men liever iets anders wil, dan kan dat natuurlijk.
Voor- en nadelen
De opzet is erg flexibel, je kunt blijven uitbreiden door containers te stapelen of naast elkaar te draaien. De eerste keer kost het wel behoorlijk wat tijd om alles om te zetten naar Docker containers, maar dat kun je ook stapsgewijs doen door in eerste instantie nog wat handmatige acties uit te voeren. Sommige ontwikkelaars willen liever de IDE buiten Docker draaien. Gelukkig kan dat makkelijk en kun je vanuit die IDE verbinden naar de Docker containers. Hierdoor houden ontwikkelaars de vrijheid om hun eigen IDE te kiezen en te draaien waar ze dat willen. Bij sommige hardware ondervonden we helaas wat problemen met de VNC/RDP verbinding. Dus voordat je alles omzet in Docker containers kun je beter eerst even proberen of het goed werkt. Gelukkig ging het bij andere hardware wel goed. Al met al zijn we erg tevreden met de huidige oplossing en het wordt ook door behoorlijk wat ontwikkelaars gebruikt. Hierdoor kunnen nieuwe teamleden nu sneller beginnen en kunnen bestaande omgevingen in Docker snel bijgewerkt worden.
Links
Docker voorbeelden met X11/VNC/RDP: https://bitbucket.org/johanjanssen/dockeride
Docker intro: https://nljug.org/databasejava/docker/
[Auteur: Johan Janssen is Java trainer, architect en competence centre Java lead bij Info Support ]Ontwikkelstraat op basis van OpenShift
Overzicht van de omgeving
Voor het beheren van de verschillende omgevingen wordt gebruikt gemaakt van Docker images. De basis images worden beheerd in een eigen Docker registry. Dit geeft het voordeel dat we onze eigen omgevingen kunnen beheren, met bijvoorbeeld de laatste security patches en ook custom images kunnen maken zoals bijvoorbeeld een combinatie van Jenkins en SonarQube in één image. Ook kunnen we klantspecifieke images maken. Door gebruik te maken van de layering mogelijkheid van Docker images kunnen we basis images bieden met bijvoorbeeld Java 8 en JBoss EAP, die vervolgens door de ontwikkelteams verder uitgebreid kunnen worden. Deze teams kunnen hun eigen images vervolgens weer opslaan in de Docker registry.
Waarom hebben we voor deze omgeving gekozen?
De Redhat OpenShift omgeving maakt het mogelijk om een eigen ontwikkelstraat per project in te richten. Door gebruik te maken van een eigen Docker registry is het toch mogelijk om standaarden af te dwingen en beheer te doen op omgevingen. Zo hoeven de gebruikers zich niet druk te maken om OS versies en security patches en hebben ze toch de vrijheid om hun omgeving zelf in te richten.
Het voordeel van deze opzet is dat de omgevingen portable worden en het eenvoudiger wordt om meerdere omgevingen uit te rollen en te beheren. De Dockerfiles kunnen immers ook gebruikt worden op de lokale omgeving van de ontwikkelaar. Fouten die ontstaan door configuratieverschillen komen op deze manier nagenoeg niet meer voor. Ook het opzetten van een omgeving kost minder tijd en behoeft minder documentatie. In de praktijk maakt dit het eenvoudiger om omgevingen te beheren en wordt het mogelijk om configuratiewijzigingen te volgen in versiebeheer.
Op de lokale omgeving van de ontwikkelaars wordt verder nog Docker compose gebruikt om meerdere containers tegelijk te starten en met elkaar te verbinden. Bijvoorbeeld voor het lokaal draaien een database. Docker compose maakt gebruik van een yml configuratie bestand. In dit bestand worden de verschillende containers gedefinieerd en geconfigureerd. Dit kan door gebruik te maken van een Dockerfile of doormiddel van het specificeren van een image. Het is best-practise om applicatieconfiguratie via omgevingsvariabelen te doen. Binnen de OpenShift omgeving kunnen deze namelijk eenvoudig worden beheerd.
Sogeti images in private docker registry, dockerfiles in versiebeheer |
|
Voor- en nadelen
Het vastleggen van de configuratie van omgevingen in Dockerfiles en deze opnemen in versiebeheer heeft als voordeel dat het opzetten van omgevingen heel eenvoudig wordt. Het is ook mogelijk om wijzigingen terug te halen en verschillen eenvoudig te analyseren, omdat er een audit-trail van de configuratie ontstaat.
Het gebruik van Docker compose op de lokale ontwikkelomgeving van de ontwikkelaar sluit goed aan bij het gebruik van OpenShift in de ontwikkelstraat. Beide omgevingen werken met dezelfde Docker files. Lokaal zorgt Docker compose voor het starten en stoppen van omgevingen. Binnen OpenShift wordt dit door middel van Kubernetes en de Docker strategy build gedaan. Zowel lokaal als binnen OpenShift wordt de configuratie verzorgt door omgevingsvariabelen.
In de praktijk blijkt het draaien van de applicatie in Docker containers tijdens het ontwikkelen niet in alle gevallen de beste optie te zijn. Er is altijd een extra stap nodig voor het deployen van nieuwe code, ten opzichte van het draaien van de applicatie binnen een IDE. Ook het debuggen van applicaties binnen Docker containers vergt extra configuratie. Om via remote debugging de applicatie binnen de container en de Docker VM te kunnen benaderen moeten we gebruik maken van port-forwarding. Hierdoor kiezen ontwikkelaars er toch vaak voor om de applicatie binnen hun eigen IDE draaien. Dit betekent dat er een extra omgeving is die moet worden onderhouden en gedocumenteerd.
Omdat Docker zowel op Windows als op OSX binnen een VM draait lopen ontwikkelaars gedurende hun werk toch tegen kleine problemen aan, de meeste hiervan zijn op te lossen door een herstart van de Docker VM. De Docker omgeving is toch een extra omgeving die beheert moet worden, naast de IDE, eventuele database en middleware.
De keuze om niet gebruik te maken van complete virtuele ontwikkelomgevingen heeft als nadeel dat er niet voor elk project een omgeving is waarmee je direct aan de slag kunt. Omgevingen moeten altijd worden ingericht op de laptop van de ontwikkelaar. Voor ontwikkelaars die aan veel projecten tegelijk werken betekent dit dat er veel omgevingen door elkaar geïnstalleerd zijn met alle bijbehorende tools. Ontwikkelaars hebben vaak veel Java versies, applicatie servers en databases lokaal staan. Dit is een punt waar een VM of remote desktop omgeving veel voordelen kan bieden.
Links
OpenShift build strategieen: https://docs.openshift.org/latest/architecture/core_concepts/builds_and_image_streams.html#docker-build
Erwin de Gier is software architect en trainer bij de business line Open Source van Sogeti.
Ontwikkelen op je eigen omgeving
Bij de productlijn Cloud, Big Data, Internet van de Nationale Politie hebben we niet gekozen voor organisatiebrede opzet van een ontwikkelomgeving, maar voor één die toegespitst is op de producten die wij leveren.
Dit is een bewuste keuze. De Nationale Politie is een grote organisatie met een zeer divers landschap. Hierdoor zou een generieke oplossing te veel concessies moeten doen om alle producten te kunnen bedienen, wat de effectiviteit van een ontwikkelomgeving niet ten goede komt.
Wij streven continu naar een verdere optimalisatie van ons Continuous Delivery proces. Dit doen we door in stappen alles te automatiseren en te standaardiseren. Dit is een complex proces waar we regelmatig van tools wisselen in onze zoektocht.
Het applicatie landschap van de productlijn is gebouwd op basis van een netwerkarchitectuur waar microservices een belangrijk onderdeel in zijn. Vanuit een infrastructuur perspectief is er geen onderscheid tussen een microservice, frontend, database of webserver: alles is ‘gewoon’ een component die draait op een virtuele machine.
Wij werken het meeste aan de microservices en de frontend. Deze worden ontwikkeld met de laatste versies van Java, Spring en de bekende Javascript frameworks. Momenteel ziet onze ontwikkelomgeving voor deze componenten er als volgt uit.
Wij werken met sprints van twee weken. Voordat er een story ‘in sprint’ genomen wordt zijn er een aantal refinement slagen geweest waar de gewenste functionaliteit en de oplossingsrichting besproken wordt.
Dit administreren wij momenteel in Phabricator. Dit is een open source suite van web-based software development tools, vergelijkbaar met de commerciele Atlassian suite.
Voorheen gebruikten we Redmine voor onze project administratie. Die tool voldeed niet aan onze werkwijze en werd uiteindelijk een belemmering. Na een snelle review van wat alternatieven proberen we nu dus Phabricator. Het is vooralsnog een verademing.
Ons bouwproces wordt gedirigeerd door Jenkins. Het opleveren van elk product naar onze centrale repository is in zogenaamde pipelines gedefinieerd. Een pipeline bestaat uit kleinere autonome stappen zoals bouw, test, package, deploy, etc. Zo’n stap wordt een job genoemd. De pipeline wordt getriggerd door een push naar git.
Voor de job definities gebruiken we de ‘pipeline as code’ features van Jenkins 2. Dit heeft een aantal voordelen. Op deze manier is het bouwproces gestandaardiseerd, zijn bouwstappen herbruikbaar over alle builds heen en zijn alle job en pipeline definities onder versiebeheer. Wij hebben de deployment op deze development-omgevingen geautomatiseerd met Rundeck. Dit is de laatste stap in onze Jenkins pipelines.
In een ideale situatie draait de gewenste functionaliteit autonoom in een microservice. Wij streven er daarom naar de backend processen embedded in onze testen te kunnen draaien. Dit lukt niet altijd. In zulk soort gevallen vallen we terug op mock services waartegen de testen kunnen draaien. Deze mock services draaien in een speciale omgeving die Jenkins voor ons opzet zodra we de testen uitvoeren.
Af en toe kiezen we er ook voor om integratie-tests te schrijven die met een ‘productie-like’ omgeving communiceren zodat de effecten van een gewijzigde, afhankelijke, API direct duidelijk zijn.
De virtualisatie techniek is identiek aan onze productie omgevingen. Immers hebben wij al onze infrastructuur geautomatiseerd. Deze scripts halen dan de nieuwe producten op vanuit de centrale repository zodra wij een update nodig achten.
Deze werkwijze zorgt er voor dat je als ontwikkelaar vrij bent om je IDE en OS te kiezen. Een deel ontwikkelt met OSX, een deel met Linux. Deze keuzevrijheid leidt tot kleine variaties in de versies van JDK, maven, Node, e.d. per ontwikkelaar.
Hiermee ontstaat de onvermijdelijke ‘works-on-my-machine’ bug. Dat zien we niet direct als een probleem. Het leidt tot beter begrip over de interne werking van je product, het geeft beter inzicht het gewenste autonome design en het forceert je beter op de standaarden van de taal te focussen.
Verder gebruiken we SonarQube en enkele JavaScript frameworks om de technische kwaliteit van onze producten te bewaken.
De huidige opzet werkt goed. Het bouwproces verloopt over het algemeen vlotjes. Door de vergaande standaardisatie heb je snel overzicht van de verschillende interacties binnen het applicatie landschap. Nieuwe teamleden kunnen snel aan de slag met hun favoriete tools en als ontwikkelaar heb je veel invloed om het buildproces te optimaliseren.
Dat laatste is volgens ons essentieel voor een effectieve ontwikkelomgeving. Uiteindelijk maakt het niet uit welke tool of mooie spulletjes je hebt. Als het niet precies aansluit de gewenste werkwijze van het team, dan leveren deze enkel ruis op een effectieve productiegang.
[Auteurs:
Eelco Meuter <TODO>
Bert Jan Schrijver is software craftsman bij JPoint, momenteel werkzaam bij de Nationale Politie.
Foto: zie eerdere edities van Java Magazine]
Conclusie
Uiteindelijk blijkt dat de ideeën achter alle oplossingen grotendeels vergelijkbaar zijn. Allemaal proberen we zoveel mogelijk te automatiseren om tijd te besparen. Daarnaast is het belangrijk dat de oplossingen flexibel zijn en de teams ondersteunen. Het is niet onze bedoeling om teams oplossingen door de spreekwoordelijke strot te duwen, we willen ze helpen. Helaas zien we ook nog wel eens dat er bijvoorbeeld slechts 1 Jenkins instantie is voor alle teams die niet aanpasbaar is. De ervaring heeft ons echter geleerd dat dat de voortgang van de teams niet helpt. De uiteindelijke implementatie verschilt nog wel iets, maar dat is ook afhankelijk van de al beschikbare kennis en interesses.
Belangrijk in de keuze van het proces en de tools is dat je wilt dat ontwikkelaars zich aan bepaalde standaard houden, dat je het ze zo makkelijk mogelijk moet maken. Vergaande automatisering is hier een krachtig mechanisme voor. Neem als voorbeeld het geval waarbij je als standaard hebt dat het opzetten van een omgeving volledig gedocumenteerd is, zodat een nieuwe ontwikkelaar deze zonder hulp kan inrichten. Om dit af te dwingen is het beter om omgevingen te beschrijven in uitvoerbare configuratiebestanden (zoals bijvoorbeeld Dockerfiles) en deze op te nemen in versiebeheer, dan ontwikkelaars te vragen om een installatiehandleiding op een wiki bij te houden.