Docker for Dummies
Alles wat je moet weten over Docker β van nul tot begrijpen hoe containers werken, hoe je ze beheert en hoe je ze veilig migreert naar een andere server.
π’ Beginnersniveau β geen voorkennis vereistWat is Docker eigenlijk?
Voordat je iets beheert, is het handig om te begrijpen waarom Docker bestaat en wat het oplost.
Het klassieke probleem
Je hebt vast wel eens gehoord: "Bij mij werkt het wel!". Dat is precies het probleem dat Docker oplost. Software heeft afhankelijkheden: de juiste versie van Node.js, de juiste database-driver, de juiste configuratiewaarden. Als die versies net iets anders zijn op jouw laptop versus op de server, crasht de applicatie.
Vroeger werden goederen los verscheept en moest elke haven alles anders inladen β chaos en vertraging. Toen kwamen standaard vrachtcontainers: alles past overal, elk schip laadt ze hetzelfde. Docker doet exact hetzelfde voor software. Een Docker-container bevat je applicatie plus alles wat die nodig heeft. Je zet hem op Ontwikkelaar-A, Server B, Server C β hij werkt overal precies hetzelfde.
Wat is het verschil met een virtuele machine (VM)?
| Kenmerk | Virtuele Machine (VM) | Docker Container |
|---|---|---|
| Heeft eigen OS | β Volledig eigen kernel | β Deelt de kernel van de host |
| Opstartsnelheid | Minuten | Seconden (of minder) |
| Geheugengebruik | Hoog (GBs per VM) | Laag (MBs per container) |
| Isolatie | Maximaal | Goed (gedeelde kernel) |
| Portabiliteit | Beperkt (GBs groot) | Uitstekend (MBs groot) |
Docker containers zijn geen mini-computers. Ze draaien direct op de Linux-kernel van jouw server, maar hebben hun eigen afgeschermde bestandssysteem, processenlijst en netwerk.
De kernconcepten
Docker werkt met een paar bouwblokken die je moet kennen. Eenmaal begrepen, snap je de rest vanzelf.
Dockerfile
Een recept. Een tekstbestand met stap-voor-stap instructies over hoe een Image gebouwd moet worden. Bijvoorbeeld: "Begin met Node.js 20, kopieer de bestanden, installeer dependencies, start de server."
Image
Het gebakken recept. Una Image is een onveranderlijk snapshot van je applicatie, klaar om te draaien. Je kunt een Image vergelijken met een ISO-bestand of een install-DVD. Je kunt er meerdere containers van draaien.
Container
Een draaiende instantie van een Image. Start je een Image op, dan wordt het een Container. Je kunt tientallen containers van dezelfde image draaien tegelijk β elk in zijn eigen afgeschermde omgeving.
Volume
Persistente opslag buiten de container. Als een container stopt of verwijderd wordt, verdwijnt alles erin β tenzij je een Volume gebruikt. Volumes overleven herstarts en container-verwijderingen.
Netwerk
Het interne telefoonnet tussen containers. Containers in hetzelfde Docker-netwerk kunnen
elkaar bellen via de servicenaam (bv. db), zonder dat je IP-adressen hoeft te
weten.
Docker Compose
Een orkestratietool. Met één YAML-bestand (docker-compose.yml) definieer je meerdere
containers tegelijk, inclusief hun netwerken, volumes en afhankelijkheden. Starten gaat met één
commando.
De Dockerfile is het pizzarecept. De Image is de bereide pizza in de doos, klaar voor bezorging. De Container is de pizza op tafel die iemand eet. De Volume is de koelkast β ook na de maaltijd blijft de inhoud bestaan. Het Netwerk is het bezorgsysteem dat alles verbindt.
Hoe werkt het samen?
Zo ziet de architectuur eruit van de coin-trader op deze server β van buitenaf tot diep in de container.
β
ββ /api β proxy β Server Container :3005
ββ /docker-manager β proxy β Docker Manager :9090
ββ /* β Statische bestanden /var/www/html
(via hostnaam "db":3306)
coin-trader-client-1 β serveert frontend via Apache (poort 80 intern)
De levenscyclus van een container
Elke container doorloopt een eenvoudige levenscyclus:
# 1. Image bouwen (eenmalig of na codewijziging) docker build -t mijn-app . # 2. Container starten (image β draaiende container) docker run -d --name mijn-container mijn-app # 3. Container is actief β verwerkt verzoeken docker ps # status bekijken docker logs mijn-container # 4. Container stoppen (maar niet verwijderen) docker stop mijn-container # 5. Container verwijderen docker rm mijn-container # 6. Image verwijderen (ruimt schijfruimte op) docker rmi mijn-app
EssentiΓ«le commando's
Je hoeft niet alles te kennen, maar deze commando's gebruik je dagelijks.
Containers beheren
# Alle draaiende containers zien docker ps # Alle containers zien (ook gestopte) docker ps -a # Container starten / stoppen / herstarten docker start mijn-container docker stop mijn-container docker restart mijn-container # Container verwijderen (moet eerst gestopt zijn) docker rm mijn-container # Container verwijderen, ook als hij draait docker rm -f mijn-container # Logs bekijken (laatste 50 regels, live volgen) docker logs --tail=50 --follow mijn-container # Shell openen IN een draaiende container docker exec -it mijn-container sh
Images beheren
# Alle lokale images zien docker images # Image downloaden van Docker Hub docker pull mariadb:10.11 # Image bouwen vanuit een Dockerfile docker build -t mijn-app-naam . # Image exporteren naar een bestand (voor migratie!) docker save -o mijn-app.tar mijn-app-naam # Image importeren op een andere server docker load -i mijn-app.tar # Ongebruikte images, containers en netwerken opruimen docker system prune -f
Systeem informatie
# Overzicht van schijfgebruik docker system df # Gedetailleerde info over Docker installatie docker info # Live CPU/geheugen gebruik per container docker stats
Netwerken begrijpen
Netwerken is vaak de bron van verwarring voor beginners. Eenmaal begrepen, is het eigenlijk simpel.
Twee werelden: intern en extern
Docker heeft twee "werelden" qua netwerk:
| Wereld | Wie bereikt het | Hoe |
|---|---|---|
| Intern Docker netwerk | Alleen containers onderling | Via servicenaam (bv. db) |
| Extern (host poorten) | Jij, de browser, het internet | Via poort-mapping (bv. 8080:80) |
Poort-mapping uitgelegd
Als je in docker-compose.yml schrijft "8080:80", dan zeg je: "Verzoeken die binnenkomen
op de host via poort 8080 worden doorgestuurd naar poort 80 binnenin de container."
De host-server is een kantoorgebouw met een begane grond (poorten). Elke container is een kantoor op een verdieping. De receptie (poort-mapping) stuurt bezoekers van de voordeur (host poort 8080) door naar het juiste kantoor (container intern poort 80). Zonder doorschakeling is het kantoor onbereikbaar van buitenaf, maar de kantoren onderling kunnen elkaar wel intern bereiken via de gangen (Docker netwerk).
# Alle Docker netwerken docker network ls # Details van het coin-trader netwerk docker network inspect coin-trader_coin-network # IP-adressen van draaiende containers docker inspect --format '{{.Name}} -> {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \ coin-trader-db-1 coin-trader-server-1 coin-trader-client-1
Let op: Interne IP-adressen (172.18.x.x) veranderen elke keer als je
docker compose down && docker compose up doet. Gebruik altijd de
servicenaam (zoals db) voor communicatie tussen containers β nooit een
hardcoded IP.
Data opslaan met Volumes
Dit is een van de meest onderschatte concepten β en de bron van kostbare dataloss als je het verkeerd doet.
Waarom volumes zo belangrijk zijn
Een container is van nature vluchtig. Alles wat erin wordt opgeslagen verdwijnt zodra de container wordt verwijderd of opnieuw gebouwd. Dat is soms handig (reproduceerbaar), maar voor een database is dat rampzalig.
Kritieke valkuil: Als je een MariaDB-container verwijdert zonder volume, ben je alle databasedata kwijt. Altijd een volume definiΓ«ren voor databases!
In onze docker-compose.yml staat dit geregeld:
db:
image: mariadb:10.11
volumes:
- db_data:/var/lib/mysql # β data gaat naar het volume 'db_data'
volumes:
db_data: # β Docker beheert dit volume
Soorten volumes
| Type | Hoe | Gebruik |
|---|---|---|
| Named Volume | db_data:/var/lib/mysql |
Database data β Docker beheert de locatie |
| Bind Mount | /host/pad:/container/pad |
Ontwikkeling β realtimecode synchronisatie |
| Anoniem | Geen naam opgegeven | Tijdelijk β vermijden! |
# Alle volumes tonen docker volume ls # Volume backuppen naar een tar-bestand docker run --rm \ -v coin-trader_db_data:/data \ -v /tmp:/backup \ alpine tar czf /backup/db_backup.tar.gz /data # Volume herstellen vanuit backup docker run --rm \ -v coin-trader_db_data:/data \ -v /tmp:/backup \ alpine tar xzf /backup/db_backup.tar.gz -C /
Docker Compose begrijpen
Met Docker Compose beheer je meerdere containers als één project β het hart van onze coin-trader setup.
Anatomie van een docker-compose.yml
services: # β Hier definieer je elke container db: # β Servicenaam (ook hostnaam op intern netwerk) image: mariadb:10.11 # β Welke image te gebruiken restart: always # β Automatisch herstarten bij crash env_file: # β Laad omgevingsvariabelen uit bestand - ./server/.env volumes: - db_data:/var/lib/mysql # β Data opslaan buiten de container ports: - "3307:3306" # β host_poort:container_poort networks: - coin-network # β Intern netwerk server: build: # β Bouw image via Dockerfile (in plaats van pull) context: ./server dockerfile: Dockerfile depends_on: # β Start pas NA de db container - db # ... (overige velden) networks: coin-network: # β Definieer het interne netwerk driver: bridge volumes: db_data: # β Definieer het named volume
Meerdere omgevingen (test vs. productie)
Met Docker Compose kun je eenvoudig meerdere omgevingen naast elkaar draaien met override-bestanden:
# Testomgeving (ander project-label, andere poorten) sudo docker compose -p coin-test -f docker-compose.yml -f docker-compose.test.yml up -d # Productieomgeving sudo docker compose -p coin-prod -f docker-compose.yml -f docker-compose.prod.yml up -d # Status per project sudo docker compose -p coin-test ps sudo docker compose -p coin-prod ps
Migreren naar een andere server
Dit is waar veel mensen de mist ingaan. Hier is het complete, veilige stappenplan.
De kracht van Docker is dat de applicatiecode reproductief is β je hoeft alleen de code + Dockerfiles + .env mee te nemen. Maar de databasedata zit in een volume en moet apart geback-upt worden!
Stap-voor-stap migratieplan
-
1
Maak een volledige backup van de database
Dit is de meest kritieke stap. Dump de database vanuit de draaiende container:
bash β Database dumpsudo docker exec coin-trader-db-1 \ mysqldump -u cointrader_user -p'Jouw-wachtwoord' cointrader_live \ > /tmp/backup_cointrader_live.sql # Controleer of het bestand gevuld is ls -lh /tmp/backup_cointrader_live.sql -
2
Exporteer de Docker images (optioneel)
Als de nieuwe server geen internettoegang heeft, kun je de images exporteren. Anders bouw je ze gewoon opnieuw (zie stap 5).
bash β Image export# Exporteer de gebouwde images sudo docker save -o /tmp/coin-server.tar coin-trader-server sudo docker save -o /tmp/coin-client.tar coin-trader-client -
3
Kopieer alle bestanden naar de nieuwe server
Je hebt nodig: de broncode (of Git repo),
.envbestanden, de database dump, en als je images exporteerde ook die tar-bestanden.bash β Bestanden kopiΓ«ren via SCP# Kopieer broncode (of gebruik git clone op de nieuwe server) scp -r /var/www/html/coin-trader gebruiker@nieuwe-server:/var/www/html/ # Kopieer database backup scp /tmp/backup_cointrader_live.sql gebruiker@nieuwe-server:/tmp/ # Kopieer images (als je ze geΓ«xporteerd hebt) scp /tmp/coin-server.tar /tmp/coin-client.tar gebruiker@nieuwe-server:/tmp/
-
4
Installeer Docker op de nieuwe server
bash β Docker installatie (nieuwe server)sudo apt-get update sudo apt-get install -y docker.io docker-compose-v2 sudo usermod -aG docker $USER
-
5
Start de containers op (zonder database data)
bash β Containers startencd /var/www/html/coin-trader # Als je images geΓ«xporteerd hebt: sudo docker load -i /tmp/coin-server.tar sudo docker load -i /tmp/coin-client.tar # Alles bouwen en starten sudo docker compose up -d --build # Wacht tot MariaDB is opgestart (Β±15 seconden) sudo docker logs coin-trader-db-1
-
6
Importeer de database in de nieuwe container
bash β Database importeren# Importeer de dump in de nieuwe MariaDB container sudo docker exec -i coin-trader-db-1 \ mysql -u cointrader_user -p'Jouw-wachtwoord' cointrader_live \ < /tmp/backup_cointrader_live.sql # Controleer of de data er in staat sudo docker exec coin-trader-db-1 \ mysql -u cointrader_user -p'Jouw-wachtwoord' cointrader_live \ -e "SHOW TABLES;"
-
7
Test alles grondig
Controleer de logs:
sudo docker compose logs server --tail=50. Test de API:curl http://localhost:3015/api/status. Controleer de frontend in de browser.
Wat je NOOIT mag vergeten bij migratie
De .env bestanden! Deze staan niet in de Git repository (terecht, want er zitten
wachtwoorden in). Kopieer ze altijd handmatig mee: server/.env en de root
.env.
Poortconflicten op de nieuwe server. Controleer wat er al draait:
ss -tlnp | grep -E '3306|3005|80'. Pas poorten in docker-compose.yml aan
indien nodig.
Firewall regels. Op de nieuwe server moeten de benodigde poorten open staan:
sudo ufw allow 80 && sudo ufw allow 443.
Veelgemaakte fouten & valkuilen
Leer van de fouten die iedereen maakt β inclusief wat we zelf zijn tegengekomen tijdens de installatie op deze server.
πͺ€ Valkuil 1: Data opslaan IN de container
Je schrijft bestanden rechtstreeks in de container (bv. uploads, logs, database). Als de container wordt verwijderd of herbouwd: alles weg.
β Fix: Gebruik altijd een named volume of bind mount voor data die bewaard moet blijven.πͺ€ Valkuil 2: Hardcoded IP-adressen gebruiken
Je gebruikt 172.18.0.4 als database-host in je applicatie. Na een
docker compose down/up krijgen containers andere IP-adressen en werkt niets meer.
DB_HOST=db.
πͺ€ Valkuil 3: .env bestanden vergeten bij migratie
Je kopieert de broncode maar vergeet de .env bestanden. De containers starten, maar de
applicatie geeft cryptische fouten over ontbrekende configuratie.
πͺ€ Valkuil 4: Poortconflicten
Je start een container met poort 3306, maar de host MariaDB gebruikt die al. De container crasht bij opstarten met "address already in use".
β Fix: Controleer altijd bezette poorten metss -tlnp en gebruik
afwijkende host-poorten (bv. 3307).
πͺ€ Valkuil 5: docker-compose v1 vs v2
docker-compose (oud, Python, v1.29) is incompatibel met Docker Engine 28+. Je krijgt
cryptische errors zoals "ContainerConfig KeyError".
docker compose (zonder koppelteken) β dat is de
moderne v2 plugin.
πͺ€ Valkuil 6: Te groot bouwen (alles in één container)
Beginners stoppen database, webserver en applicatie in één container. Dat werkt, maar is niet de Docker-filosofie en maakt schalen, herstarten en backuppen lastig.
β Fix: ΓΓ©n verantwoordelijkheid per container. Zoals bij coin-trader: db, server, client als aparte containers.πͺ€ Valkuil 7: Schijf volloopt door oude images
Elke rebuild maakt nieuwe image layers. Na verloop van tijd staat de schijf vol met verlaten images (dangling images).
β Fix: Periodiek opruimen metsudo docker system prune -f. Controleer
schijfgebruik met sudo docker system df.
Docker Cheatsheet
De meest gebruikte commando's op één plek.
ββββ CONTAINERS ββββββββββββββββββββββββββββββββββββββββββββ docker ps # Draaiende containers docker ps -a # Alle containers docker start|stop|restart <naam> # Starten/stoppen/herstarten docker rm -f <naam> # Forceer verwijderen docker logs --tail=100 <naam> # Logs bekijken docker exec -it <naam> sh # Shell in container docker stats # CPU/geheugen live ββββ IMAGES ββββββββββββββββββββββββββββββββββββββββββββββββ docker images # Alle images docker build -t naam . # Bouwen vanuit Dockerfile docker pull naam:tag # Downloaden van Docker Hub docker rmi naam # Image verwijderen docker save -o bestand.tar naam # Exporteren docker load -i bestand.tar # Importeren ββββ VOLUMES βββββββββββββββββββββββββββββββββββββββββββββββ docker volume ls # Alle volumes docker volume inspect <naam> # Details docker volume rm <naam> # Verwijderen (pas op!) ββββ COMPOSE (gebruik ALTIJD zonder koppelteken) βββββββββββ docker compose up -d # Alles starten in achtergrond docker compose up -d --build # Starten + images herbouwen docker compose down # Alles stoppen + verwijderen docker compose ps # Status docker compose logs <service> # Logs van een service docker compose restart <service> # Herstarten ββββ SYSTEEM βββββββββββββββββββββββββββββββββββββββββββββββ docker system df # Schijfgebruik overview docker system prune -f # Ruim ongebruikte zaken op docker info # Systeem info ββββ MIGRATIE VOLGORDE βββββββββββββββββββββββββββββββββββββ # 1. Database dumpen docker exec -i db mysqldump -u user -p'pass' dbname > backup.sql # 2. Bestanden kopiΓ«ren (scp of git) # 3. .env bestanden kopiΓ«ren (!) # 4. Docker installeren op nieuwe server # 5. docker compose up -d --build # 6. Database importeren docker exec -i db mysql -u user -p'pass' dbname < backup.sql