CI pipeline op Jenkins + Gitlab + AWS

Onbeperkt CI pipelines parallel uitvoeren in GitLab + Jenkins + AWS

GitLab fungeert als een robuust versiebeheersysteem (VCS) met een reeks functionaliteiten voor continue integratie (CI). Echter, het voldeed niet aan de verwachtingen voor een specifiek project, wat resulteerde in ontwikkelaars die hun 'merge request' uitstelden vanwege buitensporig lange wachttijden voor pipelines, wat de 'continue' aard van het werkproces ondermijnde.

Doelstelling

Het doel was om een situatie te creëren waarin meerdere pipelines gelijktijdig konden worden uitgevoerd, elk binnen een tijdelijke instantie gehost op AWS.

Integratie van GitLab en Jenkins

Hoewel GitLab hiervoor enige ondersteuning biedt, ondervond ons team aanzienlijke uitdagingen tijdens de implementatie. Bijvoorbeeld, GitLab's voorgestelde aanpak maakt gebruik van een afgeleide versie van de inmiddels verouderde Docker Machine. Bovendien waren onze vereisten aanzienlijk specifieker.

Jenkins, een al lang bestaande CI/CD-orchestrator, blinkt uit in het beheren van taken over meerdere nodes, hetzij in een dynamisch gegenereerde 'cloud' omgeving, hetzij met statische nodes. Hoewel Jenkins een complexere configuratie vereist dan GitLab CI en een enigszins verouderde interface heeft, bleek het beter geschikt om onze doelen te bereiken.

Prestaties

Mijn uiteindelijke opzet omvat de volgende kenmerken:

  • De mogelijkheid om een vrijwel onbeperkt aantal CI-pipelines gelijktijdig uit te voeren
  • Directe activering van pipelines door samenvoegverzoeken (MR's) in GitLab
  • Weergave van de status van de pipeline binnen de MR, vergelijkbaar met de werkwijze van GitLab-pipelines
  • Toewijzing van specifieke nodes voor de uitvoering van elke pipeline
  • Dynamische creatie van nieuwe nodes door Jenkins wanneer er wachtrijen zijn voor pipeline-taken, en het verwijderen van nodes wanneer ze niet langer nodig zijn
  • Gebruik van 'spot instances' voor nodes, die gebruikmaken van reservecloudbronnen, tijdelijk van aard zijn en kosteneffectiever dan reguliere instanties.

Beheer van Resources door Jenkins

Ik was aangenaam verrast door de mogelijkheden van Jenkins om het beheer van runners te regelen. Dit leidde tot het formuleren van beleidsregels om bronuitputting te voorkomen en relatief 'verse' nodes te behouden:

  • Elke node voert vijf builds uit voordat deze wordt beëindigd, om uitputting van schijfruimte te voorkomen
  • Een enkele node blijft voortdurend actief en staat klaar om een build uit te voeren
  • Er zijn maximaal tien nodes tegelijkertijd actief
  • Een node wordt beëindigd als deze langer dan een uur inactief blijft

Hierdoor zijn tijdens perioden van intensieve ontwikkelingsactiviteit 'warme' nodes direct beschikbaar om CI-pipelines uit te voeren. De vertraging tussen de wachtende en actieve status wordt verminderd, in tegenstelling tot strategieën die nodes alleen lanceren bij het starten van een pipeline.

Bovendien worden de agents (ook wel 'runners' genoemd) gelanceerd met een 'golden AMI' die is voorzien van de vereiste Docker-image pulls. Dit versnelt de uitvoering van het pipeline-script door direct de benodigde Docker-images voor integratietests te leveren, zoals Node.js, Elasticsearch en MySQL 8.

AWS-architectuur

Om deze Jenkins-setup op AWS te realiseren, is het volgende systeem opgezet:

  • Een Jenkins-controller-node, die fungeert als enkelvoudig storingspunt dat operationeel moet blijven. Deze controller-node draait binnen een ECS Fargate-container, wat automatische vervanging bij uitval garandeert. Hierbij is een EFS-bestandssysteem toegevoegd voor gegevensbehoud.
  • Voor agents is een 'golden AMI' voorbereid met benodigde bronnen. Dit omvat het configureren van een starttemplate gekoppeld aan een autoschaalgroep, waardoor geschikte instantieformaten kunnen worden gekozen uit beschikbare spot instances. Jenkins beheert de autoschaalgroep op basis van de bronvereisten.

Integratie van Jenkins met GitLab

Het integreren van onze Jenkins-pipelines met GitLab was een eenvoudig proces, dankzij direct beschikbare Jenkins-plugins voor GitLab en EC2-fleetbeheer.

Optimalisatie R&D

Jenkins biedt een scala aan mogelijkheden voor controle en orchestratie, waaronder de mogelijkheid om:

  • Pipeline-stadia op verschillende nodes uit te voeren
  • Pipeline-stadia parallel uit te voeren
  • Parallelle pipeline-stadia uit te voeren op verschillende nodes

We hebben geëxperimenteerd met het partitioneren van tests en het gelijktijdig uitvoeren ervan op verschillende nodes. Hoewel we succes behaalden, vertaalde dit zich niet in snellere pipeline-uitvoering vanwege verschillende redenen:

  • De noodzaak om toepassings/VCS-bestanden naar andere nodes te kopiëren en resultaten terug te sturen naar het ouderstadium. Dit omvat trage tar- en gzip-acties voordat parallelle stadia beginnen.
  • Toenemende complexiteit in het declaratieve pipeline-script
  • Optreden van CPU- en geheugenuitputting bij het gelijktijdig uitvoeren van meerdere stadia op een enkele machine
  • Wisselende uitvoeringstijden bij het verplaatsen van stadia tussen nodes tijdens gelijktijdige pipeline-uitvoeringen

Deze aanpak zou rendabel kunnen worden wanneer onze pipeline-uitvoeringen/tests langer worden (momenteel ongeveer 20 minuten), en de overhead van opslaan en herstellen minder significant wordt ten opzichte van de duur van een opgesplitste uitvoering.


Met deze post wil ik inzicht geven in de strategieën en gedachten achter mijn DevOps-projecten. Het laat mijn inzet zien om de DevOps best practices te volgen en up-to-date te blijven met technologische vooruitgang.



Datum: Augustus, 2023

Toekomst van Jenkins

Een van de redenen waarom we voor Jenkins hebben gekozen, is dat de oplossing van GitLab gebruikmaakte van een 'fork' van een verouderde applicatie. Dit klinkt niet erg toekomstbestendig. Maar is er toekomst voor Jenkins?

Jenkins is al geruime tijd aanwezig en zal dat ook blijven, omdat het wijdverbreid wordt gebruikt. De gebruikersinterface van Jenkins is vrij ouderwets, maar het doet wat het moet doen. Belangrijker dan de gebruikersinterface is het declaratieve pipeline-script; hiermee zul je het grootste deel van de tijd werken.

Dit script is geschreven in de stijl van Groovy, wat aanvankelijk redelijk leesbaar is, maar de leesbaarheid neemt af naarmate het script complexer wordt. Zoveel accolades om opeenvolgende en parallelle stadia in te kaderen...

Een van de kenmerken waarvan ik denk dat het een verbetering zou zijn (die ergens als een feature request bij Jenkins vermeld staat), is ondersteuning voor op YAML gebaseerde pipelines in plaats van de 'groovy'-stijl. Ik denk dat dit een van de meest significante verbeteringen zou zijn voor Jenkins qua leesbaarheid en ontwerp van pipelines.

Bekijk de Jenkins roadmap

Vaardigheden

  • AWS
  • Containerization
  • AWS EC2
  • Jenkins
  • GitLab CI
  • Docker
  • AWS ECS / Fargate
  • AWS EFS
  • EC2 AMI creation
  • EC2 Launch Templates
  • AWS EC2 AutoScaling groups
  • AWS EC2 Security Groups