Laboratorium 01 / Docker

Uruchom całą misję w kontenerach

Otrzymujesz działający kod Angular + Spring Boot. Twoim zadaniem jest przygotowanie obrazów, uruchomienie PostgreSQL, połączenie usług i zapisanie całej konfiguracji w Docker Compose.

Cel ćwiczenia

Wersja materiałów: ta instrukcja dotyczy aplikacji Spring Boot + PostgreSQL z zajęć nr 1. W repozytorium materiałów kursu zachowuje ją tag Z1: git switch --detach Z1. Bieżący starter do zajęć nr 2 używa .NET + MySQL; nie mieszaj jego konfiguracji z poleceniami poniżej.

Najpierw uruchomisz każdy komponent osobno poleceniami Docker.

Zadanie 0 — instalacja środowiska w domu

To zadanie wykonaj przed laboratorium na własnym komputerze. Instrukcja zakłada pracę na Windowsie z Rancher Desktop. Polecenia Docker możesz później wykonywać z PowerShella, Windows Terminal albo terminala WSL.

Wymagania komputera

  • Windows 11 z aktualnymi poprawkami,
  • włączona wirtualizacja sprzętowa,
  • stałe połączenie z Internetem podczas instalacji i pobierania obrazów,
  • zalecane co najmniej 8 GB RAM i 4 rdzenie procesora.
Windows 10: aktualna wersja Rancher Desktop nie wymienia Windows 10 jako obsługiwanego systemu. Jeśli nie możesz zaktualizować komputera do Windows 11, skonsultuj z prowadzącym użycie stanowiska laboratoryjnego zamiast instalowania przypadkowej starszej wersji.

1. Zainstaluj WSL 2

Otwórz PowerShell jako administrator i wykonaj:

wsl --install

Uruchom ponownie komputer. Następnie otwórz zwykły PowerShell i sprawdź instalację:

wsl --update
wsl --status
wsl --list --verbose

Dystrybucja widoczna na liście powinna używać wersji 2. Nie musisz wykonywać ćwiczenia w terminalu Linuksa — WSL 2 jest potrzebny Rancher Desktop jako warstwa uruchomieniowa dla kontenerów.

Instrukcja instalacji WSL w Microsoft Learn

2. Zainstaluj Rancher Desktop

  1. Pobierz aktualny instalator MSI, korzystając z oficjalnej instrukcji Rancher Desktop.
  2. Uruchom instalator. Na własnym komputerze możesz zezwolić na instalację usługi uprzywilejowanej, aby publikowanie portów nie było ograniczone wyłącznie do 127.0.0.1.
  3. Uruchom Rancher Desktop i poczekaj na zakończenie pierwszej konfiguracji.
  4. Wybierz Preferences → Container Engine → dockerd (moby). Ten silnik udostępnia Docker API i polecenie docker.
  5. W Preferences → Kubernetes wyłącz Enable Kubernetes. Kubernetes nie jest potrzebny w tym laboratorium, a jego wyłączenie zmniejsza zużycie zasobów.
Nie instaluj równolegle Docker Desktop: do tego laboratorium używamy Rancher Desktop. Dwie działające aplikacje zarządzające Dockerem mogą przełączać kontekst i utrudniać ustalenie, w którym silniku znajdują się kontenery oraz obrazy.

3. Zainstaluj pozostałe narzędzia

  • Wymagane: Git for Windows do pobrania repozytorium i wysłania rozwiązania.
  • Potrzebne w zadaniu 2: DataGrip albo inny klient PostgreSQL zaakceptowany przez prowadzącego.
  • Opcjonalne: IntelliJ IDEA lub Visual Studio Code do wygodnej edycji plików.

Nie musisz instalować lokalnie PostgreSQL, JDK, Mavena, Node.js ani nginx. W docelowym rozwiązaniu te narzędzia i procesy będą dostarczane przez obrazy kontenerów.

4. Sprawdź instalację

Upewnij się, że Rancher Desktop jest uruchomiony, a następnie otwórz nowy terminal i wykonaj:

docker version
docker compose version
docker info
docker run --rm hello-world
git --version

Polecenie z obrazem hello-world pobierze niewielki obraz testowy, uruchomi kontener i usunie go po zakończeniu.

Pokaż ściągę z poleceń Docker
  • docker version — pokazuje wersję klienta oraz serwera. Obie sekcje potwierdzają, że terminal łączy się z działającym silnikiem.
  • docker compose version — sprawdza dostępność Docker Compose.
  • docker info — wyświetla informacje o aktywnym silniku, liczbie kontenerów i obrazów.
  • docker run --rm <obraz> — pobiera obraz, jeśli nie ma go lokalnie, tworzy kontener, uruchamia go i usuwa po zakończeniu dzięki opcji --rm.
Sprawdź w Rancher Desktop: aplikacja ma stan gotowości, wybrany jest silnik dockerd (moby), a w zakładce Images po wykonaniu testu widoczny jest obraz hello-world. Kontenera może już nie być na liście, ponieważ opcja --rm usuwa go po zakończeniu.
Gdy polecenie docker nie działa: zamknij i ponownie otwórz terminal, sprawdź, czy Rancher Desktop jest uruchomiony oraz czy wybrano dockerd (moby). Jeśli Rancher nie startuje, ponownie sprawdź wsl --status i wirtualizację w Menedżerze zadań.

Przygotowanie stanowiska

  1. Otwórz link do zadania przekazany przez prowadzącego w GitHub Classroom.
  2. Zaakceptuj zadanie. GitHub Classroom automatycznie utworzy Twoje indywidualne repozytorium z kodem startowym.
  3. Na stronie utworzonego repozytorium wybierz Code → HTTPS i skopiuj adres.
  4. Otwórz PowerShell, Windows Terminal albo terminal WSL i sklonuj repozytorium. Wybierz środowisko, w którym działają polecenia git i docker.
docker version
docker compose version
git --version

git clone <ADRES_TWOJEGO_REPOZYTORIUM>
cd <NAZWA_TWOJEGO_REPOZYTORIUM>

Od tego momentu wszystkie polecenia wykonuj wewnątrz sklonowanego repozytorium. To właśnie w nim zapisujesz rozwiązanie i wysyłasz zmiany do GitHub Classroom.

Sprawdź: w katalogu głównym powinny znajdować się katalogi backend, frontend i docs oraz plik README.md.

Docelowa architektura

Angular
przeglądarka → port hosta
→ HTTP
Spring Boot API
port 8080 kontenera
→ JDBC
PostgreSQL
port 5432 kontenera

Przeglądarka musi mieć dostęp do frontendu i API przez porty hosta. Backend i baza powinny komunikować się we wspólnej sieci Dockera po nazwie kontenera.

Projekt startowy

Aplikacja zarządza listą misji kosmicznych. Backend udostępnia CRUD pod /api/missions, a frontend wyświetla rekordy i pozwala dodawać oraz usuwać misje.

<Twoje repozytorium z GitHub Classroom>/
├── backend/       # Spring Boot + Maven
├── frontend/      # Angular
├── docs/          # mockupy i kontrakt API
├── .env.example
└── README.md

Kontrola przed konteneryzacją

Możesz uruchomić aplikacje lokalnie, aby odróżnić problemy aplikacji od problemów konfiguracji Docker. Nie jest to jednak konfiguracja końcowa.

Zadanie 1 — PostgreSQL

Uruchom oficjalny obraz PostgreSQL jako kontener. Samodzielnie dobierz:

  • nazwę kontenera, bazę, użytkownika i hasło,
  • mapowanie portu umożliwiające dostęp z hosta,
  • named volume dla katalogu danych,
  • wspólną sieć dla bazy i późniejszego API.

Przykładowe uruchomienie

Poniższe polecenia działają w PowerShellu, Windows Terminal oraz terminalu WSL:

docker network create mission-net
docker volume create postgres-data

docker run -d --name mission-db --network mission-net -p 5432:5432 -e POSTGRES_DB=mission_control -e POSTGRES_USER=mission -e POSTGRES_PASSWORD=mission-pass -v postgres-data:/var/lib/postgresql/data postgres:17
  • mission-db — nazwa kontenera,
  • 5432:5432 — port hosta i port PostgreSQL w kontenerze,
  • mission_control — nazwa tworzonej bazy,
  • mission / mission-pass — użytkownik i hasło używane wyłącznie w ćwiczeniu,
  • postgres-data — named volume przechowujący dane.
Podpowiedź: obraz PostgreSQL przy pierwszym starcie korzysta ze zmiennych POSTGRES_DB, POSTGRES_USER i POSTGRES_PASSWORD. Sprawdź dokumentację obrazu przed uruchomieniem.

Po starcie sprawdź kontener i jego logi:

docker ps
docker logs mission-db

Zapisz swoje polecenie — przyda się podczas tworzenia Compose.

Pokaż ściągę z poleceń Docker
  • docker network create <nazwa> — tworzy sieć, do której można dołączać kontenery.
  • docker volume create <nazwa> — tworzy nazwany wolumen na trwałe dane.
  • docker run [opcje] <obraz> — tworzy i uruchamia kontener z obrazu. Przydatne opcje: -d uruchamia w tle, --name nadaje nazwę, --network dołącza do sieci, -p <host>:<kontener> publikuje port, -e <klucz>=<wartość> ustawia zmienną, a -v <wolumen>:<ścieżka> montuje dane.
  • docker ps — pokazuje działające kontenery; opcja -a uwzględnia zatrzymane.
  • docker logs <kontener> — wyświetla logi; opcja -f śledzi nowe wpisy.
Sprawdź w Rancher Desktop: w zakładce Containers kontener mission-db ma stan uruchomiony, obraz postgres:17 i opublikowany port 5432. Otwórz szczegóły kontenera i odszukaj zmienne środowiskowe oraz zamontowany wolumen. W zakładkach Images i Volumes powinny być widoczne odpowiednio obraz PostgreSQL oraz postgres-data.

Zadanie 2 — połączenie przez DataGrip

Utwórz nowe źródło danych PostgreSQL. Użyj hosta localhost, opublikowanego portu oraz danych podanych przy starcie kontenera.

  • Test Connection kończy się powodzeniem.
  • Widać właściwą bazę i schemat.
  • Po uruchomieniu backendu pojawia się tabela missions.
  • Potrafisz wykonać zapytanie SELECT.
Sprawdź w Rancher Desktop: w szczegółach mission-db potwierdź publikację portu używanego przez DataGrip. W zakładce Logs sprawdź, czy PostgreSQL zakończył inicjalizację i jest gotowy do przyjmowania połączeń.

Zadanie 3 — obraz backendu

W katalogu backend utwórz wieloetapowy Dockerfile. Pierwszy etap ma zbudować plik JAR za pomocą Mavena, a drugi uruchomić go na obrazie JRE.

# Szkielet — uzupełnij obrazy, ścieżki i polecenia
FROM ... AS build
WORKDIR ...
COPY ...
RUN ...

FROM ...
WORKDIR ...
COPY --from=build ...
EXPOSE ...
ENTRYPOINT [...]

Dodaj .dockerignore. Obraz nie powinien zawierać repozytorium Git, ustawień IDE ani lokalnego katalogu target.

Konfiguracja połączenia

Spring Boot odczytuje ustawienia ze zmiennych DB_URL, DB_USERNAME i DB_PASSWORD. W adresie JDBC użyj nazwy kontenera bazy, a nie localhost.

Pokaż ściągę z poleceń Docker
  • docker build -t <nazwa>:<tag> <kontekst> — buduje obraz z pliku Dockerfile. Kontekst to katalog plików dostępnych dla instrukcji COPY; często wskazuje się go kropką.
  • docker image ls — wyświetla lokalne obrazy i pozwala sprawdzić nazwę, tag oraz rozmiar wyniku budowania.
  • docker run [opcje] <nazwa-obrazu>:<tag> — uruchamia kontener z przygotowanego obrazu; konfigurację aplikacji można przekazać opcjami -e, --network i -p.
Sprawdź w Rancher Desktop: po zbudowaniu obrazu odszukaj go w zakładce Images i sprawdź jego tag oraz rozmiar. Po uruchomieniu backendu otwórz szczegóły kontenera: potwierdź obraz, zmienne DB_*, opublikowany port i brak błędów połączenia z bazą w Logs.

Zadanie 4 — obraz frontendu

Przygotuj wieloetapowy obraz: Node buduje aplikację Angular, a lekki serwer HTTP udostępnia wynik. Skonfiguruj przekierowanie ścieżki /api do backendu lub przygotuj właściwy adres API na etapie budowania.

Przygotowanie konfiguracji nginx

W projekcie startowym znajduje się szkielet docs/mockups/nginx.conf.mockup. Na jego podstawie utwórz plik frontend/nginx.conf, uzupełnij znaczniki <...> i dopiero wtedy skopiuj ten plik w Dockerfile do katalogu konfiguracji nginx.

Nie pomyl plików: frontend/proxy.conf.json jest używany przez ng serve podczas pracy lokalnej. Kontener produkcyjny uruchamia nginx, dlatego potrzebuje osobnego pliku nginx.conf. Nazwa hosta w proxy_pass powinna wskazywać kontener lub usługę backendu, a nie localhost.
Pokaż przykładowy plik nginx.conf

Przykład zakłada, że backend jest dostępny w sieci Docker pod nazwą api i słucha na porcie 8080. Jeśli używasz innej nazwy kontenera lub usługi, zmień ją w proxy_pass.

server {
  listen 80;
  server_name _;

  root /usr/share/nginx/html;
  index index.html;

  location / {
    try_files $uri $uri/ /index.html;
  }

  location /api/ {
    proxy_pass http://api:8080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
# Szkielet
FROM node:... AS build
WORKDIR ...
COPY package*.json ./
RUN ...
COPY . .
RUN ...

FROM nginx:...
COPY --from=build ...
COPY nginx.conf ...
EXPOSE ...

Preferowane rozwiązanie: przeglądarka wysyła żądania do /api, a nginx przekazuje je do kontenera API. Dzięki temu nie trzeba kodować adresu hosta w aplikacji.

Pokaż ściągę z poleceń Docker
  • docker build -t <nazwa>:<tag> <kontekst> — buduje obraz i oznacza go nazwą oraz tagiem. Opcja -f <plik> pozwala wskazać Dockerfile o innej nazwie lub w innym miejscu.
  • docker run [opcje] <obraz> — uruchamia kontener. Opcja -p <port-hosta>:<port-kontenera> udostępnia serwer HTTP przeglądarce.
  • docker logs <kontener> — pomaga sprawdzić, czy nginx wystartował i czy obsługuje żądania.
Sprawdź w Rancher Desktop: w zakładce Images znajdź zbudowany obraz frontendu. Po uruchomieniu kontenera potwierdź w Containers jego stan i mapowanie portu, a w Logs sprawdź, czy serwer nginx uruchomił się bez błędów.

Zadanie 5 — komponent po komponencie

  1. Utwórz sieć.
  2. Uruchom PostgreSQL z wolumenem.
  3. Zbuduj obraz backendu.
  4. Uruchom backend w tej samej sieci i przekaż zmienne.
  5. Sprawdź endpoint /actuator/health oraz CRUD.
  6. Zbuduj obraz frontendu.
  7. Uruchom frontend w tej samej sieci.
  8. Dodaj misję w interfejsie i zobacz rekord w DataGrip.
curl http://localhost:8080/actuator/health
curl http://localhost:8080/api/missions
docker ps
docker logs <nazwa-api>
Nie przechodź dalej, dopóki zapis dodany w interfejsie nie jest widoczny w bazie.
Pokaż ściągę z poleceń Docker
  • docker network create <nazwa> — tworzy wspólną sieć; kontenery dołączone przez --network <nazwa> mogą odwoływać się do siebie po nazwach.
  • docker build -t <obraz> <kontekst> — buduje obraz aplikacji.
  • docker run -d --name <kontener> [opcje] <obraz> — uruchamia nazwany kontener w tle. Powtarzaj polecenie osobno dla każdego komponentu, dobierając jego sieć, porty i zmienne.
  • docker ps, docker logs <kontener> i docker exec -it <kontener> <polecenie> — służą kolejno do sprawdzenia stanu, logów i wykonania polecenia wewnątrz działającego kontenera.
Sprawdź w Rancher Desktop: w Containers powinny być widoczne trzy uruchomione, samodzielne kontenery: baza, API i frontend. Porównaj ich obrazy oraz opublikowane porty. Otwórz Logs API i frontendu, a następnie potwierdź cały przepływ, dodając rekord w interfejsie i odczytując go w DataGrip.

Zadanie 6 — usuń środowisko

Zatrzymaj i usuń utworzone kontenery oraz własną sieć. Usuń obrazy aplikacji, aby Compose musiał je ponownie zbudować. Na razie zachowaj wolumen i sprawdź, czy potrafisz go wskazać.

Następnie usuń także wolumen. To świadomie destrukcyjny krok dotyczący wyłącznie danych ćwiczenia — potwierdź nazwę poleceniem docker volume ls.

Pokaż ściągę z poleceń Docker
  • docker stop <kontener> — zatrzymuje działający kontener.
  • docker rm <kontener> — usuwa zatrzymany kontener; opcja -f wymusza zatrzymanie i usunięcie.
  • docker network ls / docker network rm <sieć> — wyświetla lub usuwa sieć, której nie używa już żaden kontener.
  • docker image ls / docker image rm <obraz> — wyświetla lub usuwa lokalny obraz.
  • docker volume ls / docker volume rm <wolumen> — wyświetla lub usuwa wolumen. Usunięcie wolumenu oznacza utratę zapisanych w nim danych.
Sprawdź w Rancher Desktop: po usunięciu kontenerów nie powinno ich być w Containers, a własne obrazy aplikacji powinny zniknąć z Images. Najpierw potwierdź, że postgres-data nadal istnieje w Volumes; usuń wyłącznie wolumen tego ćwiczenia i sprawdź, czy zniknął z listy.

Zadanie 7 — Docker Compose

W katalogu głównym projektu utwórz compose.yaml opisujący trzy usługi. Nie kopiuj poleceń docker run jako tekstu — przełóż ich parametry na deklarację.

services:
  db:
    # obraz, środowisko, wolumen, healthcheck
  api:
    # build, środowisko, zależność od zdrowej bazy
  frontend:
    # build, port, zależność od API

volumes:
  # nazwany wolumen danych

networks:
  # opcjonalna jawna sieć

Wymagania

  • hasła są pobierane z pliku .env, którego nie commitujesz,
  • .env.example opisuje wymagane klucze bez prawdziwych sekretów,
  • baza posiada healthcheck,
  • API startuje po uzyskaniu gotowości bazy,
  • tylko porty potrzebne hostowi są publikowane,
  • dane PostgreSQL znajdują się w named volume.
docker compose config
docker compose up --build -d
docker compose ps
docker compose logs -f
docker compose down
Pokaż ściągę z poleceń Docker Compose
  • docker compose config — sprawdza składnię i pokazuje wynikową konfigurację po uwzględnieniu zmiennych.
  • docker compose build — buduje obrazy usług posiadających sekcję build.
  • docker compose up [opcje] — tworzy i uruchamia usługi; --build wcześniej buduje obrazy, a -d pozostawia środowisko w tle.
  • docker compose ps — pokazuje stan usług należących do projektu.
  • docker compose logs [-f] [usługa] — wyświetla logi wszystkich albo wskazanej usługi; -f śledzi nowe wpisy.
  • docker compose down — usuwa kontenery i sieć projektu, ale domyślnie zachowuje named volumes. Opcja -v usuwa także wolumeny i ich dane.
Sprawdź w Rancher Desktop: trzy kontenery powinny być zgrupowane jako jedno wdrożenie Docker Compose. Potwierdź ich stan, obrazy i porty, przejrzyj logi każdej usługi oraz odszukaj wolumen utworzony przez projekt. Po docker compose down grupa kontenerów powinna zniknąć, a wolumen z danymi pozostać.

Diagnostyka

API zgłasza connection refused

Sprawdź nazwę hosta w JDBC, sieć obu kontenerów, gotowość PostgreSQL i logi. W kontenerze localhost oznacza ten sam kontener.

Frontend zwraca 502

Sprawdź nazwę usługi API w konfiguracji nginx, port procesu backendu oraz logi obu usług.

DataGrip nie łączy się z bazą

Sprawdź publikację portu na hosta. Komunikacja między kontenerami nie wymaga publikacji, ale DataGrip działający w Windows jej potrzebuje.

Zmiana hasła nie działa

Zmienne inicjalizacyjne obrazu bazy działają przy tworzeniu pustego katalogu danych. Istniejący wolumen zachowuje wcześniejszą konfigurację.

Kryteria ukończenia

  • PostgreSQL działa w kontenerze i przechowuje dane w wolumenie.
  • DataGrip łączy się z bazą przez port hosta.
  • Backend i frontend posiadają wieloetapowe Dockerfile.
  • Frontend wywołuje API, a API zapisuje dane w PostgreSQL.
  • Kontenery komunikują się po nazwach usług.
  • Całość uruchamia się poleceniem docker compose up --build.
  • Dane pozostają po docker compose down i ponownym starcie.
  • Sekrety nie znajdują się w kodzie ani historii obrazu.
  • Potrafisz znaleźć błąd za pomocą ps, logs, inspect i exec.

Pytania do samodzielnego sprawdzenia

  1. Dlaczego API nie powinno używać localhost jako hosta bazy?
    Pokaż odpowiedź

    Wewnątrz kontenera localhost wskazuje ten sam kontener, czyli API, a nie PostgreSQL. Kontenery działające we wspólnej sieci powinny komunikować się przez nazwę kontenera lub nazwę usługi zdefiniowaną w Compose.

  2. Czym różni się port kontenera od portu publikowanego na hoście?
    Pokaż odpowiedź

    Port kontenera jest dostępny w jego własnej przestrzeni sieciowej oraz dla innych kontenerów w tej samej sieci. Publikacja w postaci <port-hosta>:<port-kontenera> przekierowuje ruch z hosta do kontenera i umożliwia dostęp np. z przeglądarki lub DataGrip.

  3. Co stanie się z danymi po usunięciu kontenera bazy bez wolumenu?
    Pokaż odpowiedź

    Dane zapisane wyłącznie w zapisywalnej warstwie kontenera zostaną usunięte razem z kontenerem. Named volume przechowuje je poza cyklem życia kontenera, dlatego można podłączyć go do nowej instancji bazy.

  4. Dlaczego warto rozdzielić etap budowania i uruchamiania obrazu?
    Pokaż odpowiedź

    Etap budowania może zawierać kompilator, Maven, Node i kod źródłowy, których aplikacja nie potrzebuje podczas działania. Do obrazu końcowego trafiają tylko artefakty i środowisko uruchomieniowe, dzięki czemu obraz jest mniejszy i zawiera mniej zbędnych narzędzi.

  5. Czy depends_on gwarantuje gotowość bazy bez healthcheck?
    Pokaż odpowiedź

    Nie. Samo uruchomienie kontenera nie oznacza, że PostgreSQL zakończył inicjalizację i przyjmuje połączenia. Należy zdefiniować healthcheck bazy i uzależnić start API od warunku service_healthy; aplikacja powinna również potrafić ponawiać połączenie.

  6. Kiedy DataGrip wymaga publikacji portu, a kiedy publikacja nie jest potrzebna?
    Pokaż odpowiedź

    DataGrip uruchomiony w Windows łączy się z bazą przez hosta, dlatego port PostgreSQL musi być opublikowany. Publikacja nie jest potrzebna do komunikacji API z bazą — kontenery w tej samej sieci korzystają z nazwy usługi i wewnętrznego portu bazy.

  7. Jak sprawdzić wartości konfiguracji wynikowej Compose?
    Pokaż odpowiedź

    Użyj docker compose config. Polecenie sprawdza składnię i pokazuje konfigurację po połączeniu plików oraz podstawieniu zmiennych środowiskowych.

  8. Jak usunąć środowisko bez usuwania danych?
    Pokaż odpowiedź

    Użyj docker compose down bez opcji -v. Kontenery i sieć projektu zostaną usunięte, ale named volumes pozostaną i mogą zostać ponownie podłączone przy następnym uruchomieniu.

Powrót do kursu