dhbw phase 4: alle 8 kapitel slide-MD komplett (4176 zeilen → ~1100 zeilen, ~210 slides total)

kap1 web_eng (104 → 57 slides): kurs-intro + prüfungsleistung-übersicht, internet-timeline (4 milestones), URL→pixel-7-schritte, HTTP, onboarding (node 24 LTS via fnm, brew/winget paket-manager, VS Code/Chromium/Hoppscotch, git setup mit SSH-key, projekt-skeleton), HTML (anatomie, self-closing, grundgerüst, semantik-tabelle, native elemente details/dialog/input-types), A11y volle tiefe (BFSG+EAA 28.06.2025, WCAG-POUR, Perceivable/Operable/Understandable/Robust einzeln mit code, testen mit lighthouse/axe/manuell), CSS-vorschau (anatomie/selektoren/spezifizität/box-model/farben/einheiten/pseudos), JS-vorschau (ECMAScript-timeline/grundlagen/DOM/fetch), frameworks-übersicht (react/vue/svelte/astro counter-vergleich), build-tools/rendering-modi, selbstlernen erste-eigene-seite

kap2 css_extended (13 → 28 slides): box-model + box-sizing global, flexbox basics+achsen+item-properties+typische-patterns, grid basics+areas+auto-layout, flexbox-vs-grid tabelle, mobile-first media queries, viewport-meta, fluid sizing mit clamp(), transitions (was performant), keyframe-animationen, prefers-reduced-motion pflicht, CSS-variablen + dark-mode-pattern, CSS functions (calc/clamp/gradient/oklch/filter), CSS-frameworks tabelle + tailwind pro/contra, selbstlernen flexboxfroggy+cssgridgarden

kap3 nodejs_basics (~22 → 20 slides): was-ist-node + warum-node-tabelle, fnm versions-manager + node 24 LTS + .nvmrc, npm init + package.json (mit type:module), ESM modern + CJS legacy, built-in modules (node: prefix), HTTP-server eingebaut + express, REST mit express (GET/POST/PUT/DELETE-pattern), alternative frameworks tabelle (fastify/hono/koa), routing-pattern mit Router, fetch global ab node 18, error-handling res.ok, environment variables mit dotenv + niemals committen, selbstlernen mini-API

kap4 nodejs_advanced (~24 → 22 slides): synchron-vs-asynchron, promise then-vs-async/await, sequenziell-vs-parallel + Promise.all, promise-methoden tabelle (all/allSettled/race/any), fs/promises lesen+schreiben, streams pipeline für große dateien, DB-übersicht SQLite/Postgres/MongoDB/Redis, better-sqlite3 prepared statements, ORM-übersicht (drizzle/prisma/knex), CRUD-pattern express+sqlite, auth-strategien tabelle, bcrypt für passwörter, JWT-pattern mit httpOnly+secure cookie, try/catch + error-klassen, globaler express-error-handler, selbstlernen REST+DB+auth

kap5 testing (~23 → 23 slides): test-pyramide 70%/20%/10%, vitest setup + describe/it/expect, AAA-pattern, matchers tabelle, TDD red-green-refactor mit konkretem beispiel, supertest für API-integration, test-DB :memory: + lifecycle hooks, mocking mit vi.spyOn (sparsam), playwright E2E mit role-based-locators, E2E-vs-unit tradeoffs, coverage realistisch (80% gut), CI mit github-actions, selbstlernen tests-schreiben

kap6 typescript (~30 → 19 slides): was-ist-TS + warum-tabelle, setup mit tsx + tsconfig.json + strict:true, basis-typen + any vermeiden, object-typen + interfaces + type-aliases, function-typen (Promise<T>), union + discriminated union + intersection, generics + constraints, built-in generics (Partial/Omit/Record), typ-inference (nicht über-annotieren), typen für REST-API mit express, zod runtime-validation + z.infer, any-vs-unknown-vs-never, best practices, selbstlernen API-zu-TS-migration

kap7 docker (~24 → 24 slides): auf-meinem-rechner-läufts problem, container-vs-VM tabelle, docker basics (image/container/dockerfile/registry), dockerfile hallo-welt, schichten + cache-optimierung, multi-stage build, base-image-größen tabelle (alpine ~180MB vs node ~1.1GB), .dockerignore, compose.yml beispiel, compose-commands, networking (service-namen als hostnamen), volumes (named vs bind), env+secrets, github-actions docker-build, registry-übersicht, health-checks, best-practices, selbstlernen app-dockerizen

kap8 best_practices (~30 → 28 slides): sinnvolle commits + conventional commits format, .gitignore template, branches+PRs workflow, README-template mit 6 sektionen, SOLID 5 prinzipien tabelle, S-single-responsibility + D-dependency-inversion mit code, 12-factor wichtigste (codebase/deps/config/processes/disposability), factor 3 config-in-env, factor 6 stateless-processes, SemVer MAJOR.MINOR.PATCH, package.json caret/tilde/exakt, code-reviews geben+nehmen + checkliste, doku-quellen tabelle (MDN/nodejs/npm/tc39/caniuse), stack-overflow gut-nutzen, AI-assistenten pro/contra (verstehen-nicht-kopieren), selbstlernen projekt-hygiene-check

build geht durch alle 8 kapitel-html+pdf
render-spot-check: cover-slide-dark-mode-invert, git-setup code-syntax-highlight, css-anatomie code-blocks — alle sauber

kein deploy, nur build+verify
This commit is contained in:
2026-05-14 17:14:02 +02:00
parent 443150c40e
commit 4053185e74
8 changed files with 2623 additions and 3163 deletions
+268 -232
View File
@@ -9,339 +9,375 @@ title: Docker und Service Orchestration
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
:root { --color-foreground: #1a1a2e; --color-highlight: #d63384; }
section.invert { --color-foreground: #fff; }
section { font-size: 1.4rem; }
h1 { color: #a02060; }
section.invert h1 { color: #fff; }
h2 { color: #1f2937; }
pre { background: #0f0f23; color: #f48fb1; border-radius: 8px; border-left: 3px solid #d63384; }
pre code { background: transparent; color: inherit; }
code { background: #1a1a2e; color: #f48fb1; padding: 0.15em 0.4em; border-radius: 4px; }
a { color: var(--color-highlight); }
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
<!-- _class: lead -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 9 — Docker, Proxies and DBs
# Kapitel 7 — Docker
## Container für reproducible Setups
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# „Auf meinem Rechner läuft's"
# Docker
Das klassische Software-Problem:
## Service Orchestration
- Entwickler: Node 22 + Postgres 16 lokal
- Server: Node 18 + Postgres 14
- → bricht beim Deploy
**Lösung:** App + Abhängigkeiten als **Container** verpacken.
---
# Inhalt
# Container vs. VM
1. Architektur: Reverse Proxy + API + DB
2. Container vs VM
3. `Dockerfile`
4. `compose.yml`
5. Docker CLI / Compose Befehle
6. Live Demo: `dhbw-docker`
| | Container | Virtual Machine |
|--|-----------|-----------------|
| Startzeit | ms | min |
| Größe | MB | GB |
| Kernel | shared (Host-Linux) | eigener |
| Isolation | Prozess-Level | Hardware-Level |
| Overhead | minimal | hoch |
Container = leichtgewichtige Prozess-Isolation.
---
# Architektur
# Docker — die Idee
```
Browser (localhost:10007)
│
▼
┌──────────────────┐
│ Reverse Proxy │ (nginx / traefik)
└─────────┬────────┘
┌────┴────┐
▼ ▼
┌─────────┐ ┌────────┐
│ Web │ │ API │
│ App │ │ (Node) │
└────┬────┘ └────┬───┘
└─────┬──────┘
▼
┌────────┐
│ DB │ (Postgres)
└────────┘
- **Image** = Schnappschuss (App + Files + OS-Layer)
- **Container** = laufende Instanz eines Images
- **Dockerfile** = Anleitung zum Image-Bauen
- **Registry** = Image-Speicher (Docker Hub, GHCR)
```sh
docker pull node:24
docker run node:24 node --version
```
---
# Container vs VM
| | VM | Container |
|---|----|-----------|
| Kernel | eigener | Host-Kernel |
| Boot | Minuten | Sekunden |
| Größe | GB | MB |
| Isolation | stark | namespaces, cgroups |
| Image | komplett | Layer (diff) |
→ **Docker** = Container-Engine + Image-Format + Registry.
---
# Dockerfile – Express API
# Dockerfile — Hallo Welt
```dockerfile
FROM node:lts-slim AS builder
FROM node:24-alpine
WORKDIR /app
COPY ./package*.json ./
RUN npm ci --no-audit --no-fund --no-progress --loglevel=error
COPY package*.json ./
RUN npm ci
COPY ./ ./
COPY . .
ENV NODE_ENV="production"
EXPOSE 8080
CMD ["npm", "start"]
EXPOSE 3000
CMD ["node", "src/server.js"]
```
**`npm ci`** statt `npm i` für reproduzierbare Builds.
```sh
docker build -t mein-app .
docker run -p 3000:3000 mein-app
```
---
# Dockerfile-Schichten verstehen
Jedes `RUN`/`COPY`/`ADD` = neue Schicht.
```dockerfile
# Schlecht — npm install nach JEDER Code-Änderung
COPY . .
RUN npm ci
# Gut — package.json zuerst, npm install gecached
COPY package*.json ./
RUN npm ci
COPY . .
```
**Build-Cache** spart Minuten.
---
# Multi-Stage Build
```dockerfile
FROM node:lts-slim AS builder
# Stage 1: Build mit allem
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm ci && npm run build
FROM node:lts-slim AS runtime
# Stage 2: nur Runtime
FROM node:24-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 8080
CMD ["node", "dist/index.js"]
EXPOSE 3000
CMD ["node", "dist/server.js"]
```
→ kleines Final-Image, keine Build-Tools im Runtime.
Finales Image: kleiner, sicherer (kein Build-Tools drin).
---
# Image-Größen reduzieren
| Base | Größe |
|------|------:|
| `node:24` | ~1.1 GB |
| `node:24-slim` | ~280 MB |
| `node:24-alpine` | **~180 MB** |
| Distroless | ~150 MB |
**`alpine`** = Alpine Linux, sehr klein. **Distroless** = nur Runtime, kein Shell.
---
# .dockerignore
Verhindert dass Müll ins Image kommt:
```
node_modules
dist
.git
.env
*.log
coverage
build/
dist/
.DS_Store
README.md
.github/
```
Spart Build-Kontext und verhindert Geheimnis-Leaks.
Schneller Build, kleineres Image, weniger Secrets im Image.
---
# compose.yml – Services
<!-- _class: lead -->
# docker compose
---
# Multi-Container — die Realität
Eine App braucht oft:
- App-Server
- Datenbank
- Cache (Redis)
- Reverse Proxy
**docker compose** orchestriert das.
---
# compose.yml — Beispiel
```yaml
name: dhbw-docker-app
services:
api:
build: ./api
container_name: dhbw-api
environment:
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
DATABASE_NAME: ${DATABASE_NAME}
DATABASE_HOST: db
DATABASE_PORT: 5432
app:
build: .
ports:
- "10000:8080"
networks: [net, data]
- "3000:3000"
environment:
DATABASE_URL: postgres://user:pass@db:5432/app
depends_on:
db:
condition: service_healthy
```
- db
---
# compose.yml – Datenbank + Proxy
```yaml
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- dbdata:/var/lib/postgresql/data
networks: [data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$DATABASE_USERNAME"]
interval: 5s
retries: 5
proxy:
image: nginx:alpine
ports: ["10007:80"]
volumes: ["./nginx.conf:/etc/nginx/nginx.conf:ro"]
networks: [net]
depends_on: [api]
- db-data:/var/lib/postgresql/data
volumes:
dbdata:
networks:
net:
data:
db-data:
```
---
# Docker Compose – Befehle
# compose-Commands
```bash
docker compose -p "dhbw-docker-app" \
-f compose.yml \
--env-file .env \
up \
--build \
--remove-orphans \
--force-recreate
```
```sh
docker compose up # alles starten (Foreground)
docker compose up -d # detached (background)
docker compose down # alles stoppen + entfernen
docker compose down -v # ... und Volumes löschen
| Flag | Bedeutung |
|------|-----------|
| `-p` | Projektname |
| `-f` | mehrere Compose-Files möglich |
| `--env-file` | `.env` einlesen |
| `--build` | Images neu bauen |
| `--remove-orphans` | verwaiste Container weg |
| `--force-recreate` | Container neu starten |
---
# Compose – Alltag
```bash
docker compose up -d # detached starten
docker compose ps # Status
docker compose logs -f api # Logs streamen
docker compose exec api sh # Shell im Container
docker compose down -v # Stop + Volumes löschen
docker compose restart api # Neustart einzelner Service
docker compose logs -f app # Logs streamen
docker compose exec app sh # Shell im laufenden Container
docker compose ps # Status sehen
```
---
# Health Checks
# Networking in compose
Standard: alle Services im selben `default`-Netzwerk.
```yaml
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
services:
app:
environment:
# `db` ist der Service-Name → DNS-Hostname
DATABASE_URL: postgres://...@db:5432/app
```
`depends_on: { condition: service_healthy }` wartet, bis Health passt.
**Service-Namen werden zu Hostnamen.** Kein localhost zwischen Containern.
---
# Networks: Segmentierung
- **`net`** – öffentlich erreichbar (Proxy — App)
- **`data`** – nur intern (App — DB)
- DB ist **nicht** vom Internet erreichbar
- Service-DNS: `api`, `db`, `proxy` (Container-Name = Hostname)
→ Defense in Depth.
---
# Volumes – Daten persistent
# Volumes — Daten überleben
```yaml
volumes:
dbdata: # benannt
- ./code:/app # bind mount (dev)
- /app/node_modules # anonymous (überschreibt bind)
db-data: # named volume (managed by Docker)
services:
db:
volumes:
- db-data:/var/lib/postgresql/data # named
- ./backups:/backups # bind mount
```
`docker compose down` ohne `-v` lässt Volumes leben.
**Named Volumes** für DB-Daten. **Bind Mounts** für lokale Config.
---
# Live Demo
# Environment + Secrets
Repo: https://github.com/nextlevelshit/dhbw-docker
```bash
git clone https://github.com/nextlevelshit/dhbw-docker
cd dhbw-docker
cp .env.example .env
docker compose up --build
```yaml
services:
app:
env_file: .env # ganze .env-Datei laden
environment:
NODE_ENV: production # einzeln setzen
DATABASE_URL: ${DATABASE_URL} # vom Host
```
→ http://localhost:10007
**Secrets nicht ins compose.yml.** Lieber `.env` (gitignored) oder Docker Secrets.
---
# 100 Concepts of Docker (Exkurs)
<!-- _class: lead -->
- Image · Container · Volume · Network
- Dockerfile · Layer Cache · BuildKit
- Compose · Stack · Swarm · Kubernetes
- Registry · Tag · Digest
- Dev/Prod-Parity (12-Factor)
- Secrets · Configs
- Logging Drivers
- Health Checks · Restart Policies
# CI/CD
---
# Docker in GitHub Actions
```yaml
name: Build + Push
on:
push:
branches: [main]
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: user/app:latest
```
---
# Container-Registry
| Registry | Stärke |
|----------|--------|
| **Docker Hub** | Standard, kostenlos für Public |
| **GHCR** (GitHub) | mit Repo verbunden |
| **GitLab Registry** | mit Repo verbunden |
| **AWS ECR** | für AWS-Hosting |
```sh
# Tag + Push
docker tag mein-app ghcr.io/user/mein-app:v1.0.0
docker push ghcr.io/user/mein-app:v1.0.0
```
---
# Health-Checks
```dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
```
```javascript
// In Express
app.get('/health', (req, res) => res.json({ ok: true }));
```
Orchestrators (k8s, ECS, Coolify) entscheiden via HealthCheck, ob neu starten.
---
# Best Practices
- **`alpine`** oder **distroless** als Base
- **Multi-stage builds** für kleinere Images
- **`.dockerignore`** anlegen
- **Non-root user** im Container (`USER node`)
- **`COPY` nach `RUN npm ci`** für Cache-Hit
- **Tags statt `latest`** für Reproducibility
- **Health-Checks** für Orchestration
- **Secrets via env, niemals im Image**
---
<!-- _class: aufgabe -->
# Selbstlernen — App dockerizen
1. **Dockerfile** für eure Node-API (multi-stage)
2. **.dockerignore** mit node_modules, .env, .git
3. **compose.yml** mit App + Postgres
4. **`docker compose up`** lokal starten
5. **Health-Check** auf `/health`-Endpoint
**Bonus:** GitHub-Actions-Workflow, der Image bei jedem Push baut.
---
# Zusammenfassung
**Heute gelernt:**
- Container vs. VM — Prozess-Isolation, leicht + schnell
- **Dockerfile** mit `FROM`, `COPY`, `RUN`, `CMD`
- **Multi-stage Builds** für kleine Production-Images
- **alpine** + Distroless als Base
- **docker compose** für Multi-Container-Setups
- **Volumes** für persistente Daten
- **Service-Namen als Hostnamen** im compose-Netzwerk
- **CI/CD** mit GitHub Actions + Registry
**Nächste Stunde:** Best Practices — Git, README, SOLID, 12-Factor.