Export Alle Cheat Sheets

Hieronder staan alle cheat sheets achter elkaar. Klik op de knop om ze als één groot PDF-boek op te slaan.

DevOps

Docker Basics

Commando's en configuratie voor de Nginx oefening.

Stap 1: Container Starten

docker pull nginx
Download de officiële Nginx image
docker run -d -p 80:80 --name devops-nginx nginx
Start container (poort 80->80, naam 'devops-nginx')

Stap 2: Aanpassen

docker exec -it devops-nginx bash
Open een interactieve shell in de container
apt-get update && apt-get install -y nano
Update packages en installeer nano editor (in container)
nano /usr/share/nginx/html/index.html
Bewerk index.html en voeg de tekst 'Ik kan docker!' toe
exit
Verlaat de container shell en keer terug naar de host

Stap 3: Opslaan

docker commit devops-nginx devops-image
Maak een nieuwe image van de gewijzigde container
docker save -o ~/mijnimage.tar devops-image
Exporteer de image naar een .tar bestand in je home directory
DevOps

Examen URL Shortener

Deploy een NodeJS API en Nginx Frontend met Redis storage achter een Traefik v3 reverse proxy.

Stap 1: Traefik (Infrastructure)

mkdir -p ~/traefik && cd ~/traefik
Maak de map voor de reverse proxy aan
docker network create traefik
Maak het gedeelde netwerk aan voor de proxy
nano docker-compose.yml
Maak de Traefik configuratie aan (zie Files voor inhoud)
docker compose up -d
Start de Traefik reverse proxy

Stap 2: Dockerfiles Bouwen

cd ~/examen-url-shortener/backend
Ga naar de backend map
nano Dockerfile
Maak de Dockerfile aan voor de backend (zie Files)
cd ~/examen-url-shortener/frontend
Ga naar de frontend map
nano Dockerfile
Maak de Dockerfile aan voor de frontend (zie Files)

Stap 3: Applicatie Opzetten

cd ~/examen-url-shortener
Ga terug naar de root van het project
nano docker-compose.yml
Maak de main compose file aan (zie Files)
nano .env
Maak het secrets bestand aan (zie Files)

Stap 4: Starten & Testen

docker compose up -d
Start de applicatie (API + Frontend + Redis)
docker compose down -v && docker compose up -d --build
Gebruik dit als je wijzigingen maakt aan Dockerfiles of config

Bestanden

backend/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]
De NodeJS backend (Port 4000)
frontend/Dockerfiledockerfile
FROM nginx:1.25
COPY . /usr/share/nginx/html
De Nginx frontend
~/traefik/docker-compose.ymlyaml
services:
traefik:
image: traefik:v3.6.7
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`{{domain-2}}`)"
- "traefik.http.routers.dashboard.service=api@internal"
networks:
traefik:
external: true
De Reverse Proxy configuratie
~/examen-url-shortener/docker-compose.ymlyaml
services:
backend:
build: ./backend
env_file: .env
networks:
- traefik
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.backend.rule=Host(`{{domain-1}}`) && PathPrefix(`/api`)"
- "traefik.http.services.backend.loadbalancer.server.port=4000"
frontend:
build: ./frontend
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`{{domain-1}}`)"
redis:
image: redis:7
volumes:
- redis-data:/data
networks:
- backend
volumes:
redis-data:
networks:
traefik:
external: true
backend:
De hoofdapplicatie (Services + Redis)
~/examen-url-shortener/.envproperties
STORAGE=redis
REDIS_HOST=redis
REDIS_PORT=6379
De hoofdapplicatie .env
DevOps

Examen Blog

Full stack met NodeJS, Postgres, en beveiligde Admin via Traefik.

Stap 1: Traefik Setup

mkdir ~/traefik && cd ~/traefik
Maak de map voor de proxy
docker network create traefik
Maak het publieke netwerk
nano docker-compose.yml
Maak de config (zie Files sectie)
docker compose up -d
Start de proxy server

Stap 2: Dockerfiles

cd ~/examen-blog/backend
Ga naar de backend map
nano Dockerfile
Maak de API Dockerfile (zie Files: API Dockerfile)
cd ../frontend
Ga naar de frontend map
nano Dockerfile
Maak de Frontend Dockerfile (zie Files: Frontend Dockerfile)

Stap 3: Authenticatie & Config

sudo apt-get install apache2-utils
Installeer htpasswd tool (indien niet aanwezig)
htpasswd -nb bloemkool SuperLekkerEten!
Genereer de hash voor Traefik labels
nano .env
Maak het environment bestand aan

Stap 4: Starten

docker compose up -d --build
Bouw images en start alles op
docker compose logs -f api
Debug: Check database connectie

Bestanden

api/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8000
CMD ["node", "index.js"]
Backend luistert op poort 8000
frontend/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]
Frontend luistert op poort 4000
~/traefik/docker-compose.ymlyaml
services:
traefik:
image: traefik:v3.6.7
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`{{domain-2}}`)"
- "traefik.http.routers.dashboard.service=api@internal"
networks:
traefik:
external: true
De infrastructuur layer (Traefik v3)
~/examen-blog/docker-compose.ymlyaml
services:
# --- Backend API ---
backend:
build: ./backend
networks:
- traefik
- backend
env_file: .env
labels:
- "traefik.enable=true"
- "traefik.http.routers.backend.rule=Host(`{{domain-1}}`) && PathPrefix(`/api`)"
- "traefik.http.services.backend.loadbalancer.server.port=8000"
- "traefik.docker.network=traefik"
# --- Frontend (Node EJS) ---
frontend:
build: ./frontend
networks:
- traefik
- backend
environment:
- API_URL=http://backend:8000
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`{{domain-1}}`)"
- "traefik.http.services.frontend.loadbalancer.server.port=4000"
- "traefik.docker.network=traefik"
# --- Admin (Static Nginx) ---
admin:
image: nginx:1.25
volumes:
# Mount ./admin naar /usr/share/nginx/html/admin
# Hierdoor werkt de URL /admin vanzelf zonder StripPrefix problemen
- ./admin:/usr/share/nginx/html/admin
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.admin.rule=Host(`{{domain-1}}`) && PathPrefix(`/admin`)"
# Pas aan met output van: htpasswd -nb bloemkool SuperLekkerEten!
- "traefik.http.middlewares.admin-auth.basicauth.users=bloemkool:$$apr1$$..."
- "traefik.http.routers.admin.middlewares=admin-auth"
# --- Database ---
db:
image: postgres:12.22
volumes:
- postgres-data:/var/lib/postgresql/data
- ./postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql
networks:
- backend
env_file: .env
volumes:
postgres-data:
networks:
traefik:
external: true
backend:
De applicatie configuratie. Let op: postgres-init.sql moet actief zijn.
~/examen-blog/.envproperties
STORAGE=postgres
POSTGRES_HOST=db
POSTGRES_USER=postgres
POSTGRES_PWD=mysecret
POSTGRES_DB=blog
Environment variabelen.
DevOps

Examen Todo

NodeJS API (poort 4000), Statische Nginx Frontend & MongoDB achter Traefik v3.

Stap 1: Traefik (Infrastructure)

mkdir ~/traefik && cd ~/traefik
Maak de map aan
docker network create traefik
Maak het publieke netwerk
nano docker-compose.yml
Maak de Traefik config (zie Files)
docker compose up -d
Start de proxy server

Stap 2: Dockerfiles Bouwen

cd ~/examen-todo/backend
Ga naar de backend map
nano Dockerfile
Maak de API Dockerfile (Node 20, Poort 4000)
cd cd ~/examen-todo/frontend
Ga naar de frontend map
nano Dockerfile
Maak de Frontend Dockerfile (Nginx 1.25)

Stap 3: Configureren

cd ~/examen-todo
Terug naar project root
nano docker-compose.yml
Maak de applicatie stack aan (zie Files)
nano .env
Maak het secrets bestand aan

Stap 4: Starten & Testen

docker compose up -d --build
Bouw en start de applicatie
docker compose logs -f api
Check of API verbindt met MongoDB

Bestanden

api/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]
Backend image. Let op EXPOSE 4000!
frontend/Dockerfiledockerfile
FROM nginx:1.25
COPY . /usr/share/nginx/html
Statische frontend (Nginx)
~/examen-todo/.envproperties
STORAGE=mongodbMONGODB_HOST=db
MONGODB_DB=examen
MONGODB_USER=examen
MONGODB_PWD=examenwachtwoord
Environment variabelen voor de API connectie
~/traefik/docker-compose.ymlyaml
services:
traefik:
image: traefik:v3.6.7
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`{{domain-2}}`)"
- "traefik.http.routers.dashboard.service=api@internal"
networks:
traefik:
external: true
De Reverse Proxy setup (Traefik v3)
~/examen-todo/docker-compose.ymlyaml
services:
backend:
build: ./backend
env_file: .env
networks:
- traefik
- backend
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.backend.rule=Host(`{{domain-1}}`) && PathPrefix(`/api`)"
- "traefik.http.services.backend.loadbalancer.server.port=4000"
frontend:
build: ./frontend
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.frontend.rule=Host(`{{domain-1}}`)"
db:
image: mongo:4.4.6
environment:
- MONGO_INITDB_DATABASE=examen
volumes:
- mongodb-data:/data/db
# Zorg dat dit bestand bestaat in je map (uit de opdracht files)
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
networks:
- backend
volumes:
mongodb-data:
networks:
gateway:
external: true
backend:
De applicatie stack. Let op de 'mongo-init.js' mount!
DevOps

Voorbeeldexamen - ChatBox

MongoDB (4.4), Node API (poort 3000) en Nginx Frontend.

Stap 1: Traefik (Infrastructure)

mkdir ~/traefik && cd ~/traefik
Maak de map voor de proxy
docker network create traefik
Maak het publieke netwerk
nano docker-compose.yml
Maak de config (zie Files)
docker compose up -d
Start Traefik (v3.6.7)

Stap 2: Dockerfiles

cd chatAPI
Ga naar de backend map
nano Dockerfile
Maak API image (Node 20, Poort 3000)
cd ../frontend
Ga naar de frontend map
nano Dockerfile
Maak Frontend image (Nginx)

Stap 3: Applicatie Config

cd ..
Ga terug naar de root van het project
nano docker-compose.yml
Maak de stack aan (zie Files)
MONGO_URL=mongodb
Let op: Service naam moet 'mongodb' zijn!

Stap 4: Starten

docker compose up -d --build
Start de ChatBox
docker compose logs -f api
Debug database connectie

Bestanden

chatAPI/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
Backend (Let op: Poort 3000)
frontend/Dockerfiledockerfile
FROM nginx
COPY . /usr/share/nginx/html
Frontend (Nginx)
~/traefik/docker-compose.ymlyaml
services:
traefik:
image: traefik:v3.6.7
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`{{domain-2}}`)"
- "traefik.http.routers.dashboard.service=api@internal"
networks:
traefik:
external: true
Traefik v3 configuratie
docker-compose.yml (Project Root)yaml
services:
chatapi:
build: ./chatAPI
networks:
- traefik
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.backend.rule=Host(`{{domain-1}}`) && PathPrefix(`/api`)"
- "traefik.http.services.backend.loadbalancer.server.port=3000"
- "traefik.docker.network=traefik"
environment:
- MONGO_USER=${MONGO_USER}
- MONGO_PWD=${MONGO_PWD}
- MONGO_URL=${MONGO_URL}
frontend:
build: ./frontend
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`{{domain-1}}`)"
- "traefik.docker.network=traefik"
mongodb:
image: mongo:4.4
volumes:
- mongo-data:/data/db
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
networks:
- backend
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PWD}
volumes:
mongo-data:
networks:
traefik:
external: true
backend:
De applicatie stack. Let op service naam 'mongodb', environment variables en Mongo versie 4.4.
.env (Project Root)yaml
MONGO_USER=admin
MONGO_PWD=S3cret
MONGO_URL=mongodb
De applicatie .env.
DevOps

Examen - Distributed Telemetry Archival Platform (DTAP™)

NodeJS (Poort 4000), PostgreSQL (12.22), InfluxDB (2.7) en Nginx Frontend achter Traefik.

Stap 1: Traefik (Infrastructure)

mkdir ~/traefik && cd ~/traefik
Maak de map voor de proxy
docker network create traefik
Maak het publieke netwerk aan (indien nog niet bestaat)
nano docker-compose.yml
Maak de Traefik config (zie Files)
docker compose up -d
Start Traefik (v3.6.7)

Stap 2: Dockerfiles

cd ~/examen-telemetry-platform
Ga naar de projectmap
nano api/Dockerfile
API Image: Node 20-bullseye, Poort 4000
nano frontend/Dockerfile
Frontend Image: Nginx 1.25

Stap 3: Applicatie Config & Data

nano .env
Definieer secrets (Postgres & Influx Credentials)
nano docker-compose.yml
Definieer services: api, frontend, postgres, influxdb
INFLUX_TOKEN=...
Tip: Zet een vast token in de Influx setup vars!

Stap 4: Starten & Verifiëren

docker compose up -d --build
Start de stack
docker compose logs -f api
Check of API connectie maakt met beide DB's
docker compose ps
Controleer of alles 'Up' is

Bestanden

api/Dockerfiledockerfile
FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]
Backend (NodeJS)
frontend/Dockerfiledockerfile
FROM nginx:1.25
COPY . /usr/share/nginx/html
Frontend (Nginx)
~/traefik/docker-compose.ymlyaml
services:
traefik:
image: traefik:v3.6.7
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`{{domain-2}}`)"
- "traefik.http.routers.dashboard.service=api@internal"
networks:
traefik:
external: true
Traefik Proxy Configuratie
.env (Project Root)properties
# Postgres Config
POSTGRES_USER=myuser
POSTGRES_PWD=mypassword
POSTGRES_DB=telemetry
# InfluxDB Config
INFLUX_USER=admin
INFLUX_PWD=adminpassword123
INFLUX_ORG=helios
INFLUX_BUCKET=metrics
INFLUX_TOKEN=my-super-secret-admin-token
Secrets voor de applicatie. Zorg dat API en DB variabelen matchen.
docker-compose.yml (Project Root)yaml
services:
api:
build: ./api
networks:
- traefik
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`{{domain-1}}`) && PathPrefix(`/api`)"
- "traefik.http.services.api.loadbalancer.server.port=4000"
- "traefik.docker.network=traefik"
environment:
- STORAGE_MODE=telemetry
- POSTGRES_HOST=postgres
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PWD=${POSTGRES_PWD}
- INFLUX_URL=http://influxdb:8086
- INFLUX_TOKEN=${INFLUX_TOKEN}
- INFLUX_ORG=${INFLUX_ORG}
- INFLUX_BUCKET=${INFLUX_BUCKET}
depends_on:
- postgres
- influxdb
frontend:
build: ./frontend
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`{{domain-1}}`)"
- "traefik.docker.network=traefik"
postgres:
image: postgres:12.22
volumes:
- postgres-data:/var/lib/postgresql/data
- ./postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql:ro
networks:
- backend
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PWD}
influxdb:
image: influxdb:2.7
volumes:
- influx-data:/var/lib/influxdb2
networks:
- backend
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=${INFLUX_USER}
- DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUX_PWD}
- DOCKER_INFLUXDB_INIT_ORG=${INFLUX_ORG}
- DOCKER_INFLUXDB_INIT_BUCKET=${INFLUX_BUCKET}
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=${INFLUX_TOKEN}
volumes:
postgres-data:
influx-data:
networks:
traefik:
external: true
backend:
De volledige stack configuratie. Let op InfluxDB 2.7 init variabelen.
ICT Architecture

Labo 01: Docker Swarm Mode Basics

Basiscommando's voor het opzetten van een Docker Swarm cluster, deployen van services en stacks.

Cluster Opzetten

docker swarm init --advertise-addr <IP-ADRES-MANAGER>
Initialiseer een nieuwe Swarm op de manager node.
docker swarm join-token worker
Toon het commando en token om workers toe te voegen.
docker swarm join --token <TOKEN> <IP-ADRES-MANAGER>:2377
Voeg een node toe als worker aan de Swarm.
docker node ls
Toon alle nodes in de Swarm (enkel via manager).

Service Beheer

docker service create --name echo-service --replicas 2 -p 8080:5678 hashicorp/http-echo -text="hello world"
Maak een nieuwe service met 2 replica's.
docker service ps echo-service
Bekijk op welke nodes de replica's draaien.
docker service logs echo-service
Bekijk de geaggregeerde logs van de service.
docker service update --args "-text=\"updated world\"" echo-service
Voer een rolling update uit op de service.

Node Beheer (Draining)

docker node update --availability drain <NODE_ID_OF_HOSTNAME>
Zet een node op 'drain' (verplaatst containers naar andere nodes).

Stack Deployments

docker stack deploy -c stack.yaml labo1-stack
Deploy of update een stack via een Compose file.
docker stack ls
Toon alle actieve stacks.
docker stack ps labo1-stack
Toon de status van alle services in een stack.
docker stack rm labo1-stack
Verwijder een stack en alle bijhorende services.

Bestanden

stack.yamlyaml
version: '3.8'
services:
web:
image: flask-redis-app:latest
ports:
- "5000:5000"
deploy:
replicas: 2
placement:
constraints:
- node.role == worker
update_config:
parallelism: 1
delay: 30s
depends_on:
- redis
redis:
image: redis:alpine
deploy:
placement:
constraints:
- node.role == worker
Docker Swarm Stack file voor Flask + Redis app met constraints en update config.
ICT Architecture

Labo 02: Vervolg Docker Swarm

Werken met Docker Configs, Secrets en volumes in Swarm Mode.

Configs & Secrets

docker config create mysql_init_script init.sql
Maak een Docker config aan vanuit een lokaal bestand.
docker service update --config-rm mysql_init_script --config-add source=mysql_init_script_v2,target=/docker-entrypoint-initdb.d/init.sql mysql-service
Update een service om een nieuwe config te gebruiken (configs zijn immutable).
echo "mijn_wachtwoord" | docker secret create mysql_root_password -
Maak een Docker secret aan.

Schalen & Herstel

docker rm -f <CONTAINER_ID>
Forceer het verwijderen van een container om Swarm's self-healing te testen.
docker service scale mysql-secure=2
Schaal een service handmatig op of af.

Bestanden

init.sqlsql
CREATE DATABASE IF NOT EXISTS ApplicationDB;
USE ApplicationDB;
CREATE TABLE IF NOT EXISTS ApplicationData (
id INT AUTO_INCREMENT PRIMARY KEY,
data_value VARCHAR(255)
);
INSERT INTO ApplicationData (data_value) VALUES ('Eerste record');
SQL opstartscript voor MySQL database.
mysql-stack.yamlyaml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
MYSQL_DATABASE: ApplicationDB
secrets:
- mysql_root_password
configs:
- source: mysql_init_script
target: /docker-entrypoint-initdb.d/init.sql
deploy:
replicas: 1
configs:
mysql_init_script:
file: ./init.sql
secrets:
mysql_root_password:
external: true
Stack file met MySQL, Configs en Secrets configuratie.
ICT Architecture

Labo 03: Traefik in Docker Swarm

Configuratie en deployen van de Traefik reverse proxy in een Swarm cluster.

Netwerk Setup

docker network create --driver=overlay traefik-public
Maak een overlay netwerk aan voor communicatie tussen Traefik en services.

Testen

curl -H "Host: whoami.localhost" http://<IP-MANAGER>
Test de routing via de command line met curl headers.

Bestanden

traefik-stack.yamlyaml
version: '3.8'
services:
traefik:
image: traefik:v3.1
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- traefik-public
deploy:
placement:
constraints:
- node.role == manager
networks:
traefik-public:
external: true
Traefik proxy configuratie voor Swarm API routing.
whoami-stack.yamlyaml
version: '3.8'
services:
whoami:
image: traefik/whoami
networks:
- traefik-public
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
traefik-public:
external: true
Voorbeeld service met Traefik labels voor dynamische routing.
ICT Architecture

Labo 04: CephFS Shared Storage

Installatie en configuratie van MicroCeph voor gedistribueerde bestandssystemen.

MicroCeph Installatie

sudo snap install microceph
Installeer MicroCeph via snap (op alle nodes).
sudo microceph cluster bootstrap
Start de cluster op de eerste node (bootstrap).
sudo microceph cluster add <HOSTNAME>
Genereer een join-token voor een nieuwe node (op node 1).
sudo microceph cluster join <TOKEN>
Voeg een nieuwe node toe aan de cluster.

Opslag & CephFS

sudo microceph disk add /dev/sdb --wipe
Voeg een harde schijf toe aan de Ceph opslagpool.
sudo microceph enable cephfs
Schakel de CephFS functionaliteit in (enkel op de eerste node).

Mounting

sudo ceph auth get-key client.admin
Haal de admin auth key op om te kunnen mounten.
sudo ceph mon dump
Haal de monitor IP-adressen op.
sudo mount -t ceph <MON_IP1>,<MON_IP2>:/ /mnt/mycephfs -o name=admin,secret=<AUTH_KEY>
Mount de gedeelde storage lokaal op het systeem.

Bestanden

traefik-cephfs.yamlyaml
version: '3.8'
services:
traefik:
image: traefik:v3.1
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/mnt/mycephfs/traefik/acme.json:/acme.json"
networks:
- traefik-public
deploy:
replicas: 2
placement:
constraints:
- node.role == manager
networks:
traefik-public:
external: true
Traefik configuratie met gedeelde acme.json op CephFS.
ICT Architecture

Labo 05: RabbitMQ Basics

Basisgebruik van RabbitMQ voor asynchrone communicatie via Python pika.

Broker Setup

docker compose up -d
Start de RabbitMQ broker met de management interface.
http://localhost:15672
Toegang tot de management UI (guest/guest).

Tutorial Scripts (Python)

python -u 01_hello_world/receive.py
Start de ontvanger (unbuffered output).
python 01_hello_world/send.py
Start de zender.
python 02_work_queues/new_task.py "Taak"
Stuur een taak naar de work queue.
python 03_Publish-Subscribe/emit_log.py "Log info"
Publiceer een logbericht naar alle subscribers.

Bestanden

docker-compose.yamlyaml
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
Docker Compose file voor RabbitMQ.
ICT Architecture

Labo 06: RabbitMQ Clusteren

Opzetten van een hoog beschikbare RabbitMQ cluster binnen Docker Swarm met peer discovery.

Handmatig Clusteren

chmod 600 .erlang.cookie && chown 999:999 .erlang.cookie
Zet de juiste permissies voor de Erlang cookie.
docker exec -it <container_id> rabbitmqctl stop_app
Stop de RabbitMQ applicatie op een node.
docker exec -it <container_id> rabbitmqctl join_cluster rabbit@<hostname_node_1>
Join de cluster via de eerste node.
docker exec -it <container_id> rabbitmqctl cluster_status
Controleer of de node succesvol is gejoind.

Declaratieve Setup

echo "MijnGeheimeCookie" | docker secret create erlang_cookie -
Maak een secret aan voor de Erlang cookie in Swarm.

Bestanden

rabbitmq.confini
cluster_formation.peer_discovery_backend = classic_config
cluster_formation.classic_config.nodes.1 = rabbit@1.rabbitmq
cluster_formation.classic_config.nodes.2 = rabbit@2.rabbitmq
cluster_formation.classic_config.nodes.3 = rabbit@3.rabbitmq
Configuratie voor automatische peer discovery.
rabbitmq-stack.yamlyaml
version: '3.8'
services:
rabbitmq:
image: rabbitmq:3-management
hostname: "{{.Task.Slot}}.rabbitmq"
environment:
RABBITMQ_USE_LONGNAME: "true"
RABBITMQ_NODENAME: "rabbit@{{.Task.Slot}}.rabbitmq"
RABBITMQ_ERLANG_COOKIE_FILE: "/run/secrets/erlang_cookie"
secrets:
- erlang_cookie
configs:
- source: rabbit_config
target: /etc/rabbitmq/rabbitmq.conf
networks:
- rabbit-net
deploy:
replicas: 3
endpoint_mode: dnsrr
networks:
rabbit-net:
driver: overlay
configs:
rabbit_config:
file: ./rabbitmq.conf
secrets:
erlang_cookie:
external: true
Stack configuratie voor RabbitMQ cluster met templates en secrets.
ICT Architecture

Labo 07: Microkernel Architectuur

Implementatie van een plugin-systeem in C# voor een TextCell spreadsheetapplicatie.

Compileren & Uitvoeren

dotnet build
Compileer de C# applicatie.
dotnet run
Start de applicatie op.

Bestanden

IPlugin.cscsharp
using System.Collections.Generic;
public interface IPlugin
{
string Name { get; }
BerekendeCel Execute(List<BerekendeCel> args);
}
De interface waaraan alle plugins moeten voldoen.
MinPlugin.cscsharp
using System;
using System.Collections.Generic;
public class MinPlugin : IPlugin
{
public string Name => "min";
public BerekendeCel Execute(List<BerekendeCel> args)
{
if (args.Count == 0) return new BerekendeCel("Fout: Geen argumenten", CelType.ERROR);
int minWaarde = int.MaxValue;
foreach (var arg in args)
{
if (arg.CelType != CelType.INT) return new BerekendeCel($"Fout: geen getal.", CelType.ERROR);
int val = Convert.ToInt32(arg.VoorstellingWaarde);
if (val < minWaarde) minWaarde = val;
}
return new BerekendeCel(minWaarde.ToString(), CelType.INT);
}
}
Een implementatie van de IPlugin interface voor een 'min' functie.
Program.cs (Snippet)csharp
using System.Reflection;
public static List<IPlugin> LoadPlugins(string path)
{
var plugins = new List<IPlugin>();
if (!Directory.Exists(path)) return plugins;
foreach (var file in Directory.GetFiles(path, "*.dll"))
{
var assembly = Assembly.LoadFrom(file);
var types = assembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
foreach (var type in types)
{
if (Activator.CreateInstance(type) as IPlugin is IPlugin plugin) plugins.Add(plugin);
}
}
return plugins;
}
Code om plugins dynamisch in te laden via Reflection.
ICT Architecture

Labo 08: Microservices Patronen

Voorbeelden van API Gateway en API Composition in een Node.js/Python ecosysteem.

Omgeving

docker compose up -d
Start de basis services (Customers, Orders, Products).

Bestanden

gateway.pypython
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/order-summary', methods=['GET'])
def get_order_summary():
email = request.args.get('email')
customer = requests.get(f"http://customers:3000/customers?email={email}").json()[0]
orders = requests.get(f"http://orders:3000/orders?customerId={customer['id']}").json()
full_orders = []
for order in orders:
product = requests.get(f"http://products:3000/products/{order['productId']}").json()
full_orders.append({
"productName": product['name'],
"priceAtPurchase": product['price']
})
return jsonify({
"customerName": customer['name'],
"orderHistory": full_orders
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Een Python API Gateway die data uit meerdere services combineert.
ICT Architecture

Labo 09: Transactional Outbox

Implementatie van een Message Relay in Node.js die periodiek onverzonden berichten naar RabbitMQ stuurt.

Uitvoeren

node relay.js
Start de message relay.

Bestanden

relay.jsjavascript
const mysql = require('mysql2/promise');
const amqp = require('amqplib');
async function relayMessages() {
const connection = await mysql.createConnection({ host: 'localhost', user: 'root', database: 'microservices' });
const channel = await (await amqp.connect('amqp://localhost')).createChannel();
setInterval(async () => {
const [rows] = await connection.execute('SELECT * FROM outbox WHERE is_sent = FALSE');
for (const row of rows) {
channel.publish('domain_events', row.type, Buffer.from(JSON.stringify(row.payload)));
if (Math.random() < 0.1) process.exit(1); // Simuleer 10% falen
await connection.execute('UPDATE outbox SET is_sent = TRUE WHERE id = ?', [row.id]);
}
}, 10000);
}
relayMessages();
Periodieke controle op de outbox tabel.
outbox.sqlsql
CREATE TABLE outbox (
id INT AUTO_INCREMENT PRIMARY KEY,
aggregate_type VARCHAR(255),
aggregate_id VARCHAR(255),
type VARCHAR(255),
payload JSON,
is_sent BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Schema voor de outbox tabel.
ICT Architecture

Labo 10: Autorisatie en Authenticatie (OAuth)

Login implementatie via GitHub OAuth.

Setup & Installatie

npm install express axios dotenv
Installeer de nodige packages voor Node.js.
node index.js
Start de OAuth Express server.

Bestanden

.envini
GITHUB_CLIENT_ID=jouw_client_id
GITHUB_CLIENT_SECRET=jouw_client_secret
GITHUB_CALLBACK_URL=http://localhost:3000/callback
Omgevingsvariabelen voor GitHub OAuth.
oauth.jsjavascript
const tokenResponse = await axios.post('https://github.com/login/oauth/access_token', {
client_id: process.env.GITHUB_CLIENT_ID,
client_secret: process.env.GITHUB_CLIENT_SECRET,
code: req.query.code
}, {
headers: { Accept: 'application/json' }
});
const accessToken = tokenResponse.data.access_token;
Snippet voor het inwisselen van een code voor een token.