Legioniście, czas nauczyć się pisać instrukcje budowy obozu — czyli Dockerfile! Dockerfile to plik tekstowy zawierający sekwencję komend, które Docker wykonuje aby zbudować obraz. Każda komenda tworzy nową warstwę (stratum) w obrazie.
Dockerfile składa się z instrukcji, z których każda pełni określoną rolę:
Instrukcja
FROM wybiera bazowy obraz, na którym budujemy. Dla aplikacji NestJS używamy obrazu Node.js:1# Używamy oficjalnego obrazu Node.js w wersji Alpine (lekki!)
2FROM node:20-alpineAlpine Linux to minimalistyczna dystrybucja — waży ~5MB zamiast ~900MB pełnego Ubuntu. To jak lekki obóz polowy zamiast pełnej fortecy — szybki do postawienia i przeniesienia.
Instrukcja
WORKDIR ustawia katalog roboczy wewnątrz kontenera:1# Ustawiamy katalog roboczy
2WORKDIR /appCOPY kopiuje pliki z naszego komputera do kontenera:1# Kopiujemy pliki package.json i yarn.lock
2COPY package.json yarn.lock ./
3
4# Kopiujemy resztę kodu źródłowego
5COPY . .RUN wykonuje komendy podczas budowy obrazu:1# Instalacja zależności
2RUN yarn install --frozen-lockfile
3
4# Budowanie aplikacji
5RUN yarn buildCMD definiuje komendę uruchamianą przy starcie kontenera:1# Uruchomienie aplikacji
2CMD ["node", "dist/main"]1FROM node:20-alpine
2
3WORKDIR /app
4
5COPY package.json yarn.lock ./
6RUN yarn install --frozen-lockfile
7
8COPY . .
9RUN yarn build
10
11EXPOSE 4000
12
13CMD ["node", "dist/main"]Prosty Dockerfile ma problem — końcowy obraz zawiera narzędzia developerskie, pliki źródłowe TypeScript, devDependencies. To jak zostawienie rusztowań po zakończeniu budowy fortecy.
Multi-stage build pozwala używać wielu etapów budowy, gdzie finalny obraz zawiera tylko to, co potrzebne do uruchomienia:
1# =========================
2# ETAP 1: Builder (Inżynierowie legionowi)
3# =========================
4FROM node:20-alpine AS builder
5
6WORKDIR /app
7
8# Kopiujemy pliki zależności
9COPY package.json yarn.lock ./
10RUN yarn install --frozen-lockfile
11
12# Kopiujemy kod źródłowy i budujemy
13COPY . .
14RUN yarn build
15
16# Instalujemy tylko produkcyjne zależności
17RUN yarn install --frozen-lockfile --production
18
19# =========================
20# ETAP 2: Production (Gotowy obóz)
21# =========================
22FROM node:20-alpine AS production
23
24WORKDIR /app
25
26# Kopiujemy TYLKO zbudowany kod i produkcyjne zależności
27COPY /app/dist ./dist
28COPY /app/node_modules ./node_modules
29COPY /app/package.json ./package.json
30
31# Tworzymy użytkownika bez uprawnień root
32RUN addgroup -g 1001 -S nodejs && \
33 adduser -S nestjs -u 1001
34
35USER nestjs
36
37EXPOSE 4000
38
39CMD ["node", "dist/main"]1const porownanie = {
2 singleStage: {
3 rozmiar: '~800 MB',
4 zawiera: ['node_modules (dev + prod)', 'src/', 'test/', '.git'],
5 bezpieczenstwo: 'root user'
6 },
7 multiStage: {
8 rozmiar: '~200 MB',
9 zawiera: ['dist/', 'node_modules (prod only)', 'package.json'],
10 bezpieczenstwo: 'non-root user (nestjs)'
11 }
12};Tak jak
.gitignore zapobiega dodawaniu plików do repozytorium, .dockerignore zapobiega kopiowaniu niepotrzebnych plików do obrazu:1node_modules
2dist
3.git
4.gitignore
5*.md
6.env
7.env.*
8test
9coverage
10.vscode
11.ideaDzięki
.dockerignore komenda COPY . . nie skopiuje tych plików, co przyspiesza budowę i zmniejsza rozmiar obrazu.1# Buduj obraz
2docker build -t roman-imperium-api:1.0 .
3
4# Uruchom kontener
5docker run -d -p 4000:4000 --name imperium-api roman-imperium-api:1.0
6
7# Sprawdź logi
8docker logs imperium-api
9
10# Sprawdź działanie
11curl http://localhost:4000/healthPamiętaj, legioniście — multi-stage build to najlepsza praktyka dla produkcji. Budujemy ciężko, ale wdrażamy lekko!