Lokal virtualisering
Der er flere attraktive funktionaliter i det beskrevne setup, bl.a. virtualiseret Continuous Integration som beskrives mere detaljeret igennem artiklen. Det beskrevne setup er forholdsvis komplekst, men du behøver ikke bruge det hele for at få glæde af ideerne og værktøjerne.
Hvis du af den ene eller den anden grund ikke kan drage fuld nytte af de muligheder udvikling i Skyen (f.eks. PaaS) giver, kan lokal virtualisering hjælpe dig på vej.
Virtualisering og reproducerbarhed er en væsentlig del af grundlaget for Cloud-teknologiers voldsomme vækst i de senere år. De teknologier, der gør det muligt, er tilgængelige på almindelige laptops i dag. Hvis du gør brug af dem, kan du opnå nogle af de fordele Cloud-baserede udviklings-setup’s har, i udviklingsmiljøet på din udviklermaskine.
De mål der forfølges her, kan opnås ad flere veje. Artiklen er skrevet som inspiration til alternative veje som du kan bruge, hvis det giver mening i din situation.
Som eksempel vil jeg bruge en Java implementation af huskelisten over alle huskelister “Tjello”, hvor data gemmes i en MongoDB. Teamet bruger Eclipse som IDE og gradle som byggeværktøj. Projektet bruger Jenkins som CI og git som repositorie. I dette tilfælde skal udviklere have installeret Eclipse, git, Java og MongoDB i de rigtige versioner og for Eclipse’ vedkommende også den rigtige konfiguration.
Scenarier
Scenarierne nedenfor er velkendte:
- udviklere, der ikke før har arbejdet på projektet, skal arbejde på projektet i en periode. Hver gang skal der bruges tid på at få en udviklingsplatform sat op.
- der skiftes version af databasen, hvorefter alle udviklere skal opdatere deres lokale version.
- udviklerne arbejder også på andre projekter, som desværre har modstridende afhængigheder (f.eks. Java- eller database-version) og derfor skal udviklernes udviklingsmiljø sættes op til at håndtere afhængigheder på tværs af projekter.
- et eller andet holder op med at virke på din udviklingsmaskine, og du bruger timer på at få det til at virke igen.
- CI blev sprunget over, fordi:
- der var ikke en CI maskine tilgængelig.
- corporate policy gjorde det for langsommeligt at lave CI på dette project.
- den eksisterende CI infrastruktur er for langsom.
- den eksisterende CI infrastruktur er ikke tidsvarende.
- CI blev implementeret, men:
- en udvikler kommer til at checke fejlbehæftiget kode ind, og blokerer derved andre.
- den eksisterende CI infrastruktur er for langsom.
- den eksisterende CI infrastruktur er ikke tidsvarende.
Fordele ved lokal virtualisering
Alle de ovenstående scenarier kan imødekommes med lokal virtualisering, hvor alt (OS, Desktop, IDE, værktøjer, kode, konfigurationer, database installationer, Java version) er under versionsstyring og checkes ud og bygges sammen med koden.
Et system som dette har flere fordele, f.eks.:
- Du er ikke afhængig af at eksterne systemer skal være tilgængelige, som f.eks. en database.
- Du har din egen infrastruktur som du kan eksperimentere med uden at påvirke andre.
- Du kan til hver en tid checke et nyt, friskt system ud – all inclusive.
- Din infrastruktur kører på din egen maskine – du skal altså ikke betale for (eller ansøge om) maskinkraft.
- Du er ikke afhængig af større infrastrukturelle investeringer i f.eks. en privat PaaS.
- Du kan versionsstyre din infrastruktur.
Dette eksempel er beskrevet som en samlet enhed, men ideerne fungerer selvstændigt. Det er f.eks. ikke nødvendigt at virtualisere hele sin Desktop for at virtualisere sin udviklingsdatabase eller sit CI setup. Læs denne artikel som et input til, hvordan du kan gøre ting anderledes, og brug det der passer i din virkelighed.
Målet med det setup, der beskrives her, er at:
skabe reproducerbart, versionsstyret udviklingsmiljø. Miljøet har indbygget CI der er centralt konfigureret og tilbyder skalerbart pre-test commit.
Beskrivelserne i resten af denne artikel er alle taget fra et fuldt fungerende system, men er her holdt på et overordnet niveau.
Teknologier
I det følgende vil der blive anvendt en række teknologier og termer, som jeg kort vil beskrive. Alle teknologier har alternativer, som du kan anvende, hvis de passer bedre for dig. Det centrale i artiklen er ideerne, ikke de specifikke værktøjer.
- VirtualBox (Virtualiserings provider). Bruges til at starte virtuelle maskiner på din laptop. Valgt fordi det er open source og tilgængeligt på på Windows, OSX og Linux. Alternativer: VMWare, AWS, Docker, Hyper-V
- Vagrant (Virtualiseringskonfiguration). Bruges til at konfigurere de virtuelle maskiner, f.eks sætte netværk op og installere software. Bl.a. valgt fordi det er Open Source, modent og integrerer med mange virtualiserings providers og virtualiserings provisioners.
- Puppet og shell (Virtualiserings provisioner). Bruges til at installere software på de virtuelle maskiner. Virker i samspil med vagrant. Valgt ved at slå med en terning. Der findes mange andre fine alternativer, såsom Shell, Chef, Salt, CFEngine og Ansible
- Git (Revisionskontrol). Bruges til at gemme (alle versioner af) alle konfigurationer, så alt er reproducerbart og kan deles med mange. Valgt fordi det er mit favorit SCM. Alternativer: mercurial, subversion.
- Jenkins (Continuous Integration). Særligt for dette eksempel bruges Jenkins til at lave pre-test commits, dvs. sikre at fejlbehæftet kode ikke rammer andre udviklere før det er blevet testet. Bruges derudover til “normal” CI. Alternativer : Bamboo, TeamCity.
- Ubuntu (Operativsystem). Bruges til at køre CI og Desktop. Valgt fordi det er open source og derfor kan anvendes af alle uden licensproblemer. Alternativer: Windows, Docker (kan anvendes til en del af de ting der laves her, men ikke alle. Den fulde forklaring er udenfor scope).
Setup
Den mest almindelige anvendelse af et setup som dette er situationen, hvor en ny udvikler for første gang skal arbejde med “Tjello”. Set fra helikopteren vil den nye udvikler checke “Tjello” projektet ud fra git, og derefter bygge udviklingsmiljø med de 3 kommandoer nedenfor og starte med at kode.
git clone [email protected]:tjello.git
cd tjello
vagrant up
“vagrant up” fortæller vagrant, at den skal gøre som beskrevet i vagrants konfigurationsfil, Vagrantfile. Vagrantfile kan være skrevet af dig eller den ansvarlige for projektet. Filen er en specifikation af det miljø, du ville have lavet. Filen er typisk på 50-200 linier, men kan være kortere eller længere afhængig af kompleksiteten i dit setup (i dette tilfælde er Vagrantfile på 101 linier).
I eksemplet beder vi vagrant om at starte 2 virtuelle maskiner:
- et udsnit af den “Vagrantfile” der definerer de 2 virtuelle maskiner
- output fra kommandoen “vagrant up”. Man kan skimte lidt netværksopsætning og navnene på de to maskiner der startes,”ci” og “dev”
- den virtuelle maskine vi lige har lavet, en Ubuntu desktop til udvikling (dev) der kører under VirtualBox. Der er automatisk lavet en bruger med korrekte rettigheder til dig.
- på udviklingsmaskinen er der åbnet en browser, der viser Jenkins (ci). Den viste Jenkins er blevet bygget parallelt med udviklingsmaskinen og er specialiseret til at lave pre-test commit til “Tjello”
Når 1-2-3-4 bliver sat sammen, ser det jo nemt ud, og det er heller ikke så slemt, men hvad sker der egentlig?
Til at starte med skal vi have en maskine med vagrant og virtualbox installeret. Der skal laves en “Vagrantfile” med instruktioner til vagrant (det var den på de 101 linier).
Herefter lægges relevant software på maskinen, i dette tilfælde et udviklingsmiljø for Java, gradle og git. Det software, der skal installeres, beskrives i “Vagrantfile”. Installationen af software kaldes “Provisioning”, og udføres af “Provisioners”. I dette tilfælde bruges Puppet og shell.
De to images, samt alle konfigurations- og kodefiler, er under versionskontrol.
Versionskontrollen har en række kendte fordele: man kan gå frem og tilbage i versioner, man kan dele kode og konfiguration osv. Derudover er der, i dette tilfælde, implementeret en ikke så almindelig (men værdifuld) funktionalitet i konfigurationen af Jenkins.
Når Jenkins boxen konstrueres, hentes Jenkins basis-software, og derefter hentes vores (dvs. din) konfiguration og puttes ned i maven på Jenkins. Den konfiguration jeg har puttet ned i maven på Jenkins, sørger for at holde Jenkins egen konfiguration synkroniseret med den version, der er i versionskontrollen. Det betyder at dem, der har kontrol over “Jenkins configuration” repositoriet centralt, kan styre konfigurationen af alle maskiner.
Lad os antage et team på 10 mand bruger dette setup. Hver mand vil kunne bruge sin lokale maskines regnekraft til at afvikle tests, og administratoren vil kunne bestemme hvilke tests, der skal afvikles. Dette er i praksis et centralt styret, distribueret, virtualiseret CI setup, baseret på eksisterende hardware (udviklernes laptops) og gratis software. Det kan køre på Windows, Mac og Linux.
Man kan overveje at implementere “pre-test commit” som en del af sin CI. Pre-test commit betyder, at udviklere kun deler kode, der har bestået kvalitetskontrollen. Ny kode vil blive kvalitetstestet før andre kan hente det, og en udvikler vil derfor ikke risikere at hente kode, der indeholder fejl (der er fanget af de nuværende tests). I det her beskrevne system er “pre-test commit” implementeret således, at koden testes på udviklerens lokale maskine, før andre udviklere kan hente den til deres maskiner.
Pre-test-commit implementationen hviler på to konventioner:
A) Alle udviklere henter den nyeste kode fra “integration” branchen.
B) Alle udviklere committer til en unik, personlig branch.
Med reference til tallene 1,2 og 3 på billedet, samt detaljer, der ikke er vist på billedet men angivet med bogstaver, vil arbejdsflowet se ud som følger:
1) Udvikler “jwermuth” henter seneste version af koden fra “integration” branchen
1.a) jwermuth koder løs og commiter til sin personlige “jwermuth” branch
2) den virtualiserede Jenkins, der kører på jwermuth’s fysiske maskine, vil teste koden.
2.a) hvis koden ikke består, bliver den ikke pushet til integrations-branchen.
3) Hvis koden er OK, vil Jenkins pushe den til “integration” branchen.
3.a) Udvikler “bendsen” kan nu hente den seneste sikre version af koden.
Opsætningen af den virtuelle CI maskine, vi lige har lavet, sørger for teknikken i baggrunden:
Den centrale konfiguration, som vi har puttet ned i maven på Jenkins, definerer et job til formålet: “pre-test commit”. Hvis man ser lidt nærmere på opstartssituationen, kan man se jobbet i Jenkins jobliste
Enhver, der har skrive-adgang til “jenkins configuration” repositoriet, vil kunne ændre disse jobs, som derefter vil blive distribueret til alle instanser. Forestil dig en situation, hvor du bliver bedt om at lave en ændring i konfigurationen af Jenkins. Det er et år siden du har gjort det – eller du har aldrig gjort det – men uanset hvad, vil du kunne bygge din egen virtuelle CI maskine, der er fuldkommen magen til alle andres, og ændre og teste og dele den nye configuration ved at gøre følgende:
1) skriv kommando “vagrant up”
2) åbne en browser på din lokale maskine (http://localhost:8080) til CI og ændre løs
3) teste dine ændringer
4) logge ind på CI maskinen med “vagrant ssh”
5) committe og pushe dine ændringer til versionstyring
Herefter vil dine ændringer blive synkroniseret med alle kørende virtuelle instanser af CI maskiner.
Konklusion og “findings”
- Man kan i praksis basere et udviklingsteam på en platform som dette. Det vil ikke kræve nogen fælles infrastruktur ud over de anvendte repositorier.
- Man kan i praksis skalere CI med lokal virtualisering. Den valgte CI løsning kan langt fra håndtere alle projekter, men den virker til små projekter.
- Ja, man kunne også have anvendt Docker eller andre virtualiseringsværktøjer. Man kunne f.eks. køre den virtualiserede Jenkins maskine under Docker i stedet for med Virtualbox. Docker er herligt, men det er ikke et af de værktøjer, jeg har valgt at bruge her.
- Det kan være belastende at udvikle i et IDE, der ligger inde i en virtuel maskine. Selvom maskinen kører hurtigt nok, har jeg f.eks. oplevet uenighed mellem host (min laptop) og guest (Ubuntu Desktop) om hvem, der skal reagere på museklik og tastetryk. Man kan også ærgre sig over, at en del af laptoppens resourcer går til virtualiseringen. Det skal hertil siges, at man ville kunne lave et setup hvor selve udviklingsmiljøet (Java, Gradle, …) kører virtualiseret, mens dit IDE kører på din fysiske laptop. Dette fjerner de nævnte performance- og museklik-problemerne.
- Du vil gerne versionsstyre din konfiguration, helt ned til den mindste detalje. Det er superfedt, at kunne checke et helt miljø ud.
- Der er fordele og ulemper ved virtuelle setups. Der er her beskrevet et fuldt setup, men du kan bruge de dele af det, der giver mening i din situation og smide resten væk.
- Der findes i dag et væld af modne virtualiseringsteknologier, som bruges af store succesfulde software virksomheder. Nogle stykker af dem anvendes her, og de virker, og man kan lære at bruge dem på forholdsvis kort tid.