„U mnie działa” to zdanie, które Docker ma zlikwidować. Dla frontendowca to nie jest o Kubernetesie, tylko o dwóch rzeczach: powtarzalnym buildzie i środowisku, które nowa osoba w zespole uruchamia jedną komendą.
Wieloetapowy Dockerfile
Pierwszy etap buduje aplikację na obrazie z Node. Drugi bierze tylko gotowy folder dist i serwuje go przez nginx. Obraz końcowy nie zawiera ani Node, ani node_modules — waży kilkadziesiąt megabajtów zamiast kilkuset.
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
Kolejność linii ma znaczenie. Najpierw kopiujemy tylko package*.json i instalujemy zależności — Docker zapamięta tę warstwę i przy zmianie samego kodu nie będzie instalował wszystkiego od nowa.
.dockerignore
Bez niego do obrazu trafi lokalne node_modules, a build potrwa dłużej i może się różnić od tego na CI.
node_modules
dist
.git
.env*
Compose do pracy lokalnej
Do developmentu nie budujemy obrazu przy każdej zmianie. Montujemy kod jako wolumen i uruchamiamy serwer Vite w kontenerze. Drugi wolumen /app/node_modules sprawia, że zależności z kontenera nie mieszają się z tymi na Twoim dysku.
services:
web:
image: node:22-alpine
working_dir: /app
command: sh -c "npm ci && npm run dev -- --host"
ports:
- "5173:5173"
volumes:
- .:/app
- /app/node_modules
Trzy komendy na co dzień
| Komenda | Co robi |
|---|---|
docker compose up | uruchamia środowisko deweloperskie |
docker build -t app . | buduje obraz produkcyjny |
docker run -p 8080:80 app | serwuje build pod localhost:8080 |
Uwaga na SPA Domyślny nginx zwróci 404 dla tras routera po odświeżeniu strony. Dodaj własny plik konfiguracyjny z try_files $uri /index.html;.
To wszystko. Z tymi dwoma plikami build na CI, u Ciebie i u kolegi z nowym laptopem daje ten sam wynik.
