skip to content
Scraplab

Opzet van de blog

/ 7 min read

Table of Contents

Introductie

Al lang heb ik het idee in mijn hoofd gehad om een blog te starten, maar waar begin je dan? Ik heb namelijk veel kleine projecten waar ik het wel leuk zou vinden om iets over te documenteren. De naam scraplab vind ik dan ook passend bij mijn manier van kennis tot mij nemen. Leren door te doen. Er is echter een bekend gezegde van Adam Savage:

Remember kids, the only difference between screwing around and science is writing it down.

Al snel had ik de essentie van een project te pakken; ik wilde een blog waarbij ik zoveel mogelijk kan automatiseren om het schrijven zo min mogelijk in de weg te zitten. Daarnaast moet het framework ook al het zware werk voor mij doen, zodat ik zoveel mogelijk kan focussen op het schrijven. Het moet ruimte bieden voor pagina’s met plaatjes en om kunnen gaan met markdown, iets wat ik mijzelf heb aangeleerd bij het gebruik van Obsidian.

Eigenlijk alles om er maar voor te zorgen dat ik geen excuus heb om een blogpost te schrijven ;)

Vol goede moed begon ik aan mijn deskresearch, op zoek naar een blogging framework. In eerste instantie kwam ik uit op Hugo, maar daar kwam ik er al vrij snel achter dat de hoeveelheid functionaliteit out-of-the-box toch te miniem zou zijn. Na wat verder te hebben gezocht, ben ik uiteindelijk uitgekomen bij Astro.

Astro heeft standaard alles in huis om een strakke blog neer te zetten. Denk aan statische pagina generatie, schrijven in .md files en niet te vergeten - uitstekende documentatie. Na het lezen van de design principles van Astro zag ik al snel dat dit framework goed bij mijn gestelde eisen past. Op de themes pagina van astro kwam ik Astro Cactus tegen, het thema wat je nu ziet. Een simpel thema met een strakke look, perfect voor de scraps op mijn pagina.

Opzet

De tutorial van Astro zelf heeft al heel veel in petto, maar uiteindelijk heb ik na het volgen van de tutorial ervoor gekozen om een eigen weg in te slaan in plaats van gebruik te maken van Netlify. Dit omdat ik al een VPS heb draaien met daarop een werkende reverse-proxy waarop mijn andere services ook draaien.

Reverse proxy Mijn VPS heb ik zo ingericht dat al het binnenkomende verkeer wordt doorgezet naar docker containers die gelabeld zijn met een subdomein van scraplab.nl. Traefik handelt binnenkomende requests op scraplab.nl af en routeert ze naar de juiste docker container. Met een automatisch roterend SSL certificaat via certbot wordt al het verkeer versleuteld en dankzij mijn wildstar DNS record kan ik nu heel makkelijk nieuwe subdomeinen toevoegen aan scraplab.nl. Dit allemaal door simpelweg een nieuwe docker image op te spinnen met een aantal labels. Zo kan ik binnen een handomdraai een nieuw subdomein lanceren.

Docker voor Astro

Dan nu de volgende uitdaging; ik heb Astro lokaal draaiend gekregen en gezien dat het werkt. Nu is het zaak dat ik een Dockerfile bouw die ik vervolgens mijn setup kan gebruiken.

FROM node:lts AS build
WORKDIR /app
COPY . .
RUN npm i
RUN npm run build
FROM httpd:2.4 AS runtime
COPY --from=build /app/dist /usr/local/apache2/htdocs/
EXPOSE 80

Bovenstaande dockerfile werkt als volgt;

  • Hij kopieert de applicatie naar de docker image
  • Hij installeert en bouwt Astro middels npm

Vervolgens maak ik een tweede stage genaamd runtime waarin ik:

  • met een simpele httpd image de buildfiles kopieer naar de plek waar apache ze verwacht.

EXPOSE 80 is puur documentatie om aan te geven welke poort open moet. Poort 80 (aka HTTP) kan ik hier gebruiken aangezien Traefik automatisch al het binnenkomende verkeer omzet naar 80 voor intern gebruik en zodra het naar buiten gaat weer terugzet naar 443 (aka HTTPS) met behulp van het SSL certificaat.

Met multi-stage builds kan ik gebruik maken van de node:lts image zonder dit uiteindelijk in mijn uiteindelijke docker image op te nemen. Dit resulteert dan ook in een klein docker image van 57.42 Mb:

Docker hub informatie, compressed size 57.42 mb image

Github + Docker Hub

Vervolgens wil ik natuurlijk dat dit image gebouwd wordt op het moment dat ik een wijziging heb aangebracht in mijn blog. Dat doe ik het liefste zodra ik iets commit en push in mijn blog repository.

Ik heb nog niet eerder gewerkt met Github Actions, maar maak wel al jarenlang gebruik van Github. Dit was voor mij dan ook een goed moment om mij hierin eens te verdiepen. Github Actions werkt op basis van configuraties die toegepast worden als bepaalde voorwaarden worden voldaan. Die kun je instellen middels het on: object in je configuration.yml bestand.

name: Publish Docker image
on:
push:
branches: ["master"]

Zo zie je hier dat ik een trigger heb geschreven die afwacht totdat er een push plaatsvindt op de master branch.

Vervolgens maak ik dankbaar gebruik van de community, namelijk de github actions die door Docker en Github zelf worden aangeboden:

jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: scraplab/blog
- name: Build and push Docker image
id: push
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

Dit zijn gecureerde stappen die veelvuldig getest zijn, zowel in de repository zelf als door andere afnemers. Ik kan middels deze kleine herbruikbare stappen dus al snel een docker image publishen naar Docker Hub!

Mijn workflow is nu bijna compleet, er mist nog maar een laatste stap: de deploy stap.

Deployen maar!

Natuurlijk wil ik na al deze stappen automatisering ook de deployment automatiseren. Ik wist al langer van de docker image Watchtower maar had er nog geen usecase voor - tot nu.

Watchtower is een docker image die middels de docker socket andere containers letterlijk in de gaten kan houden. Middels environment variabelen en labels op containers kun je aangeven welke containers automatisch geupdate moeten worden.

De standaard configuratie is om alle containers die binnen Docker draaien te updaten naar de laatste versie, maar dat is niet handig voor mijn andere services die hierdoor wellicht omvallen. Denk aan home-assistant of andere gameservers die spontaan beginnen te updaten waardoor ze compatibiliteitsissues krijgen met de clients. Gelukkig hebben de developers van Watchtower hierover nagedacht. Je kan namelijk met WATCHTOWER_LABEL_ENABLE aangeven dat in plaats van opt-out je een opt-in strategy wilt gebruiken.

Zo kun je met com.centurylinklabs.watchtower.enable=true als label op een container aangeven dat Watchtower de container in de gaten moet houden.

Standaard scanned Watchtower elke dag 1 keer of de containers geupdate moeten worden. In het geval van mijn blog wil ik dat het gelijk live staat zodra ik heb gepushed. Om die reden heb ik gekozen voor een interval van 1 minuut.

version: '3.7'
services:
watchtower:
image: containrrr/watchtower:1.7.1
container_name: watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 8080:8080
networks:
- proxy
environment:
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_POLL_INTERVAL=60
networks:
proxy:
external: true

Op basis van Watchtower wordt nu elke minuut gechecked of mijn docker image geupdate is. Als er een nieuwe versie is, dan download Watchtower de laatste versie en start hij de container opnieuw op met dezelfde argumenten. Zo zal mijn server altijd voorzien zijn van de laatste versie van mijn blog.

Conclusie

Succes notificatie op Github Actions

Door Astro te combineren met mijn bestaande server, is het gelukt om in een weekend een blog op te spinnen en een blog te schrijven. Ik hoop hier meerdere posts te gaan maken over projecten, hoe groot of klein ze ook zijn. Het kan zijn dat dit de laatste is voor een tijdje, maar dan heb ik in ieder geval een goede technische basis gelegd in combinatie met een strakke CI/CD pipeline waardoor ik in ieder geval op dat vlak niet geremd kan worden.

// Brian