Kurs NestJS · Moduł 12: Konteneryzacja i CI/CD

Dockerfile dla NestJS - Budowa Obozu

3 min czytania
W tej lekcji5

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.

Podstawowe instrukcje Dockerfile

Dockerfile składa się z instrukcji, z których każda pełni określoną rolę:

FROM - Fundament obozu

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-alpine

Alpine 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.

WORKDIR - Wyznaczenie placu budowy

Instrukcja WORKDIR ustawia katalog roboczy wewnątrz kontenera:

1# Ustawiamy katalog roboczy
2WORKDIR /app

COPY - Transport materiałów

COPY 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 - Wykonanie prac budowlanych

RUN wykonuje komendy podczas budowy obrazu:

1# Instalacja zależności
2RUN yarn install --frozen-lockfile
3
4# Budowanie aplikacji
5RUN yarn build

CMD - Rozkaz startowy

CMD definiuje komendę uruchamianą przy starcie kontenera:

1# Uruchomienie aplikacji
2CMD ["node", "dist/main"]

Prosty Dockerfile dla NestJS

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"]

Multi-stage Build - Zaawansowana strategia budowy

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 --from=builder /app/dist ./dist
28COPY --from=builder /app/node_modules ./node_modules
29COPY --from=builder /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"]

Porównanie rozmiarów

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};

Plik .dockerignore

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.idea

Dzięki .dockerignore komenda COPY . . nie skopiuje tych plików, co przyspiesza budowę i zmniejsza rozmiar obrazu.

Budowanie i uruchamianie

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/health

Pamiętaj, legioniście - multi-stage build to najlepsza praktyka dla produkcji. Budujemy ciężko, ale wdrażamy lekko!

Kod do tej lekcji: src/dockerfile-example.ts
1// Dockerfile - Instrukcje budowy obozu legionowego
2console.log("=== DOCKERFILE DLA NestJS ===\n");
3
4// Instrukcje Dockerfile
5interface DockerInstruction {
6  command: string;
7  purpose: string;
8  example: string;
9}
10
11const instructions: DockerInstruction[] = [
12  {
13    command: 'FROM',
14    purpose: 'Bazowy obraz (fundament obozu)',
15    example: 'FROM node:20-alpine',
16  },
17  {
18    command: 'WORKDIR',
19    purpose: 'Katalog roboczy (plac budowy)',
20    example: 'WORKDIR /app',
21  },
22  {
23    command: 'COPY',
24    purpose: 'Kopiowanie plikow (transport materialow)',
25    example: 'COPY package.json yarn.lock ./',
26  },
27  {
28    command: 'RUN',
29    purpose: 'Wykonanie komendy (prace budowlane)',
30    example: 'RUN yarn install --frozen-lockfile',
31  },
32  {
33    command: 'EXPOSE',
34    purpose: 'Deklaracja portu (brama obozu)',
35    example: 'EXPOSE 4000',
36  },
37  {
38    command: 'CMD',
39    purpose: 'Komenda startowa (rozkaz na start)',
40    example: 'CMD ["node", "dist/main"]',
41  },
42];
43
44console.log("Instrukcje Dockerfile:\n");
45instructions.forEach(i => {
46  console.log(`${i.command}: ${i.purpose}`);
47  console.log(`  Przyklad: ${i.example}\n`);
48});
49
50// Multi-stage build comparison
51console.log("=== MULTI-STAGE BUILD ===\n");
52console.log("Single-stage: ~800 MB (dev + prod dependencies)");
53console.log("Multi-stage:  ~200 MB (only production)\n");
54
55console.log("Etap 1 (builder): instalacja, build, kompilacja");
56console.log("Etap 2 (production): COPY --from=builder tylko dist/");
57console.log("\nKorzysc: mniejszy obraz, brak dev-dependencies, non-root user");
58

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co definiuje instrukcja FROM w Dockerfile?

  2. 2. Jaka jest główna korzyść z multi-stage build w Docker?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Zdefiniuj dwa etapy Dockerfile: builder (budowanie) i production (uruchomienie)

  • Klikanie w kolejności

    Ułóż instrukcje Dockerfile w prawidłowej kolejności:

  • Edytor kodu

    Zaznacz, które pliki/foldery powinny być ignorowane przez Docker (node_modules, dist, .git, .env, src, test, package.json, coverage)

  • Układanie w poziomie

    Ułóż elementy komendy budowania obrazu Docker w prawidłowej kolejności:

Przydatne artykuły