Kurs NestJS · Moduł 9: Deployment i infrastruktura

CI/CD - automatyczna kuźnia legionów

5 min czytania
W tej lekcji6

Wdrożenie ręczne wygląda zawsze tak samo: ktoś loguje się na serwer, pobiera kod, uruchamia budowanie, restartuje aplikację. Działa - dopóki ten ktoś pamięta wszystkie kroki, robi je w tej samej kolejności i nie wdraża w piątek o siedemnastej.

Kuźnia legionowa nie polegała na pamięci kowala. Miała ustalony porządek: ta sama stal, ta sama temperatura, ten sam hartunek, ten sam znak na każdym ostrzu. Dzięki temu miecz wykuty w Galii pasował do pochwy zrobionej w Rzymie. CI/CD to ten porządek zapisany w pliku.

Co znaczy ten skrót

CI/CD oznacza Continuous Integration / Continuous Deployment - ciągłą integrację i ciągłe wdrażanie. Nie „Container", nie „Component", nie „Code": obie litery odnoszą się do procesu, nie do tego, co się przetwarza.

Każda połowa rozwiązuje inny problem. Continuous Integration to automatyczne budowanie i testowanie kodu po każdej zmianie - żeby błąd wyszedł na jaw w ciągu minut, a nie po tygodniu, gdy nikt już nie pamięta, czyja zmiana go wprowadziła. Continuous Deployment to automatyczne wdrożenie zmiany, która przeszła weryfikację - żeby droga od gotowego kodu do produkcji nie zależała od tego, czy ktoś ma czas.

GitHub Actions

Narzędzi jest wiele; zostaniemy przy jednym. GitHub Actions automatyzują build, test i deployment po każdym push i pull requeście (PR) - to cała ich rola.

Trzy nieporozumienia warto odsunąć od razu. Nie służą wyłącznie do zarządzania zgłoszeniami (issues) - to osobna funkcja GitHuba. Nie są dostępne tylko dla repozytoriów prywatnych; publiczne korzystają z nich za darmo i bez limitu minut. I nie zastępują Dockera - przeciwnie, to właśnie w kroku workflow wywołujesz docker build i docker push, więc jedno korzysta z drugiego.

Konfiguracja to plik YAML w katalogu .github/workflows/:

1# .github/workflows/deploy.yml
2name: Deploy
3
4on:
5  push:
6    branches:
7      - main

Trigger, czyli kiedy to ma się uruchomić

Powyższy fragment to trigger - wskazanie zdarzenia uruchamiającego workflow. Zapisuje się go w czterech zagnieżdżeniach, zawsze w tej kolejności: on: otwiera sekcję zdarzeń, push: wskazuje konkretne zdarzenie, branches: zawęża je do wybranych gałęzi, a - main jest pozycją listy tych gałęzi.

W YAML-u znaczenie ma wcięcie - każdy kolejny poziom przesuwa się o dwie spacje w prawo i to ono buduje strukturę. Myślnik przed main oznacza element listy, więc kolejne gałęzie dopisujesz w następnych liniach.

Zdarzeń jest więcej niż jedno. pull_request: uruchomi sprawdzenie przy każdym PR - i to jest zwykle ważniejsze niż push, bo pozwala wychwycić błąd zanim zmiana trafi do głównej gałęzi.

Cztery etapy pipeline'u

Właściwa praca dzieje się w zadaniu (job), a jego kroki mają ustaloną kolejność:

1jobs:
2  build:
3    runs-on: ubuntu-latest
4    steps:
5      - uses: actions/checkout@v4
6
7      - uses: actions/setup-node@v4
8        with:
9          node-version: 20
10
11      - run: npm ci
12      - run: npm test
13      - run: npm run build
14      - run: docker push registry.imperium.rome/legion-api:latest

Kolejność etapów nie jest kwestią gustu: instalacja zależności (npm ci) → linter i testy (npm test) → budowanie aplikacji (npm run build) → wdrożenie na produkcję (docker push).

Rządzi nią jedna zasada: im tańszy etap, tym wcześniej. Linter kończy się w kilka sekund, testy w kilka minut, budowanie obrazu w kilkanaście. Gdyby budowanie szło przed testami, każdy literówkowy błąd kosztowałby pełny czas budowania - a i tak zakończyłby się porażką.

Dwa kroki na początku są przygotowaniem, nie etapem pipeline'u. actions/checkout pobiera kod repozytorium do maszyny, na której workflow działa - bez tego katalog jest pusty. actions/setup-node instaluje wskazaną wersję Node.js i tym samym rozwiązuje spór „u mnie działa": wersja jest zapisana w pliku, więc każdy przebieg dostaje tę samą.

Zwróć uwagę na npm ci zamiast npm install. Pierwsze instaluje dokładnie to, co zapisano w package-lock.json, i przerywa pracę przy rozjeździe z package.json. Drugie może po cichu podnieść wersję zależności - a wtedy CI testuje coś innego niż to, co masz u siebie.

Gdy etap zawiedzie

Kroki wykonują się po kolei i pierwszy niepowodzenie zatrzymuje resztę. To zachowanie domyślne i pożądane: skoro testy nie przechodzą, budowanie obrazu i wysyłanie go do rejestru byłoby tylko marnowaniem czasu, a w najgorszym razie wdrożeniem zepsutej wersji.

Stąd praktyczny podział. Sprawdzenia - instalacja, linter, testy - uruchamiaj przy każdym push i każdym PR, na każdej gałęzi. Wdrożenie ogranicz do gałęzi głównej, przez branches: - main z poprzedniej sekcji. Dzięki temu praca nad funkcją jest sprawdzana od pierwszego commita, ale nic nie trafia na produkcję, dopóki zmiana nie zostanie scalona.

Podsumowanie

Kuźnia hartuje każde ostrze tak samo:

  • CI/CD oznacza Continuous Integration / Continuous Deployment - nie Container, nie Component, nie Code,
  • CI to automatyczne budowanie i testowanie po każdej zmianie, CD - automatyczne wdrożenie tego, co przeszło weryfikację,
  • GitHub Actions automatyzują build, test i deployment po każdym push i PR - nie służą tylko do issues, nie są zarezerwowane dla repozytoriów prywatnych i nie zastępują Dockera,
  • konfiguracja leży w .github/workflows/ i jest plikiem YAML, w którym strukturę buduje wcięcie,
  • składnia triggera: on: → push: → branches: → - main; pull_request: sprawdza zmianę przed scaleniem,
  • kolejność etapów: npm ci → npm test → npm run build → docker push, od najtańszego do najdroższego,
  • actions/checkout pobiera kod, actions/setup-node ustala wersję Node.js jednakową dla każdego przebiegu,
  • npm ci instaluje dokładnie to, co w package-lock.json; npm install może po cichu zmienić wersje,
  • pierwszy nieudany krok zatrzymuje pozostałe - dlatego sprawdzenia biegną wszędzie, a wdrożenie tylko z gałęzi głównej.

Do CI/CD wrócimy w module o infrastrukturze, przy budowaniu na wielu wersjach naraz, artefaktach i cache'owaniu zależności. A na razie zapamiętaj: pipeline nie jest po to, żeby wdrażać szybciej. Jest po to, żeby wdrażać tak samo - a szybkość wychodzi przy okazji.

Kod do tej lekcji: .github/workflows/ci.yml
1# CI/CD Pipeline - Automatyczna Stocznia Imperium
2
3name: Roman Empire CI/CD
4
5on:
6  push:
7    branches: [main, develop]
8  pull_request:
9    branches: [main]
10
11# Zmienne srodowiskowe dla calego workflow
12env:
13  NODE_VERSION: '18'
14  REGISTRY: ghcr.io
15
16jobs:
17  # === JOB 1: Testy ===
18  test:
19    name: Run Tests
20    runs-on: ubuntu-latest
21
22    steps:
23      - name: Checkout code
24        uses: actions/checkout@v4
25
26      - name: Setup Node.js
27        uses: actions/setup-node@v4
28        with:
29          node-version: '18'
30          cache: 'npm'
31
32      - name: Install dependencies
33        run: npm ci
34
35      - name: Run linter
36        run: npm run lint
37
38      - name: Run unit tests with coverage
39        run: npm run test:cov
40
41      - name: Run E2E tests
42        run: npm run test:e2e
43
44  # === JOB 2: Build ===
45  build:
46    name: Build Application
47    needs: test
48    runs-on: ubuntu-latest
49
50    steps:
51      - uses: actions/checkout@v4
52
53      - name: Build Docker image
54        run: docker build -t roman-api:latest .
55
56      - name: Run container health check
57        run: |
58          docker run -d --name test-api -p 3000:3000 roman-api:latest
59          sleep 10
60          curl -f http://localhost:3000/health || exit 1
61          docker stop test-api
62
63  # === JOB 3: Deploy ===
64  deploy:
65    name: Deploy to Production
66    needs: build
67    runs-on: ubuntu-latest
68    if: github.ref == 'refs/heads/main'
69
70    steps:
71      - uses: actions/checkout@v4
72
73      - name: Deploy
74        run: echo "Deploying Roman Empire API..."
75
76      - name: Verify health
77        run: echo "Checking health endpoint..."
78
79# CI = Continuous Integration (testy przy kazdym pushu)
80# CD = Continuous Deployment (automatyczny deploy po merge)
81

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. CI/CD oznacza:

  2. 2. GitHub Actions w kontekście CI/CD:

Zadania praktyczne w grze

  • Edytor kodu

    Stwórz .github/workflows/deploy.yml z krokami: install, lint, test, build, deploy

  • Układanie w pionie

    Uporządkuj etapy pipeline CI/CD od pierwszego do ostatniego:

  • Układanie w pionie

    Ułóż składnię triggera GitHub Actions workflow:

Przydatne artykuły