
Sag Auf Wiedersehen zu Docker Volumes

Hast du jemals versucht, Docker Volumes für Hot-Reloading in deiner Web-App zu verwenden? Wenn du die gleiche schreckliche Erfahrung wie ich gemacht hast, wirst du das neueste Feature lieben, das Docker gerade veröffentlicht hat: docker-compose watch! Lass mich dir zeigen, wie du dein bestehendes Projekt aufwertest, um ein wunderbares Docker-Dev-Setup zu haben, das dein Team tatsächlich gerne nutzen wird 🤩
TL;DR: Schau dir diese docker-compose Datei und die offizielle Dokumentation an
Lass uns loslegen!
Einführung
Docker hat gerade Docker Compose Watch mit Docker Compose Version 2.22 veröffentlicht. Mit diesem neuen Feature kannst du docker-compose watch
anstelle von docker-compose up
verwenden und automatisch deinen lokalen Quellcode mit dem Code in deinem Docker-Container synchronisieren, ohne Volumes verwenden zu müssen!
Schauen wir uns an, wie das in einem echten Projekt funktioniert, indem wir ein Projekt verwenden, über das ich zuvor geschrieben habe.
In diesem Projekt habe ich ein Monorepo mit einem Frontend, Backend und einigen zusätzlichen Bibliotheken für die UI und Datenbank.
├── apps
│ ├── api
│ └── web
└── packages
├── database
├── eslint-config-custom
├── tsconfig
└── ui
Beide Apps (api
und web
) sind bereits dockerisiert und die Dockerfiles befinden sich im Root des Projekts (1, 2)
Die docker-compose.yml
Datei würde so aussehen:
services:
web:
build:
dockerfile: web.Dockerfile
ports:
- "3000:3000"
depends_on:
- api
api:
build:
dockerfile: api.Dockerfile
ports:
- "3001:3000"from within the Docker network
Das ist schon ziemlich gut, aber wie du bereits weißt, ist es nervig, damit während der Entwicklung zu arbeiten. Du musst deine Docker-Images neu bauen, wann immer du deinen Code änderst, obwohl deine Apps wahrscheinlich Hot-Reloading von Haus aus unterstützen (oder mit etwas wie Nodemon, wenn nicht).
Um das zu verbessern, führt Docker Compose Watch ein neues Attribut namens watch
ein. Das watch-Attribut enthält eine Liste von sogenannten Regeln, die jeweils einen Pfad enthalten, den sie beobachten, und eine Aktion, die ausgeführt wird, sobald sich eine Datei in dem Pfad ändert.
Sync
Wenn du einen Ordner zwischen deinem Host und deinem Container synchronisiert haben möchtest, würdest du hinzufügen:
services:
web: # gekürzt der Übersichtlichkeit halber
build:
dockerfile: web.Dockerfile
develop:
watch:
- action: sync
path: ./apps/web
target: /app/apps/web
Wann immer sich eine Datei auf deinem Host im Pfad ./apps/web/
ändert, wird sie zu deinem Container nach /app/apps/web
synchronisiert (kopiert). Das zusätzliche app im Zielpfad ist erforderlich, weil dies unser WORKDIR
ist, das im Dockerfile definiert ist. Das ist das Hauptding, das du wahrscheinlich verwenden wirst, wenn du hot-reloadable Apps hast.
Rebuild
Wenn du Apps hast, die kompiliert werden müssen oder Dependencies, die du neu installieren musst, gibt es auch eine Aktion namens rebuild. Anstatt einfach die Dateien zwischen dem Host und dem Container zu kopieren, wird es den Container neu bauen und neustarten. Das ist super hilfreich für deine npm-Dependencies! Lass uns das hinzufügen:
services:
web: # gekürzt der Übersichtlichkeit halber
build:
dockerfile: web.Dockerfile
develop:
watch:
- action: sync
path: ./apps/web
target: /app/apps/web
- action: rebuild
path: ./package.json
target: /app/package.json
Wann immer sich unsere package.json ändert, werden wir jetzt unser gesamtes Dockerfile neu bauen, um die neuen Dependencies zu installieren.
Sync+Restart
Neben nur Synchronisieren und Neubauen gibt es auch etwas dazwischen namens sync+restart. Diese Aktion wird zuerst die Verzeichnisse synchronisieren und dann sofort deinen Container neustarten, ohne neu zu bauen. Die meisten Frameworks haben normalerweise Konfigurationsdateien (wie next.config.js
), die nicht hot-reloaded werden können (nur sync reicht nicht aus), aber auch keinen langsamen Rebuild benötigen.
Das würde deine compose-Datei zu diesem ändern:
services:
web: # gekürzt der Übersichtlichkeit halber
build:
dockerfile: web.Dockerfile
develop:
watch:
- action: sync
path: ./apps/web
target: /app/apps/web
- action: rebuild
path: ./package.json
target: /app/package.json
- action: sync+restart
path: ./apps/web/next.config.js
target: /app/apps/web/next.config.js
Nachteile
Wie immer gibt es kein kostenloses Mittagessen und ein paar Nachteile 😬
Das größte Problem mit dem neuen watch
-Attribut ist, dass die Pfade noch sehr grundlegend sind. Die Dokumentation besagt, dass Glob-Muster noch nicht unterstützt werden, was zu einer riesigen Anzahl von Regeln führen kann, wenn du spezifisch sein willst.
Hier sind einige Beispiele, was funktioniert und was nicht:
✅ apps/web
Das wird alle Dateien in ./apps/web
matchen (z.B. ./apps/web/README.md
, aber auch ./apps/web/src/index.tsx
)
❌ build/**/!(*.spec|*.bundle|*.min).js
Globs werden leider (noch?) nicht unterstützt
❌ ~/Downloads
Alle Pfade sind relativ zum Projektstamm!
Nächste Schritte
Wenn du immer noch nicht zufrieden mit deinem Docker-Setup bist, gibt es noch viele Möglichkeiten, es zu verbessern!
Zusammenarbeit ist ein großer Teil der Softwareentwicklung und in Silos zu arbeiten kann deinem Team ernsthaft schaden. Langsame Docker-Builds und komplizierte Setups helfen nicht! Um dem entgegenzuwirken und eine Kultur der Zusammenarbeit zu fördern, kannst du Docker-Erweiterungen wie Livecycle verwenden, um deine lokalen docker-compose Apps sofort mit deinen Teamkollegen zu teilen. Da du bereits Docker und docker-compose verwendest, musst du nur die Docker Desktop Extension installieren und auf den Share-Toggle klicken. Deine Apps werden dann ins Internet getunnelt und du kannst deine einzigartige URL mit deinem Team teilen, um Feedback zu bekommen! Ich habe mehr darüber in diesem Post geschrieben, wenn du mehr Anwendungsfälle von Livecycle auschecken willst :)
Stelle wie immer sicher, dass dein Dockerfile Best Practices folgt, besonders bei Multi-Stage-Builds und Caching. Obwohl das das Schreiben des anfänglichen Dockerfiles schwieriger machen könnte, wird es deine Docker-Apps während der Entwicklung viel angenehmer zu verwenden machen.
Eine grundlegende .dockerignore
-Datei zu erstellen und die Dependency-Installation vom Code-Building zu trennen, bringt viel!
Fazit
Wie immer hoffe ich, dass du heute etwas Neues gelernt hast! Lass mich wissen, wenn du Hilfe beim Einrichten deines Docker-Projekts brauchst oder wenn du anderes Feedback hast
Cheers, Jonas Co-Founder sliplane.io