Ga naar hoofdinhoud

Docentenhandleiding

Praktische informatie voor docenten die deze cursus klassikaal willen inzetten: wat de site is, welke randvoorwaarden er zijn, en hoe je de lessen indeelt.

Wat is dit?​

Een interactieve cursus fullstack-ontwikkeling met FastAPI: je leerlingen voegen een Python-back-end toe aan een website. Doelgroep: leerlingen die al wat Python, HTML en JavaScript kennen en willen leren hoe front-end en back-end samenwerken.

Per onderwerp staat er een minimale, werkende handler die je daarna uitbreidt met validatie, templates en een database. De nadruk ligt op het samenspel tussen verzoek (browser) en antwoord (server).

Voorkennis

Deze cursus bouwt voort op Python en op HTML, CSS en JavaScript. Plan die eerst in als je leerlingen daar nog niet mee bekend zijn. Op het cursusoverzicht staat welke cursus waarop voortbouwt.

In de lessen zelf staat de voorkennis per les in een dichtgeklapt blok, zodat een leerling die vastloopt op een def of een <form> terug kan naar de uitleg zonder dat de les daarmee begint.

Technische randvoorwaarden​

WatAntwoord
Operating systemWindows, macOS of Linux
OefenenVeel voorbeelden draaien in de browser
Eigen projectPython 3 en een editor (VS Code) lokaal aanbevolen; wie wil kan er git naast gebruiken
AccountNiet nodig
InternetVereist — de site is online

Didactische aanpak (PRIMM)​

Elke les volgt hetzelfde vaste patroon, de PRIMM-methode, en sluit af met een sectie over wat er mis kan gaan:

  1. Predict: je leerling voorspelt wat een endpoint of stuk code doet.
  2. Run: je leerling draait het en controleert die voorspelling.
  3. Investigate: kleine experimenten met het verzoek of het antwoord.
  4. Modify: een opdracht die voortbouwt op de bestaande handler.
  5. Make: een vrijere opdracht waarin je leerling zelf een endpoint bouwt.
  6. Er gaat iets mis: veelvoorkomende fouten, met oorzaak en oplossing.

Een Predict is een meerkeuzevraag die meteen zegt of het antwoord klopt. Tips en antwoorden van de andere opdrachten staan in <details>-blokken; laat je leerlingen eerst zelf proberen voordat ze openklikken.

Klassikaal gebruik & differentiatie​

  • Plenair starten: bespreek de Predict-vraag klassikaal, daarna werken je leerlingen zelfstandig door.
  • Snelle leerlingen breiden een endpoint uit met extra validatie of een tweede route.
  • Langzamere leerlingen mogen de Make-opdracht overslaan zonder dat de volgende les daarop bouwt.
  • Beoordelen: de cursus heeft geen vaste eindopdracht. Het gastenboek dat je leerlingen in de basis bouwen, of een eigen project op dezelfde manier, kunnen ze uploaden op Project checken. Die nakijker laat zien welke onderwerpen erin terugkomen.

Voor een cijfer is Leerdoelen het overzicht van de concepten waarop je beoordeelt, met een pagina per niveau (1, 2 en 3) en een vergelijking van de drie; onder elk concept staat de les waarin het aan bod komt. Niveau 1 en 2 gaan over het FastAPI-project. Niveau 3 gaat over wat erna komt: Linux, Veiligheid en Online zetten. Daar is de echte server (firewall, HTTPS, back-up, een nieuwe versie uitrollen) expert. Onderaan staat Beoordelen met Bloom, met de achtergrond bij vakdidactiek in Denkvaardigheden volgens Bloom. Upload het project op Project checken voor docenten, kies bij Leerroute het niveau van de leerling, en kies onder Cijfer de denkvaardigheid die je zag. De moeilijkheid stelt de nakijker voor; klik erop om hem vast te leggen. De installatie en de ontwikkelaarstools staan in geen bestand: die vink je zelf aan als je ze in de les of het gesprek zag. Op niveau 3 geldt dat ook voor alles wat in de terminal of op de server gebeurt; wat in de code of in een bestand van het project staat (Argon2, een limiet met slowapi, een Dockerfile), vindt de nakijker zelf. Het cijfer en de zin erbij komen in de PDF.

Leerlijn​

De lessen staan in de FastAPI-sidebar en bouwen op elkaar voort. Die begint met een startpagina: wat je leerlingen aan het eind hebben (een nagebouwd plaatje van het gastenboek), de voorkennis uit de andere cursussen, en de route als kaart met per blok een zin en de lessen. Daarna komt Installatie; de Cheatsheet en Er gaat iets mis zijn naslag en staan in de navbar. Projectstructuur, met de mappen van het project per les, staat bovenaan de cheatsheet.

De sidebar heeft twee delen. De basis eindigt met een werkend gastenboek: een formulier, opslaan, een overzicht, een detailpagina, een nette 404 en accounts met een wachtwoord. Hij loopt van Je eerste server via Losse pagina's en routes, Eén route voor veel pagina's, CSS en afbeeldingen, Templates en formulieren, Gegevens opslaan en de drie categorieën met berichten naar Accounts. Wat volgt zijn drie uitbreidingen (htmx, JavaScript, cookies en sessies) en een afronding. Die maken het gastenboek mooier, maar zijn niet nodig om het te laten werken. Heb je weinig tijd, dan kun je na de basis stoppen; wie verder gaat, volgt de uitbreidingen op volgorde, want "Server of browser?" vergelijkt de server met htmx en JavaScript.

Vijf keer onderweg staat een diagram van de hele keten, browser links en server rechts, met per stap een link naar de les. Het begint kaal bij het eerste verzoek, direct na het eerste endpoint, en groeit mee. Bij één pagina, aan het eind van CSS en afbeeldingen, komen de losse verzoeken voor CSS en afbeeldingen erbij. Bij klikken en versturen, aan het eind van de basis, de database en de template, en bij een formulier de omleiding. Zonder herladen, aan het eind van de htmx-uitbreiding, wordt die omleiding een stuk HTML als antwoord; bij een sessie, direct na de les Sessies, komt de cookie erbij. Hoe een verzoek werkt bundelt ze bij het afronden met een tabel "waar hoort jouw code".

OnderwerpLeerdoel
Je eerste endpointEen API-route maken en aanroepen
Kijken wat de browser doetHet tabblad Netwerk: verzoek, antwoord en statuscode
HTML tonen en HTML in bestandenPagina's serveren vanuit Python en uit bestanden
Eén route voor veel pagina's: path-parametersNa de losse pagina's met elk een eigen route: één route met een plekhouder en een dictionary
Een waarde na een vraagteken: query-parameters?term=, een standaardwaarde, de 422, en het type int tegenover str
CSS in een eigen bestand (static files)Eén stylesheet voor alle pagina's, en de cache
Templates met Jinja2Een pagina met waarden uit Python: {{ }}, het dictionary, en wat de browser krijgt
Een formulier versturen (POST) en beantwoordenGET of POST, Form(...), en een formulier beantwoorden met een template
Gegevens opslaan met SqliteDictOpslaan, commit() en uitlezen, in een los script zonder server
Alles bekijken: len en itemsAlles in een database tonen met bekijk_db.py, in een los script
Bestaat de sleutel? in en getKijken of een sleutel er is, en get met een standaardwaarde
Verwijderen met deldel, eerst kijken met in, en commit()
Een dictionary als waardeMeer velden onder één sleutel, opslaan en ophalen
Een dictionary aanpassen: ophalen, aanpassen, terugzettenWaarom je een opgehaalde dictionary terugzet
Een formulier opslaanWat een bezoeker invult in een database zetten, onder één sleutel
Elk bericht bewaren: een eigen sleutelHet gastenboek: elk bericht een eigen sleutel met time.time_ns()
Doorsturen na opslaan (redirect)Doorsturen na een POST (post/redirect/get)
Alles tonen: een for-lus in je templateOpgeslagen records op een pagina zetten met een for-lus
Nog niets? if en else in je templateEen lege lijst opvangen met {% if %}
Eén item tonen: de sleutel uit de URLEen bericht opzoeken met de sleutel uit de URL, en linken vanuit de lijst met db.items()
Een 404 sturen: als het item niet bestaatZelf een 404 sturen met raise HTTPException
Een bericht verwijderen: een verborgen veldEen verborgen veld en del, en terug naar de lijst
Registreren: een account opslaanEen account in een eigen database, en een naam die al bestaat weigeren
Inloggen: een wachtwoord controlerenHet wachtwoord controleren voordat een bericht wordt opgeslagen, met 401
Zonder herladen met htmx (uitbreiding)Een knop die iets aan de server vraagt terwijl de pagina blijft staan
htmx-recepten (uitbreiding)Overzicht van recepten: formulier, verversen, vanzelf verversen, verwijderen, zoeken
JavaScript erbij (uitbreiding)Code die in de browser draait in plaats van op de server
Console en Elementen (uitbreiding)Uitvoer en fouten van JavaScript zien, en de pagina zoals de browser hem nu heeft
Server of browser? (uitbreiding)Kiezen waar iets hoort, en waarom controles op de server moeten
Onthouden met een cookie (uitbreiding)De browser iets laten onthouden tussen twee verzoeken
Onthouden op de server: sessies (uitbreiding)Eén keer inloggen: alleen een sessie-id bij de bezoeker, de naam op de server
Cookie of sessie? (uitbreiding)Kiezen tussen de twee, en waarom je niet alles kunt geloven
Een eigen 404-pagina (afronding)Een handler voor 404 met een template, en waarom een 500 een 500 blijft
Laat het aan anderen zienDe server openzetten voor het netwerk

De zes korte lessen over SqliteDict draaien nog zonder server: je leerlingen oefenen in losse scripts, met een script bekijk_db.py dat ze daarna bij elke database gebruiken. Zo komen de fouten die ze anders pas in de server maken (een vergeten commit(), een script in een andere map dat stil een lege database aanmaakt, een dictionary die ze aanpassen maar niet terugzetten) los van FastAPI aan bod. SqliteDict op een rij zet alles op één pagina, net als Projectstructuur buiten de sidebar.

Registreren en Inloggen sluiten de basis af met accounts: een naam en een wachtwoord in gebruikers.db, en een gastenboek dat een bericht alleen bewaart als het wachtwoord klopt. Een server onthoudt in de basis nog niemand tussen twee verzoeken, dus je leerlingen typen hun wachtwoord bij elk bericht. Dat is bewust: het is eerlijk, het is veilig genoeg om berichten op naam te zetten, en het maakt de uitbreiding Onthouden nodig, waarin ze één keer inloggen. De lessen zeggen zelf wat nog niet veilig is: de wachtwoorden staan leesbaar in de database (dat lost Wachtwoorden in de route Veiligheid op), en verwijderen kan nog iedereen.

"Een formulier opslaan" tot en met "Eén item tonen" bouwen samen het gastenboek. Alles wat een latere les nodig heeft, staat in de hoofdtekst en niet in een opdracht: het gastenboek zelf in "Een formulier opslaan", het verwijderen in "Doorsturen na opslaan". Daarna hebben je leerlingen een werkende webapplicatie met een overzicht, een detailpagina en nette foutafhandeling.

De ontwikkelaarstools van de browser krijgen twee eigen lessen, zonder schermafbeeldingen: de lessen bouwen het paneel na met de Nederlandse en Engelse naam van elk tabblad naast elkaar. "Kijken wat de browser doet" komt direct na het eerste endpoint en leert het tabblad Netwerk: één verzoek en de statuscode. De cache, de reden waarom een wijziging niet doorkomt en de vraag die je in de les het vaakst krijgt, komt bij "CSS in een eigen bestand", waar hij voor het eerst speelt. "Console en Elementen" komt na "JavaScript erbij", als de terminal niets meer laat zien, en bereidt "Server of browser?" voor: wie in Elementen maxlength weghaalt, ziet daar wat de bezoeker kan.

Na de basis komt de eerste uitbreiding, "Zonder herladen (htmx)": de server doet het werk, maar de pagina blijft staan, omdat htmx het verzoek stuurt en het antwoord op zijn plek zet. De eerste les is bewust zo klein mogelijk: één knop met twee attributen die de tijd van de server ophaalt, en een endpoint van drie regels. Daarna volgt één les met een overzicht van recepten (formulier, verversen, vanzelf verversen, verwijderen, zoeken), waaruit je leerlingen kiezen wat hun gastenboek nodig heeft. Ze schrijven er geen JavaScript voor; ze hergebruiken alles wat ze al hebben. Elk endpoint geeft een stukje HTML terug in plaats van een omleiding, en dat is ook de fout die je het meest zult zien: een hele pagina in het doel. De tweede valkuil is dat htmx een mislukt verzoek stil negeert; de lessen sturen daarom steeds naar het tabblad Netwerk. Het bestand htmx.min.js staat op deze site om te downloaden, zodat het ook werkt op een netwerk dat externe scripts blokkeert.

Vanaf "JavaScript erbij" komt de browserkant in beeld. Je leerlingen kennen JavaScript al uit de web-cursus; nieuw is de vraag wáár code hoort. Die vraag krijgt een eigen les ("Server of browser?") nadat ze de drie smaken hebben gevoeld: een formulier versturen gaat via de server en de pagina verspringt, met htmx blijft de pagina staan, en een tekenteller draait in de browser zonder dat de server er iets van ziet. De tabel in die les heeft voor elk een kolom.

Daarop volgt "Onthouden", drie lessen die het sterkst zijn als je ze achter elkaar plant. In de eerste onthoudt een cookie de naam, en veranderen je leerlingen die cookie in een andere naam: de server neemt het klakkeloos over, en alleen het wachtwoord uit Inloggen houdt ze nog tegen. In de tweede loggen ze één keer in. /inloggen maakt een sessie, er gaat alleen een sessie-id naar de bezoeker, en de naam bij een bericht komt voortaan uit de sessie in plaats van uit het formulier. Daarmee wordt "eigenaar van een bericht" iets wat de server zelf weet, en kunnen ze verwijderen afschermen met een 403. Het is voor veel leerlingen het eerste moment waarop beveiliging concreet wordt: de verwijderknop verbergen is niet hetzelfde als verwijderen onmogelijk maken.

"Cookie of sessie?" sluit dat blok af zoals "Server of browser?" het vorige afsluit: niet met nieuwe techniek, maar met de vraag wanneer je wat kiest. De kern is dat een sessie ook een cookie is; het verschil zit in wát erin staat. Leerlingen die alles voor de zekerheid een sessie willen maken, vinden daar het tegenargument: een voorkeur die niemand kan schaden hoort in een gewone cookie.

Daarna sluit de cursus af met een eigen 404-pagina, een overzicht van alle diagrammen en de server openzetten voor het netwerk, zodat de klas elkaars project kan bezoeken. De 404-les zet drie manieren naast elkaar (een handler voor 404, een handler voor elke HTTP-fout die de rest doorgeeft, en een pagina in het eigen endpoint) en werkt de handler voor 404 uit. Een handler voor alle fouten is bewust geen optie: dan lijkt een fout in de code van een leerling een ontbrekende pagina, en zoekt hij op de verkeerde plek. In een opdracht zien je leerlingen dat zelf gebeuren.

Wat hier bewust níét in zit, is fetch: JavaScript dat zelf data ophaalt bij een endpoint. Dat vraagt async/await, JSON verwerken en objecten — stof die de web-cursus nog niet behandelt. Het blok "Zonder herladen" vult dat gat met htmx: de server stuurt HTML, niet JSON. Leerlingen die toch fetch willen kun je die kant op sturen; de cursus bouwt er niet op.

Unplugged​

Onder Unplugged staan werkvormen zonder computer, elk met een printknop. In een webproject gebeurt veel tussen de bestanden en tussen browser en server, en dat zie je op het scherm niet. Op papier wel. De antwoorden staan op het scherm dicht en komen bij het printen op een losse pagina. De les waar een werkvorm bij hoort, wijst ernaar met een tip Eerst op papier vlak voor de opdrachten.

WerkvormBijMisvatting
Het verzoek-rollenspelDoorsturen na opslaanéén klik is één verzoek; de server onthoudt het vorige verzoek
Welke route antwoordt?Query-parametersde functienaam bepaalt welke code draait; elke route die past, draait
Welke taal, waar? (print het knipblad, de laatste pagina, apart en enkelzijdig)Templates met Jinja2de browser draait Python en Jinja; JavaScript kan bij de database
Volg de naamEen formulier beantwoorden met een templateeen naam die in één bestand klopt, klopt overal
Template invullenNog niets? if en elseeen ontbrekende sleutel geeft een foutmelding; de browser ziet de {% for %}
Een postbode zonder geheugenCookie of sessie?de server weet na het inloggen wie je bent; een cookie is te vertrouwen
Vertrouw de bezoeker nietWie mag watmaxlength en een verborgen knop zijn een controle
Wat blijft er over? (print het knipblad apart)Online zetten: alles in één bestandeen nieuwe versie gooit de database weg; down gooit alles weg; wat in de container staat, blijft
Waar komt je verzoek langs?Online zetten: je domein met HTTPSde firewall is het enige slot; een 404 komt van je eigen code

Losse vragen en terugkoppeling​

Elke Predict-opdracht in de lessen is een meerkeuzevraag die meteen zegt of het antwoord klopt, met bij elk fout antwoord waarom niet. Elke les met code heeft daarnaast één controlevraag vóór de opdrachten. Boven elk codeblok staat in welk bestand het hoort en of het op de server of in de browser draait, met een eigen kleur per taal.

De nakijker op Project checken laat je leerlingen ook zien waar hun bestanden niet op elkaar aansluiten: een formulierveld zonder parameter, een {{ naam }} die het dictionary niet meegeeft, een action of link zonder route, een bestand in static/ dat er niet is. Juist die fouten geven geen foutmelding in de terminal.

De reeks Veiligheid​

Naast de FastAPI-sidebar staat een tweede sidebar, Veiligheid. De meeste reeksen passen na de hele FastAPI-route, dus ook na de drie uitbreidingen: ze bouwen op formulieren, templates, htmx, sessies en HTTPException, en je leerlingen hebben dan een eigen gastenboek om te beschermen. Invoer kan al direct na de basis; de verwijzingen naar Server of browser? zijn daar optioneel. Elke reeks werkt in een eigen map met een eigen virtual environment, zodat het gastenboek van de leerling heel blijft; hoe dat gaat staat in Een map per reeks. De route begint met een startpagina (het ene idee, de spelregel, een kaart en een overzicht van statuscodes) en twee pagina's gereedschap, Een script als bezoeker en Een bezoeker die onthoudt, waarin ze met httpx verzoeken sturen zonder browser. Daarna volgen de reeksen, van makkelijk naar moeilijk. De eerste, Wat je server laat zien, heeft nog geen aanval. Je leerlingen kijken wat FastAPI zelf op /docs zet, en vinden daar het test-endpoint GET /sessies uit Sessies terug. Daarna zien ze wat er in hun .db-bestanden staat, voordat ze een zip of een repository delen. Te veel verzoeken staat als vierde, want een limiet is één regel boven een endpoint. Wachtwoorden staat achteraan: die reeks is het langst, en zet de limiet uit Te veel verzoeken op het inloggen. Elke reeks gaat van de zwakheid die de leerling zelf nadoet, via de oplossing in FastAPI, naar wat er in de praktijk nog gebeurt, inclusief wat de wet zegt. De reeksen zijn los te volgen; alleen Cookies bouwt op de server uit Wie mag wat. Wie mag wat en Cookies afschermen gebruiken de accounts uit de basis: in hun scripts registreren Sara en Alex, loggen ze in, en is de eigenaar van een bericht wie onder die naam inlogde. Wachtwoorden laat met de /registreer en de controle uit de basis zien waarom gewone tekst onveilig is, en maakt ze daarna veilig.

Elke reeks bestaat uit kleine stappen, net als Online zetten. De eerste stap zet de map en de server van de reeks klaar. Elke stap eindigt met een groen blok Gelukt als je dit ziet: bij een zwakheid ziet je leerling de zwakheid zelf (het script verwijdert het bericht van een ander), bij een verdediging dat dezelfde proef nu faalt (een 403). Daarna volgt één controlevraag. Een vinkje Het werkt onthoudt de browser van de leerling, en de kaart op de startpagina laat per stap zien wat gelukt is. De praktijkpagina's aan het eind van een reeks zijn om te lezen en hebben geen groen blok.

De reeks is opzettelijk defensief. Elke zelftest draait tegen de eigen server op 127.0.0.1 en de eigen database; er staat een :::danger-blok boven dat zegt dat het aanvallen van een ander strafbaar is. De demonstratie van een zwakheid is klein: twintig verzoeken, de eigen database bekijken, met de eigen /sessies de eigen twee testbezoekers zijn, een vet woord in het gastenboek, de eigen cookie in de Console. Er staat geen script in dat wachtwoorden raadt of een server platlegt. Waarom een snelle hash zwak is, laat de wachtwoordenreeks zien met een meting van de rekentijd.

Reeks, in volgordeZwakheidKern van de oplossing
Wat je server laat zien/docs toont elk endpoint, ook een vergeten GET /sessies; een zip neemt de databases meede handleiding uit met docs_url=None, het endpoint weg, een .gitignore
Invoer controlerenmaxlength houdt een script niet tegen; met ?naam=../../main.py leest een bezoeker je codede grens in Form op de server, en een lijst met wat mag
HTML van een bezoeker (XSS)ingevulde HTML draait in andermans browser: opgeslagen in het gastenboek, teruggezet uit een link (?term=), of door JavaScript met innerHTMLescapen bij de uitvoer: escape() of een template op de server, textContent in de browser
Te veel verzoeken (DoS)veel verzoeken leggen de server plateen verzoeklimiet met slowapi
Wie mag wateen verborgen knop is geen slotde controle op de server, met 403
Cookies afschermeneen script leest het sessie-id; een sessie die niemand uitlogt, geldt eeuwighttponly, samesite, secure, uitloggen en een verlooptijd op de server
Wachtwoorden veilig opslaande accounts uit de basis bewaren het wachtwoord leesbaar, en een lek geeft ze allemaal weg; /inloggen laat eindeloos proberenhashen met Argon2, en een limiet op /inloggen en /wijzig, in twaalf kleine lessen

De beloofde uitvoer en foutmeldingen in deze reeks draaien niet mee in CI, die alleen de Python-syntax controleert. Ze zijn met de hand nagedraaid tegen een echte server. Werk je een les bij, controleer een gewijzigde uitvoer dan zelf in een virtual environment met de echte packages.

De route Online zetten​

Een eigen route in de navbar, na Veiligheid: je leerlingen zetten hun gastenboek op een Linux-server, op een veilige manier, met Docker. De route heeft vier delen en een extra, in 43 kleine stappen. Elke stap doet één ding en eindigt met een groen blok Gelukt als je dit ziet: een concrete waarneming, en een vinkje Het werkt dat de browser van de leerling onthoudt (alleen daar, in localStorage). De kaart op de startpagina van de route zet een vinkje achter elke gelukte stap. Daarna volgt één controlevraag, zodat je leerling niet alleen ziet dát het werkt maar ook snapt waarom.

DeelWaarLessen
Op Windowslaptop, VS Codeje site met fastapi run, dat hij niet herlaadt, waar hij luistert, requirements.txt, een README, en je project in een verse map
In LinuxUbuntu in WSLgit en de ssh-sleutel in Ubuntu, je project klonen, een virtual environment, je site met fastapi run in Linux, en pushen vanuit Ubuntu
In een containerUbuntu in WSLDocker installeren, een image, een container, niet als root, een volume, Compose, Traefik als voordeur
Op een echte servereen VPSssh alleen met een sleutel, de firewall en automatische updates, de site erop met een deploy key, HTTPS via Let's Encrypt, een nieuwe versie uitrollen met een healthcheck, een back-up maken en terugzetten, de images bijwerken met --pull en opruimen
Extrade server, en via een ssh-tunnelPortainer om de containers in de browser te bekijken, en een back-up elke nacht met cron

De route legt geen Linux uit. Terminal, rechten, sudo apt, nano, diensten en ssh staan in de Linux-cursus, en elke les linkt naar de Linux-les die hij nodig heeft. Laat leerlingen die cursus eerst doen, minstens tot en met de lessen over WSL en ssh.

Vanaf les 7 werken leerlingen alleen nog in Ubuntu in WSL. Ze klonen hun repository daar met ssh (de sleutel uit de Linux-cursus staat op hun GitHub-account) en openen hem met code . vanuit Ubuntu. Hun site draait eerst met fastapi run in Linux, zonder Docker. Pas daarna komt Docker. Zo is elke overstap één ding: eerst een ander besturingssysteem, dan een container. De kopie op Windows gebruiken ze niet meer; de databases die daar staan, komen niet mee. Docker draait in de lessen als Docker Engine in Ubuntu in WSL, met de pakketten van Ubuntu (docker.io, docker-compose-v2, docker-buildx), en niet als Docker Desktop. Zo is het op de laptop hetzelfde als op de server, en is er geen licentievraag. Leerlingen komen in de groep docker; zeg erbij dat dat bijna root is. Docker Hub geeft per internetadres een beperkt aantal images per uur, en op school deelt de hele klas één adres. Haal met de klas niet tegelijk images op, of laat leerlingen een gratis account maken en docker login doen.

Voor het vierde deel is een server nodig. Een VPS bij een hostingbedrijf kost een paar euro per maand, en een domeinnaam is nodig voor HTTPS. Zeg erbij dat een server op internet binnen een uur bezoek krijgt van programma's die proberen in te loggen: les 24 laat dat zien, en les 25 tot en met 29 doen er iets aan. Laat die stappen daarom op dezelfde dag als les 24 doen, en voordat de site erop komt.

Twee dingen in de route zijn bewust streng. Docker zet een gepubliceerde poort open langs ufw, dus op de server publiceert alleen Traefik iets (80 en 443), en Portainer alleen op 127.0.0.1. En Portainer kan alles wat Docker kan: hij komt nooit achter Traefik of op een open poort, alleen via een ssh-tunnel.

Drie dingen die een echte server nodig heeft, zitten in de route. In les 30 krijgt Docker de log-driver local, die per container hoogstens honderd megabyte aan logboeken bewaart; zonder grens loopt de schijf na maanden vol. In les 39 werken leerlingen de images bij met docker compose build --pull: unattended-upgrades werkt alleen Ubuntu bij, en zonder --pull bouwt Docker steeds op het Python-image van de eerste keer. En les 41 en 42 maken de back-up elke nacht, met een script in cron. Die back-ups staan op dezelfde server, dus een kopie op de laptop blijft nodig; zeg dat erbij. Laat leerlingen het terugzetten één keer oefenen in WSL (les 38): wat docker compose cp in een container zet, is niet van app, en zonder chown geeft de site pas bij het eerste nieuwe bericht een 500.

Eén server voor de klas​

Huren niet alle leerlingen een eigen server, dan kan één schoolserver met Traefik de sites van de hele klas draaien. Dat is dezelfde opzet als in de lessen, met een paar verschillen:

  • Eén voordeur. Jij draait Traefik met HTTPS, zoals in les 34, en maakt het netwerk web. Met een DNS-record *.klas.school.nl (een wildcard) dat naar de server wijst, hoort elke naam onder dat domein bij deze server.
  • Een naam per leerling. Elke leerling krijgt een eigen naam, zoals sam.klas.school.nl, in zijn .env als DOMEIN. Traefik vraagt voor elke naam zelf een certificaat aan.
  • Unieke namen in de labels. Het label traefik.http.routers.gastenboek… moet per leerling anders heten, anders zitten de sites elkaar in de weg. Laat ze gastenboek vervangen door hun eigen naam, ook in traefik.http.services….
  • Wie mag Docker gebruiken? Wie in de groep docker zit, kan de hele server overnemen, en dus ook bij de databases van klasgenoten. Geef leerlingen daarom geen toegang tot Docker op de gedeelde server. Laat ze hun repository met jou delen (een deploy key per project, alleen lezen), en rol hun sites zelf uit met git pull en docker compose up -d --build, met een map en een .env per leerling. Portainer, via jouw eigen ssh-tunnel, geeft je dan een overzicht van alle containers.

Leerlingen doen in die opzet deel 1, 2 en 3 helemaal zelf, en van deel 4 alleen wat over hun eigen project gaat: de deploy key, de .env met hun naam, en controleren dat hun site met HTTPS werkt. Les 24 tot en met 30 lezen ze mee; die doe jij één keer voor de server.

Feedback & bijdragen​

Suggesties, fouten of nieuwe ideeën? Open een issue of pull request op GitHub. Alle bijdragen — van een typo tot een nieuwe les — zijn welkom.