Bij Nucleus hebben we naast een heleboel servers in onze datacenters ook nog een aantal vrij complexe software systemen die we zelf ontwikkelen. Naast hetgeen iedereen ziet zoals ons controlpanel en order wizard zijn er nog een heleboel interne systemen die constant in actie zijn en gebruikt worden door onze medewerkers.

Het is voor ons natuurlijk van groot belang dat deze systemen bugvrij en stabiel zijn, maar ook dat updates transparant en zonder downtime kunnen uitgevoerd worden. Daar komen Continuous integration en deployment voor in actie. In deze blogpost lichten we kort toe wat deze twee technieken zijn en hoe we bepaalde aspecten ervan toepassen in onze dagelijkse workflow bij Nucleus.

Wat is Continuous integration?

Continuous integration is een techniek waarbij je de code van je software bij elke update (of op andere key moments) onderwerpt aan een aantal tests zoals bijvoorbeeld een Unittest suite of Integration tests. Door voor bepaalde belangrijke classes of andere stukken code unittests te schrijven kan je de kwaliteit en robustheid van je code verhogen en meer vertrouwen kweken onder developers (developer confidence).

Continuous integration zorgt ervoor dat de onderdelen waarvoor tests geschreven zijn op regelmatige tijdstippen getest worden zodat ze niet door toedoen van updates aan andere onderdelen stuk gaan. Het is bijvoorbeeld best mogelijk dat door updates aan module A plots module B niet meer werkt. Continuous integration zal dit niet voorkomen maar wel bij de eerste misstap rapporteren (someone broke the build!) zodat er snel op gereageerd kan worden en het goed werken van de vitale systemen kan gegarandeerd worden.

Wat is deployment?

Deployment is het automatiseren van het uitrollen van een update naar bepaalde systemen. Deployment omschrijft het process waarbij je van de code die door developer geschreven is naar een update van je website of project op je staging of production servers gaat. Iedereen onder ons heeft wel eens een vorm van manueel deployment gedaan, wie heeft er vroeger niet ooit een website directory volledig geupload met PHP om bij de volgende update weer alles opnieuw te uploaden? Dat is ook een (al dan niet goede) manier van deployment. Maar wij willen natuurlijk voor de veiligere en robustere manier gaan.

Waarom Bamboo?

Waarom gebruiken wij Bamboo? We gebruiken reeds verschillende tools van Atlassian in onze workflow en zijn daar zeer tevreden over. Zoals Jira en Confluence. De stap naar Bamboo is dan snel gemaakt. We hadden natuurlijk voor een andere oplossing zoals Jenkins, Travis of PHPUnderControl kunnen kiezen maar we verkozen om gebruik te maken van de integratie tussen Bamboo en Jira aangezien deze beiden van Atlassian komen.

Let’s build

Een build configureren in Bamboo begint eerst en vooral bij het maken van een build script. Een build script is een script waarin je op een geautomatiseerde manier van je code een werkend product maakt en dit upload naar je production/staging server(s).

Een build script bestaat meestal uit 2 of meer stappen (of targets), in ons geval testing en deployment. Elk van deze stappen bestaat dan weer uit een aantal kleinere stappen die elk een bepaald doel hebben.

In de testing stap worden alle test suites uitgevoerd en in de deploy stap worden alle stappen ondernomen om je systeem naar de door jou aangeduide servers te verplaatsen en klaar te maken voor gebruik.

Voor het maken van build scripts zijn verschillende systemen beschikbaar. Een aantal populaire oplossingen zijn bijvoorbeeld Apache Ant, Phing en Capistrano. Wij kozen voor Apache Ant, wat zijn oorsprong vindt bij Java. Phing is een alternatief in PHP wat gebaseerd is op Ant qua syntax (xml) en opbouw, dus een goed alternatief indien je liever niet voor een Java gebaseerde oplossing kiest.

Testing

Een robust software product bevat altijd een aantal Unit tests om vitale onderdelen te testen en een aantal User Acceptance tests die de ervaring van de gebruiker testen (bv op falende javascript functionaliteit). Deze test kunnen makkelijk via de commandline aangeroepen worden. In een Ant script is dit het Exec command, dit laat je toe om makkelijk CLI scripts uit te voeren zoals bijvoorbeeld PHPUnit:

<target name="phpunit">
    <exec dir="tests" executable="/usr/bin/phpunit" failonerror="true">
        <arg line="--log-junit ${config.builddir}/phpunit/log/phpunit.xml --coverage-html ${config.builddir}/phpunit/coverage" />
    </exec>
</target>

Qua integratie met Bamboo is hier vooral de –log-junit parameter belangrijk want dit is nodig in Bamboo om de resultaten van je Unittests te kunnen evalueren. Daarnaast genereren we nog een Code Coverage rapport wat later in de Artifacts van de Bamboo build terug te vinden zal zijn. Hiermee kunnen we evalueren hoeveel van onze code door de PhpUnit tests wordt omvat.

Naast de reeds vermelde testsuites worden er in deze stap nog enkele Quality Assurance tools uitgevoerd zoals php copy/paste detector (phpcpd)PHP Mess DetectorPHP_Depend en PHP CodeSniffer. De resultaten hiervan zijn terug te vinden bij elke build, op die manier kan je na het uitvoeren van een build makkelijk de kwaliteit ervan beoordelen.

Deploying

Van je codebase een werkend product maken valt voor PHP projecten over het algemeen wel mee. Er zijn geen ingewikkelde compilaties of bewerkingen nodig. Meestal omvat het deployen volgende stappen:

1. Kopie maken van de code die je wil deployen

Een kopie maken van de code die je wil deployen is vrij simpel als je git gebruikt als Version Control System (VCS). Voor ons project is dat een kwestie van simpelweg één git commando:

    git clone user[at]foobar[dot]git -b develop ./deploy

Dit commando neemt een clone van de develop branch van het foobar project naar de deploy directory. Nu heb je alle code die je nodig hebt om te starten met het uitvoeren van je deploy.

2. Dependencies installeren

Dependencies installeren is tegenwoordig zeer simpel als je PHP Composer gebruikt.

    php composer.phar install --no-dev --optimize-autoloader

3. Database migraties uitvoeren

Wie op voorhand goed nadenkt over de setup van zijn project en welke libraries te gebruiken kan veel tijd besparen met deze stap. Zo gebruiken wij Doctrine als Database Abstraction Layer en ORM. Hierdoor kunnen we gebruik maken van de migrations van Doctrine.

Doctrine biedt standaard een command-line script om je database schema te beheren op basis van de Entities die je in de ORM layer hebt gedefinieërd en daarnaast een specifieke migration library om meer geavanceerde migraties uit te voeren.

Wees echter voorzichtig met het gebruik van het command-line script van de ORM library want verkeerd gebruik kan tot data-loss leiden, de library houdt namelijk geen rekening met de aanwezigheid van data in de tables maar beheert enkel het database schema. Het kan dus perfect zijn dat zomaar een column uit een table verwijderd wordt. Voer daarom altijd eerst je migrations uit op een testing of staging server voor ze los te laten op de production server.

4. Cache clearen en opnieuw opwarmen

Bij updates waarbij bepaalde delen van de cache ongeldig worden is het belangrijk dat je de cache (gedeeltelijk) leeg maakt en opnieuw opvult. Dit is vaak een manueel process. Wij gebruiken bijvoorbeeld een zelf geschreven command-line script om dit te doen. Afhankelijk van het door jou gebruikte framework kan dit makkelijker.

5. Backup maken van de vorige deploy op de target server

Deze stap is niet noodzakelijk, veel hangt af van hoe de volgende stap wordt uitgevoerd maar het is aan te raden om NOOIT de huidige production code te verwijderen bij het deployen. Maak dat je altijd minstens de vorige versie van je production code bewaart. In de volgende stap wordt duidelijk dat dit vrij makkelijk kan.

6. Alles kopieren naar de gewenste server (staging, production, …)

Alles uploaden naar de production kan op verschillende manieren. Je kan bijvoorbeeld via het FTP command van Ant alles naar de server uploaden, SCP of een custom Rsync command zijn echter betere alternatieven.

Om deploys snel en rollbacks makkelijk te maken gebruiken wij een symlink strategy. Dat wil zeggen dat wanneer we een deploy uitvoeren we niet de huidige deploy op de server verwijderen of overschrijven maar gebruik maken van symlinks om de server de juiste versie te laten gebruiken.

Deze stap bestaat uit 2 delen, het eerste deel is het uploaden van de nieuwe versie naar een locatie op de server waar alle versies bewaard worden. Daarna wordt de symlink die naar de vorige versie verwijst gewijzigd zodat deze naar de nieuwe geuploade versie verwijst.

deploy_structure

Op deze manier kan je zeer makkelijk een rollback doen naar de vorige versie (symlink terugzetten naar v1.3) en heb je een backup van de codebase.

In ons geval hebben we nog een shared directory waar files zoals uploads, caches en logs worden bewaard, deze directory wordt ook gesymlinked naar de shared directory in de current map.

/var/www/versions/
/var/www/versions/1.1
/var/www/versions/1.2
/var/www/versions/1.3
/var/www/current -> versions/1.3
/var/www/shared -> current/shared

Zo verlopen deploys bij Nucleus. Indien u hier graag meer uitleg over wil, over deployments & lifecycles of devops in het algemeen, laat ons dan afspreken om er over te praten!

Gerelateerde berichten
hoe voorkom je dat je gehackt wordt

Voorkomen is beter dan genezen: hoe voorkom je dat je gehackt wordt?

Breng alle cyberrisico’s op technisch en juridisch vlak in kaart en neem de passende technische en organisatorische maatregelen. Een overzicht.

Lees meer

communicatie bij een hack

Hoe moet je communiceren als je gehackt bent?

Hoe ga je als bedrijf, groot of klein, met veel of weinig gegevens, best om met de communicatie omtrent een hack en (eventueel) datalek?

Lees meer

welke juridische stappen na hack

Welke juridische maatregelen moet je nemen na een datalek?

In dit artikel willen we zo concreet mogelijk maken welke juridische stappen je moet ondernemen als je bedrijf geconfronteerd wordt met een datalek.

Lees meer