In de snelle wereld van softwareontwikkeling is het cruciaal om hoogwaardige applicaties snel en betrouwbaar te leveren. Hier komt CI/CD (Continuous Integration en Continuous Delivery/Deployment) om de hoek kijken.

CI/CD is een set van praktijken en tools die zijn ontworpen om het proces van het integreren van code wijzigingen, het testen ervan en het implementeren naar productie te automatiseren en stroomlijnen. Door CI/CD te omarmen, kan je team handmatige fouten verminderen, de releasecycli versnellen en ervoor zorgen dat je code altijd in een implementeerbare staat is.

In deze tutorial zullen we ons richten op een beginner-vriendelijke aanpak om een basis CI/CD-pijplijn op te zetten met behulp van Bitbucket, een Linux server, en Python met Flask. Specifiek zullen we een geautomatiseerd proces creëren dat de nieuwste wijzigingen van een Bitbucket repository naar je Linux server haalt wanneer er een push of merge naar een specifieke branch plaatsvindt.

Dit proces zal worden aangedreven door Bitbucket webhooks en een eenvoudige Flask-gebaseerde Python server die luistert naar inkomende webhook gebeurtenissen en de implementatie triggert.

Het is belangrijk op te merken dat CI/CD een uitgebreid en complex veld is, en deze tutorial is bedoeld om een fundament begrip te bieden in plaats van een uitputtende handleiding te zijn.

We zullen de basisprincipes behandelen van het opzetten van een CI/CD-pijplijn met tools die toegankelijk zijn voor beginners. Houd er rekening mee dat CI/CD-systemen in de echte wereld vaak meer geavanceerde tools en configuraties omvatten, zoals containerisatie, orchestratie, en multi-stage testomgevingen.

Tegen het einde van deze handleiding heb je een werkend voorbeeld van hoe je implementaties kunt automatiseren met behulp van Bitbucket, Linux en Python, waarop je verder kunt bouwen naarmate je meer vertrouwd raakt met CI/CD-concepten.

Inhoudsopgave:

  1. Waarom is CI/CD Belangrijk?

  2. Stap 1: Stel een Webhook in Bitbucket in

  3. Stap 2: Stel de Flask Listener in op je Linux-server

  4. Stap 3: Maak de Flask App toegankelijk (Optioneel)

  5. Stap 4: Test de Opstelling

  6. Stap 5: Beveiligingsoverwegingen

  7. Afronding

Waarom is CI/CD Belangrijk?

CI/CD is een hoeksteen geworden van de moderne softwareontwikkeling om verschillende redenen. Allereerst versnelt het ontwikkelingsproces. Door repetitieve taken zoals testen en implementatie te automatiseren, kunnen ontwikkelaars zich meer richten op het schrijven van code en minder op handmatige processen. Dit leidt tot een snellere levering van nieuwe functies en bugfixes, wat vooral belangrijk is in competitieve markten waar snelheid het verschil kan maken.

Een ander belangrijk voordeel van CI/CD is verminderde fouten en verbeterde betrouwbaarheid. Geautomatiseerd testen zorgt ervoor dat elke codeverandering grondig wordt gecontroleerd op problemen voordat deze wordt geïntegreerd in de hoofdcode. Dit verkleint het risico op het introduceren van bugs die de toepassing zouden kunnen verstoren of later kostbare oplossingen vereisen. Geautomatiseerde implementatiepijplijnen verminderen ook de kans op menselijke fouten tijdens het releaseproces, waardoor implementaties consistent en voorspelbaar zijn.

CI/CD bevordert ook een betere samenwerking tussen teamleden. In traditionele ontwikkelingsworkflows kan het integreren van codeaanpassingen van meerdere ontwikkelaars een tijdrovend en foutgevoelig proces zijn. Met CI/CD wordt code vaak en frequent geïntegreerd en getest, vaak meerdere keren per dag. Dit betekent dat conflicten vroegtijdig worden gedetecteerd en opgelost, en de codebase in een stabiele staat blijft. Als gevolg hiervan kunnen teams efficiënter werken en met meer vertrouwen, zelfs wanneer meerdere bijdragers tegelijkertijd aan verschillende delen van het project werken.

Tenslotte ondersteunt CI/CD continue verbetering en innovatie. Door het implementatieproces te automatiseren, kunnen teams updates vaker en met minder risico naar productie uitbrengen. Dit stelt hen in staat om sneller feedback van gebruikers te verzamelen en effectiever te itereren op hun producten.

Wat We Zullen Behandelen in Deze Handleiding

In deze handleiding zullen we het proces doorlopen om een eenvoudige CI/CD-pijplijn op te zetten die de implementatie van codeaanpassingen van een Bitbucket-opslagplaats naar een Linux-server automatiseert. Dit is wat je zult leren:

  1. Hoe je een Bitbucket-opslagplaats configureert om webhookmeldingen te verzenden telkens wanneer er een push of merge naar een specifieke tak is.

  2. Hoe je een op Flask gebaseerde Python-server op je Linux-server instelt om te luisteren naar inkomende webhookgebeurtenissen.

  3. Hoe schrijf je een script dat de laatste wijzigingen uit het repository haalt en deze naar de server implementeert.

  4. Hoe test en los je problemen op met je geautomatiseerd implementatieproces.

Tegen het einde van deze tutorial, heb je een werkend voorbeeld van een basis CI/CD-pijplijn die je kunt aanpassen en uitbreiden indien nodig. Laten we beginnen!

Stap 1: Stel een Webhook in Bitbucket in

Voordat je begint met de setup, laten we kort uitleggen wat een webhook is en hoe het past in ons CI/CD-proces.

Een webhook is een mechanisme dat het ene systeem in staat stelt om het andere systeem in real-time op de hoogte te stellen van een gebeurtenis. In de context van Bitbucket kan een webhook geconfigureerd worden om een HTTP-verzoek (vaak een POST-verzoek met payloadgegevens) naar een gespecificeerde URL te sturen telkens wanneer een specifieke gebeurtenis zich voordoet in je repository, zoals een push naar een branch of een samenvoeging van een pull-aanvraag.

In ons geval zal de webhook onze op Flask gebaseerde Python-server (draaiend op je Linux-server) op de hoogte stellen telkens wanneer er een push of samenvoeging naar een specifieke branch plaatsvindt. Deze melding zal een script op de server activeren om automatisch de laatste wijzigingen uit het repository op te halen en deze te implementeren. De webhook fungeert in feite als de brug tussen Bitbucket en je server, waardoor een naadloze automatisering van het implementatieproces mogelijk wordt.

Nu je begrijpt wat de rol van een webhook is, laten we er een instellen in Bitbucket:

  1. Log in op Bitbucket en navigeer naar je repository.

  2. In de linker zijbalk klik je op Instellingen.

  3. Onder de sectie Workflow vind je en klik je op Webhooks.

  4. Klik op de knop Webhooks toevoegen.

  5. Geef een naam op voor je webhook (bijvoorbeeld, “Automatische Pull”).

  6. In het veld URL geef je de URL op van je server waar de webhook het verzoek naartoe zal sturen. Als je een Flask-app lokaal draait, zou dit iets zijn als http://je-server-ip/pull-repo. (Voor productieomgevingen is het sterk aanbevolen om HTTPS te gebruiken om de communicatie tussen Bitbucket en je server te beveiligen.)

  7. In de Triggers-sectie, kies de gebeurtenissen waar je naar wilt luisteren. Voor dit voorbeeld zullen we Push selecteren (en optioneel Pull Request Merged als je ook na merges wilt implementeren).

  8. Sla de webhook op met een zelfverklarende naam zodat het later gemakkelijk te identificeren is.

Zodra de webhook is ingesteld, zal Bitbucket telkens wanneer de geselecteerde gebeurtenis plaatsvindt een POST-verzoek naar de gespecificeerde URL sturen. In de volgende stappen zullen we een Flask-server opzetten om deze inkomende verzoeken te verwerken en het implementatieproces te starten.

Dit is wat je zou moeten zien wanneer je de Bitbucket-webhook instelt

Stap 2: Stel de Flask Listener in op je Linux-server

In de volgende stap, zal je een eenvoudige webserver opzetten op je Linux-machine die luistert naar de webhook van Bitbucket. Wanneer het de melding ontvangt, zal het een git pull uitvoeren of een force pull (in geval van lokale wijzigingen) om het repository bij te werken.

Installeer Flask:

Om de Flask-toepassing te maken, installeer eerst Flask door het volgende commando uit te voeren:

pip install flask

Maak de Flask App:

Maak een nieuw Python-script (bijvoorbeeld, app_repo_pull.py) op je server en voeg de volgende code toe:

from flask import Flask
import subprocess

app = Flask(__name__)

@app.route('/pull-repo', methods=['POST'])
def pull_repo():
    try:
        # Haal de laatste wijzigingen op van het externe repository
        subprocess.run(["git", "-C", "/path/to/your/repository", "fetch"], check=True)
        # Force reset de lokale branch om overeen te komen met de externe 'test' branch
        subprocess.run(["git", "-C", "/path/to/your/repository", "reset", "--hard", "origin/test"], check=True)  # Vervang 'test' door jouw branch naam
        return "Force pull successful", 200
    except subprocess.CalledProcessError:
        return "Failed to force pull the repository", 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Dit is wat deze code doet:

  • subprocess.run(["git", "-C", "/pad/naar/jouw/repository", "fetch"]): Dit commando haalt de laatste wijzigingen op van het externe repository zonder de lokale werkmap te beïnvloeden.

  • subprocess.run(["git", "-C", "/pad/naar/jouw/repository", "reset", "--hard", "origin/test"]): Deze opdracht voert een harde reset uit, waardoor het lokale repository overeenkomt met de externe test-branch. Vervang test door de naam van je branch.

Zorg ervoor dat je /pad/naar/jouw/repository vervangt door het daadwerkelijke pad naar je lokale Git-repository.

Stap 3: De Flask-app openbaar maken (Optioneel)

Als je wilt dat de Flask-app toegankelijk is vanaf buiten je server, moet je deze openbaar maken. Hiervoor kun je een omgekeerde proxy instellen met NGINX. Zo doe je dat:

Installeer eerst NGINX als je dat nog niet hebt gedaan door deze opdracht uit te voeren:

sudo apt-get install nginx

Vervolgens moet je NGINX configureren om verzoeken naar je Flask-app te proxyen. Open het NGINX-configuratiebestand:

sudo nano /etc/nginx/sites-available/default

Wijzig de configuratie om dit blok op te nemen:

server {
    listen 80;
    server_name your-server-ip;

    location /pull-repo {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Vernieuw nu NGINX om de wijzigingen toe te passen:

sudo systemctl reload nginx

Stap 4: Test de configuratie

Nu alles is ingesteld, start de Flask-app door dit Python-script uit te voeren:

python3 app_repo_pull.py

Nu om te testen of alles werkt:

  1. Maak een commit: Push een commit naar de test branch in je Bitbucket repository. Deze actie zal de webhook triggeren.
  1. Webhook trigger: De webhook zal een POST-verzoek naar je server sturen. De Flask-applicatie zal dit verzoek ontvangen, een force pull uitvoeren van de test branch, en het lokale repository bijwerken.

  2. Verifieer de pull: Controleer de log-uitvoer van je Flask-applicatie of inspecteer het lokale repository om te verifiëren dat de veranderingen succesvol zijn binnengehaald en toegepast.

Stap 5: Beveiligingsoverwegingen

Wanneer je een Flask-applicatie blootstelt aan het internet, is het essentieel om je server en applicatie te beveiligen om deze te beschermen tegen ongeautoriseerde toegang, datalekken en aanvallen. Hier zijn de belangrijkste aandachtspunten:

1. Gebruik een Beveiligde Server met Juiste Firewall Regels

Een beveiligde server is geconfigureerd om de blootstelling aan externe bedreigingen te minimaliseren. Dit omvat het gebruik van firewall regels, het minimaliseren van onnodige services en ervoor zorgen dat alleen vereiste poorten openstaan voor communicatie.

Voorbeeld van een beveiligde serverconfiguratie:
  • Minimale software: Installeer alleen de software die je nodig hebt (bijvoorbeeld Python, Flask, NGINX) en verwijder onnodige services.

  • Besturingssysteem updates: Zorg ervoor dat het besturingssysteem van je server up-to-date is met de nieuwste beveiligingspatches.

  • Firewall configuratie: Gebruik een firewall om inkomend en uitgaand verkeer te controleren en de toegang tot je server te beperken.

Bijvoorbeeld, een basis UFW (Uncomplicated Firewall) configuratie op Ubuntu zou er zo uit kunnen zien:

# Sta SSH (poort 22) toe voor externe toegang
sudo ufw allow ssh

# Sta HTTP (poort 80) en HTTPS (poort 443) toe voor webverkeer
sudo ufw allow http
sudo ufw allow https

# Schakel de firewall in
sudo ufw enable

# Controleer de status van de firewall
sudo ufw status

In dit geval:

  • De firewall staat inkomende SSH-verbindingen toe op poort 22, HTTP op poort 80 en HTTPS op poort 443.

  • Alle onnodige poorten of services moeten standaard geblokkeerd worden om de blootstelling aan aanvallen te beperken.

Aanvullende Firewallregels:
  • Beperk toegang tot webhook-eindpunt: Idealiter alleen verkeer naar het webhook-eindpunt toestaan vanaf de IP-adressen van Bitbucket om externe toegang te voorkomen. Dit kan worden ingesteld in uw firewall of met behulp van uw webserver (bijvoorbeeld NGINX) door alleen verzoeken vanuit het IP-bereik van Bitbucket te accepteren.

  • Weiger al het andere inkomende verkeer: Zorg ervoor dat poorten voor services die niet blootgesteld hoeven te worden aan het internet (bijvoorbeeld databasepoorten) geblokkeerd zijn.

2. Voeg Authenticatie toe aan de Flask App

Aangezien uw Flask-app publiekelijk toegankelijk zal zijn via de webhook-URL, dient u overweging te nemen om authenticatie toe te voegen om ervoor te zorgen dat alleen geautoriseerde gebruikers (zoals de servers van Bitbucket) de pull kunnen activeren.

Voorbeeld van Basisauthenticatie:

Je kunt een eenvoudige token-gebaseerde authenticatie gebruiken om je webhook-eindpunt te beveiligen. Hier is een voorbeeld van hoe je je Flask-app kunt aanpassen om een authenticatietoken te vereisen:

from flask import Flask, request, abort
import subprocess

app = Flask(__name__)

# Definieer een geheim token voor webhook-verificatie
SECRET_TOKEN = 'your-secret-token'

@app.route('/pull-repo', methods=['POST'])
def pull_repo():
    # Controleer of het verzoek het juiste token bevat
    token = request.headers.get('X-Hub-Signature')
    if token != SECRET_TOKEN:
        abort(403)  # Verboden als het token onjuist is

    try:
        subprocess.run(["git", "-C", "/path/to/your/repository", "fetch"], check=True)
        subprocess.run(["git", "-C", "/path/to/your/repository", "reset", "--hard", "origin/test"], check=True)
        return "Force pull successful", 200
    except subprocess.CalledProcessError:
        return "Failed to force pull the repository", 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
Hoe het werkt:
  • De X-Hub-Signature is een aangepaste header die je toevoegt aan het verzoek bij het instellen van de webhook in Bitbucket.

  • Alleen verzoeken met het juiste token zullen toestemming krijgen om de pull te activeren. Als het token ontbreekt of onjuist is, wordt het verzoek afgewezen met een 403 Forbidden reactie.

Je kunt ook meer complexe vormen van authenticatie gebruiken, zoals OAuth of HMAC (Hash-based Message Authentication Code), maar deze eenvoudige tokenbenadering werkt voor veel gevallen.

3. Gebruik HTTPS voor Veilige Communicatie

Het is cruciaal om de gegevens die worden verzonden tussen je Flask-app en de Bitbucket-webhook te versleutelen, evenals alle gevoelige gegevens (zoals tokens of wachtwoorden) die via het netwerk worden verzonden. Dit zorgt ervoor dat aanvallers de gegevens niet kunnen onderscheppen of wijzigen.

Waarom HTTPS?
  • Data-encryptie: HTTPS versleutelt de communicatie, waardoor gevoelige gegevens zoals uw verificatietoken niet blootgesteld worden aan man-in-the-middle aanvallen.

  • Vertrouwen en integriteit: HTTPS zorgt ervoor dat de gegevens die door uw server ontvangen worden niet zijn gemanipuleerd.

Het beveiligen van uw Flask-app met SSL via Let’s Encrypt:
  1. Installeer Certbot (de tool voor het verkrijgen van Let’s Encrypt-certificaten):
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx

Verkrijg een gratis SSL-certificaat voor uw domein:

sudo certbot --nginx -d your-domain.com
  • Met dit commando wordt Nginx automatisch geconfigureerd om HTTPS te gebruiken met een gratis SSL-certificaat van Let’s Encrypt.

  • Zorg ervoor dat HTTPS wordt gebruikt: Zorg ervoor dat uw Flask-applicatie of Nginx-configuratie al het verkeer dwingt om HTTPS te gebruiken. Dit kunt u doen door een omleidingsregel in te stellen in Nginx:

server {
    listen 80;
    server_name your-domain.com;

    # Doorsturen van HTTP naar HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # Andere Nginx-configuratie...
}

Automatische Vernieuwing: Let’s Encrypt-certificaten zijn geldig voor 90 dagen, dus het is belangrijk om automatische vernieuwing in te stellen:

sudo certbot renew --dry-run

Deze opdracht test het vernieuwingsproces om ervoor te zorgen dat alles werkt.

4. Logging en Monitoring

Implementeer logging en monitoring voor uw Flask-app om eventuele ongeautoriseerde pogingen, fouten of ongebruikelijke activiteiten bij te houden:

  • Log verzoeken: Log alle inkomende verzoeken, inclusief het IP-adres, verzoekheaders en responsstatus, zodat u verdachte activiteiten kunt controleren.

  • Gebruik monitoringtools: Stel tools zoals Prometheus, Grafana of New Relic in om serverprestaties en app-gezondheid te monitoren.

Samenvatting

In deze zelfstudie hebben we onderzocht hoe u een eenvoudige, beginner-vriendelijke CI/CD-pijplijn opzet die implementaties automatiseert met behulp van Bitbucket, een Linux-server en Python met Flask. Hier is een samenvatting van wat u hebt geleerd:

  1. CI/CD Fundamentals: We hebben de basisprincipes van Continue Integratie (CI) en Continue Levering/Implementatie (CD) besproken, die essentiële praktijken zijn voor het automatiseren van de integratie, het testen en de implementatie van code. Je hebt geleerd hoe CI/CD helpt bij het versnellen van de ontwikkeling, het verminderen van fouten en het verbeteren van de samenwerking tussen ontwikkelaars.

  2. Instellen van Bitbucket Webhooks: Je hebt geleerd hoe je een Bitbucket webhook kunt configureren om je server op de hoogte te stellen telkens wanneer er een push of merge is naar een specifieke branch. Deze webhook dient als een trigger om het implementatieproces automatisch te starten.

  3. Creëren van een op Flask gebaseerde Webhook Listener: We hebben je laten zien hoe je een Flask-app op je Linux-server kunt opzetten om te luisteren naar inkomende webhookverzoeken van Bitbucket. Deze Flask-app ontvangt de meldingen en voert de benodigde Git-commando’s uit om de nieuwste wijzigingen op te halen en te implementeren.

  4. Het automatiseren van het implementatieproces: Met behulp van Python en Flask hebben we het proces geautomatiseerd om wijzigingen uit het Bitbucket-repository op te halen en een force pull uit te voeren om ervoor te zorgen dat de laatste code wordt geïmplementeerd. Je hebt ook geleerd hoe je de server configureert om de Flask-app bloot te stellen en verzoeken veilig te accepteren.

  5. Beveiligingsoverwegingen: We hebben kritieke beveiligingsstappen behandeld om je implementatieproces te beschermen:

    • Firewallregels: We hebben besproken hoe je firewallregels kunt configureren om blootstelling te beperken en ervoor te zorgen dat alleen geautoriseerd verkeer (van Bitbucket) toegang heeft tot je server.

    • Authenticatie: We hebben op token gebaseerde authenticatie toegevoegd om ervoor te zorgen dat alleen geautoriseerde verzoeken implementaties kunnen triggeren.

    • HTTPS: We hebben uitgelegd hoe je de communicatie tussen je server en Bitbucket kunt beveiligen met SSL-certificaten van Let’s Encrypt.

    • Loggen en monitoren: Tot slot hebben we aanbevolen om loggen en monitoren in te stellen om eventuele ongebruikelijke activiteiten of fouten bij te houden.

Volgende stappen

Tegen het einde van deze tutorial heb je nu een werkend voorbeeld van een geautomatiseerde implementatiepijplijn. Hoewel dit een basisimplementatie is, dient het als een fundament waarop je kunt voortbouwen. Naarmate je meer vertrouwd raakt met CI/CD, kun je geavanceerde onderwerpen verkennen zoals:

  • Implementatiepijplijnen met meerdere stappen

  • Integratie met containerisatietools zoals Docker

  • Complexere test- en implementatiestrategieën

  • Gebruik van orchestratietools zoals Kubernetes voor schaling

CI/CD-praktijken evolueren voortdurend, en door de basisprincipes onder de knie te krijgen, heb je jezelf klaargestoomd voor succes bij het uitbreiden van je vaardigheden op dit gebied. Veel succes met automatiseren en bedankt voor het lezen!

Je kunt de code hier fork-en.