spinny:~/writing $ less docker-containers-complete-guide.md
12Docker veranderde hoe we software bouwen, distribueren en draaien. In plaats van "het werkt op mijn machine" garandeert Docker dat je applicatie overal hetzelfde draait - op je laptop, op de machine van een collega, in CI/CD en in productie. In deze gids gaan we van nul tot het deployen van een echte applicatie.34## Wat is Docker?56Docker is een platform dat je applicatie en al zijn afhankelijkheden verpakt in een gestandaardiseerde eenheid genaamd een **container**. Een container is een geisoleerd, lichtgewicht proces dat de kernel van het host-besturingssysteem deelt maar een eigen bestandssysteem, netwerk en procesruimte heeft.78```mermaid9graph TD10 subgraph "Traditional Deployment"11 A1[App 1] --> OS1[Guest OS]12 A2[App 2] --> OS2[Guest OS]13 OS1 --> HV[Hypervisor]14 OS2 --> HV15 HV --> HW1[Hardware]16 end1718 subgraph "Docker Deployment"19 B1[App 1] --> D1[Container]20 B2[App 2] --> D2[Container]21 D1 --> DE[Docker Engine]22 D2 --> DE23 DE --> HW2[Hardware]24 end25```2627### Containers vs Virtuele Machines2829| Aspect | Containers | Virtuele Machines |30|--------|-----------|-------------------|31| **Opstart** | Seconden | Minuten |32| **Grootte** | MBs | GBs |33| **OS** | Deelt host-kernel | Volledig gast-OS |34| **Isolatie** | Procesniveau | Hardwareniveau |35| **Prestaties** | Bijna native | Overhead van hypervisor |36| **Dichtheid** | Honderden per host | Tientallen per host |3738## Docker Installeren3940```bash41# macOS42brew install --cask docker4344# Ubuntu/Debian45curl -fsSL https://get.docker.com | sh46sudo usermod -aG docker $USER4748# Verify installation49docker --version50docker run hello-world51```5253## Kernconcepten5455### Images5657Een image is een alleen-lezen sjabloon met instructies voor het aanmaken van een container. Beschouw het als een momentopname van je applicatie en zijn omgeving.5859```bash60# Pull an image from Docker Hub61docker pull node:20-alpine6263# List local images64docker images6566# Remove an image67docker rmi node:20-alpine68```6970### Containers7172Een container is een draaiende instantie van een image. Je kunt containers aanmaken, starten, stoppen en verwijderen.7374```bash75# Run a container76docker run -d --name my-app -p 3000:3000 node:20-alpine7778# List running containers79docker ps8081# List all containers (including stopped)82docker ps -a8384# Stop a container85docker stop my-app8687# Remove a container88docker rm my-app8990# View logs91docker logs my-app9293# Execute a command inside a running container94docker exec -it my-app sh95```9697## Een Dockerfile Schrijven9899Een Dockerfile is een tekstbestand met instructies om een image te bouwen. Elke instructie maakt een laag aan.100101### Basis Dockerfile voor een Node.js App102103```dockerfile104# Use an official Node.js runtime as base image105FROM node:20-alpine106107# Set working directory108WORKDIR /app109110# Copy package files first (better caching)111COPY package.json package-lock.json ./112113# Install dependencies114RUN npm ci --only=production115116# Copy application code117COPY . .118119# Expose the port the app runs on120EXPOSE 3000121122# Command to run the application123CMD ["node", "server.js"]124```125126### Bouwen en Draaien127128```bash129# Build the image130docker build -t my-node-app .131132# Run the container133docker run -d -p 3000:3000 my-node-app134135# Visit http://localhost:3000136```137138## Multi-Stage Builds139140Multi-stage builds houden je productie-images klein door de build-omgeving te scheiden van de runtime.141142```dockerfile143# Stage 1: Build144FROM node:20-alpine AS builder145WORKDIR /app146COPY package.json package-lock.json ./147RUN npm ci148COPY . .149RUN npm run build150151# Stage 2: Production152FROM node:20-alpine AS runner153WORKDIR /app154COPY --from=builder /app/dist ./dist155COPY --from=builder /app/node_modules ./node_modules156COPY --from=builder /app/package.json ./157EXPOSE 3000158CMD ["node", "dist/server.js"]159```160161Dit produceert een image met alleen de gecompileerde uitvoer en productie-afhankelijkheden - geen broncode, geen ontwikkelingsafhankelijkheden, geen build-tools.162163### Next.js Multi-Stage Voorbeeld164165```dockerfile166FROM node:20-alpine AS deps167WORKDIR /app168COPY package.json package-lock.json ./169RUN npm ci170171FROM node:20-alpine AS builder172WORKDIR /app173COPY --from=deps /app/node_modules ./node_modules174COPY . .175RUN npm run build176177FROM node:20-alpine AS runner178WORKDIR /app179ENV NODE_ENV=production180COPY --from=builder /app/public ./public181COPY --from=builder /app/.next/standalone ./182COPY --from=builder /app/.next/static ./.next/static183EXPOSE 3000184CMD ["node", "server.js"]185```186187## Volumes: Persistente Data188189Standaard gaat data in een container verloren wanneer de container wordt verwijderd. Volumes lossen dit op.190191```bash192# Create a named volume193docker volume create my-data194195# Run with a volume196docker run -d -v my-data:/app/data my-app197198# Bind mount (map host directory to container)199docker run -d -v $(pwd)/data:/app/data my-app200201# List volumes202docker volume ls203```204205## Netwerken206207Docker maakt geisoleerde netwerken aan zodat containers kunnen communiceren.208209```bash210# Create a custom network211docker network create my-network212213# Run containers on the same network214docker run -d --name api --network my-network my-api215docker run -d --name db --network my-network postgres:16216217# Containers can reach each other by name218# From "api" container: postgres://db:5432219```220221```mermaid222graph LR223 subgraph "my-network"224 API[api container\nport 3000] -- "db:5432" --> DB[db container\nport 5432]225 end226 User -- "localhost:3000" --> API227```228229## Docker Compose230231Docker Compose laat je multi-container applicaties definieren en draaien met een enkel YAML-bestand.232233### docker-compose.yml234235```yaml236services:237 api:238 build: ./api239 ports:240 - "3000:3000"241 environment:242 - DATABASE_URL=postgres://user:pass@db:5432/mydb243 depends_on:244 - db245246 db:247 image: postgres:16-alpine248 environment:249 - POSTGRES_USER=user250 - POSTGRES_PASSWORD=pass251 - POSTGRES_DB=mydb252 volumes:253 - pgdata:/var/lib/postgresql/data254 ports:255 - "5432:5432"256257 redis:258 image: redis:7-alpine259 ports:260 - "6379:6379"261262volumes:263 pgdata:264```265266### Commando's267268```bash269# Start all services270docker compose up -d271272# View logs273docker compose logs -f274275# Stop all services276docker compose down277278# Rebuild and restart279docker compose up -d --build280281# Scale a service282docker compose up -d --scale api=3283```284285## .dockerignore286287Net als `.gitignore` voorkomt dit bestand dat onnodige bestanden naar de image worden gekopieerd.288289```plaintext290node_modules291.git292.env293*.md294.next295dist296coverage297```298299## Best Practices voor Productie300301### 1. Gebruik Kleine Basis-Images302303```dockerfile304# Bad: 1GB+305FROM node:20306307# Good: ~180MB308FROM node:20-alpine309```310311### 2. Draai Niet als Root312313```dockerfile314FROM node:20-alpine315RUN addgroup -S app && adduser -S app -G app316USER app317WORKDIR /home/app318COPY --chown=app:app . .319```320321### 3. Gebruik Specifieke Image Tags322323```dockerfile324# Bad: can change unexpectedly325FROM node:latest326327# Good: pinned version328FROM node:20.11-alpine3.19329```330331### 4. Benut de Build Cache332333Orden je Dockerfile-instructies van minst naar meest frequent gewijzigd:334335```dockerfile336FROM node:20-alpine337WORKDIR /app338339# These change rarely - cached340COPY package.json package-lock.json ./341RUN npm ci --only=production342343# This changes often - not cached344COPY . .345```346347### 5. Health Checks348349```dockerfile350HEALTHCHECK --interval=30s --timeout=3s --retries=3 \351 CMD wget -qO- http://localhost:3000/health || exit 1352```353354### 6. Gebruik Omgevingsvariabelen355356```dockerfile357ENV NODE_ENV=production358ENV PORT=3000359```360361## Spiekbriefje voor Veelgebruikte Docker Commando's362363```bash364# Images365docker build -t name:tag . # Build image366docker images # List images367docker rmi image_name # Remove image368docker image prune # Remove unused images369370# Containers371docker run -d -p 3000:3000 image # Run detached372docker ps # List running373docker stop container_name # Stop374docker rm container_name # Remove375docker logs -f container_name # Follow logs376docker exec -it container sh # Shell into container377378# Compose379docker compose up -d # Start services380docker compose down # Stop services381docker compose logs -f # Follow all logs382docker compose ps # List services383384# Cleanup385docker system prune -a # Remove everything unused386```387388## Van Docker naar Kubernetes389390Docker beheert individuele containers. Wanneer je honderden containers over meerdere servers moet orchestreren, heb je Kubernetes nodig. Docker en Kubernetes vullen elkaar aan:3913921. **Docker**: bouwt en draait containers3932. **Kubernetes**: orchestreert containers op schaal (planning, schaling, zelfherstel)394395Als je geinteresseerd bent in de volgende stap, bekijk dan mijn artikel over Introductie tot Kubernetes.396397## Conclusie398399Docker is een fundamentele vaardigheid voor moderne ontwikkelaars. Het elimineert omgevingsinconsistenties, vereenvoudigt deployment en vormt de basis voor containerorchestratie met Kubernetes. Begin met een eenvoudig Dockerfile, stap over naar Docker Compose voor multi-service apps en adopteer multi-stage builds en beveiligingsbest practices naarmate je groeit.400401De beste manier om Docker te leren is een project te containeriseren waar je al aan werkt. Begin vandaag.402
:Docker voor Beginners: Van Je Eerste Image tot Productie-Deploylines 1-402 (END) — press q to close