Czym jest Docker i dlaczego warto go znać?
Docker to platforma open-source, która umożliwia pakowanie aplikacji wraz ze wszystkimi jej zależnościami w lekkie, przenośne jednostki zwane kontenerami. W przeciwieństwie do tradycyjnych maszyn wirtualnych, kontenery nie emulują całego systemu operacyjnego – współdzielą jądro systemu hosta, co sprawia, że są znacznie bardziej wydajne i szybsze w uruchamianiu.
Dla programisty Docker rozwiązuje jeden z najbardziej klasycznych problemów w branży: "u mnie działa". Dzięki konteneryzacji aplikacja zachowuje się identycznie na komputerze dewelopera, w środowisku testowym i na serwerze produkcyjnym. To ogromna oszczędność czasu i nerwów.
Podstawowe pojęcia, które musisz znać
Zanim przejdziemy do praktyki, warto zapoznać się z kluczową terminologią:
- Image (obraz) – szablon tylko do odczytu, który zawiera instrukcje tworzenia kontenera. Możemy myśleć o nim jak o przepisie kulinarnym.
- Container (kontener) – działająca instancja obrazu. To właśnie tutaj żyje nasza aplikacja.
- Dockerfile – plik tekstowy zawierający zestaw instrukcji do budowania obrazu Docker.
- Docker Hub – publiczne repozytorium obrazów, z którego możemy pobierać gotowe rozwiązania (np. obrazy baz danych, serwerów WWW).
- Docker Compose – narzędzie do definiowania i uruchamiania aplikacji składających się z wielu kontenerów.
- Volume (wolumin) – mechanizm trwałego przechowywania danych poza cyklem życia kontenera.
Instalacja i pierwsze kroki
Instalacja Dockera jest prosta i dobrze udokumentowana. Na stronie docker.com znajdziesz instalatory dla Windows, macOS oraz różnych dystrybucji Linuxa. Zalecane jest pobranie Docker Desktop, które zawiera wszystkie niezbędne narzędzia, w tym Docker Compose i GUI do zarządzania kontenerami.
Po instalacji możesz przetestować środowisko klasycznym poleceniem:
docker run hello-world
Jeśli wszystko działa poprawnie, Docker pobierze obraz hello-world z Docker Hub i wyświetli stosowny komunikat potwierdzający poprawną instalację.
Twój pierwszy Dockerfile
Sercem każdego projektu dockeryzowanego jest plik Dockerfile. Zobaczmy, jak wygląda przykładowy plik dla prostej aplikacji Node.js:
# Wybieramy obraz bazowy
FROM node:20-alpine
# Ustawiamy katalog roboczy w kontenerze
WORKDIR /app
# Kopiujemy pliki z zależnościami
COPY package*.json ./
# Instalujemy zależności
RUN npm install
# Kopiujemy resztę kodu aplikacji
COPY . .
# Otwieramy port 3000
EXPOSE 3000
# Definiujemy polecenie uruchamiające aplikację
CMD ["node", "server.js"]
Każda linia Dockerfile to jedna warstwa obrazu. Docker inteligentnie cachuje warstwy – jeśli zmienisz tylko kod aplikacji (bez modyfikacji package.json), nie będzie potrzeby ponownej instalacji zależności. To znacząco przyspiesza czas budowania.
Aby zbudować obraz na podstawie tego pliku, uruchamiamy:
docker build -t moja-aplikacja:1.0 .
A następnie uruchamiamy kontener:
docker run -p 3000:3000 moja-aplikacja:1.0
Flaga -p 3000:3000 mapuje port 3000 kontenera na port 3000 naszego hosta, dzięki czemu możemy uzyskać dostęp do aplikacji przez przeglądarkę.
Docker Compose – zarządzanie wieloma kontenerami
Rzeczywiste aplikacje rzadko kiedy składają się z jednego kontenera. Typowy projekt webowy to przynajmniej: aplikacja backendowa, baza danych i może cache Redis. Docker Compose pozwala zdefiniować całe środowisko w jednym pliku docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=postgres
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
volumes:
- .:/app
- /app/node_modules
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Teraz wystarczy jedno polecenie, aby uruchomić całe środowisko:
docker-compose up -d
Flaga -d uruchamia kontenery w tle (tryb detached). Aby zatrzymać wszystko, używamy docker-compose down. Warto zauważyć, że Docker Compose automatycznie tworzy dedykowaną sieć dla wszystkich serwisów – dzięki temu kontenery mogą się komunikować między sobą używając nazw serwisów jako hostnames.
Woluminy i persystencja danych
Kontenery są z natury efemeryczne – po usunięciu kontenera wszystkie dane w nim przechowywane znikają. W powyższym przykładzie widzimy rozwiązanie tego problemu: woluminy.
Istnieją dwa główne typy:
- Named volumes (np.
postgres_data) – zarządzane przez Docker, idealne do przechowywania danych bazy danych. - Bind mounts (np.
.:/app) – mapowanie katalogu z hosta do kontenera, świetne podczas developmentu, gdy chcemy, aby zmiany w kodzie były natychmiast widoczne w kontenerze.
Bind mount w przykładzie z Node.js sprawia, że nie musimy przebudowywać obrazu za każdym razem, gdy zmienimy kod – zmiany są od razu dostępne w kontenerze. W połączeniu z narzędziami takimi jak nodemon, uzyskujemy wygodne środowisko z automatycznym przeładowywaniem.
Przydatne polecenia Docker na co dzień
Oto zestaw poleceń, które będziesz używać najczęściej:
# Wyświetl listę działających kontenerów
docker ps
# Wyświetl wszystkie kontenery (również zatrzymane)
docker ps -a
# Wejdź do powłoki działającego kontenera
docker exec -it CONTAINER_ID bash
# Wyświetl logi kontenera
docker logs -f CONTAINER_ID
# Wyświetl listę obrazów
docker images
# Usuń zatrzymane kontenery, nieużywane sieci i obrazy
docker system prune
# Wyświetl statystyki zużycia zasobów
docker stats
Polecenie docker exec -it CONTAINER_ID bash jest szczególnie przydatne do debugowania – pozwala "wejść" do wnętrza działającego kontenera i sprawdzić jego stan bezpośrednio z poziomu terminala.
Dobre praktyki w pracy z Dockerem
Poznanie Dockera to jedno, ale efektywne i bezpieczne jego używanie to zupełnie inna kwestia. Oto kilka zasad, których warto przestrzegać:
- Używaj lekkich obrazów bazowych – wersje
alpinesą znacznie mniejsze niż standardowe. Obraznode:20-alpineważy kilkadziesiąt MB, podczas gdynode:20to kilkaset MB. - Nie przechowuj sekretów w Dockerfile – hasła i klucze API powinny być przekazywane przez zmienne środowiskowe lub dedykowane rozwiązania jak Docker Secrets czy zewnętrzne narzędzia (Vault, AWS Secrets Manager).
- Stosuj plik .dockerignore – podobnie jak
.gitignore, pozwala wykluczyć niepotrzebne pliki (np.node_modules,.git) z kontekstu budowania, co przyspiesza proces i zmniejsza rozmiar obrazu. - Jeden proces na kontener – zgodnie z filozofią Unix, każdy kontener powinien wykonywać jedno zadanie. To ułatwia skalowanie i debugowanie.
- Taguj obrazy semantycznie – zamiast polegać na tagu
latest, używaj konkretnych wersji (np.moja-app:2.1.0), co zapewnia przewidywalność wdrożeń. - Optymalizuj kolejność warstw – umieszczaj instrukcje, które zmieniają się rzadko (np. instalacja zależności), przed tymi, które zmieniają się często (kopiowanie kodu). Maksymalizuje to korzyści z cache.
Docker w pipeline CI/CD
Docker świetnie integruje się z narzędziami CI/CD takimi jak GitHub Actions, GitLab CI czy Jenkins. Typowy pipeline wygląda następująco: po pushu kodu do repozytorium, system CI buduje obraz Docker, uruchamia testy wewnątrz kontenera, a następnie publikuje gotowy obraz do rejestru (Docker Hub, AWS ECR, GitHub Container Registry). Serwer produkcyjny pobiera nowy obraz i zastępuje nim poprzednią wersję – zazwyczaj bez żadnego downtime'u dzięki mechanizmom rolling update.
Taki przepływ pracy gwarantuje, że środowisko testowe i produkcyjne są identyczne, eliminując całą klasę błędów związanych z różnicami w konfiguracji.
Podsumowanie
Docker stał się standardem w branży i znajomość konteneryzacji jest dziś praktycznie wymagana od każdego programisty pracującego z aplikacjami webowymi czy mikroserwisami. Zaczynając od prostego Dockerfile, przez Docker Compose, aż po integrację z pipeline'ami CI/CD – każdy krok przynosi realne korzyści w postaci szybszego developmentu, większej niezawodności i łatwiejszego wdrażania aplikacji.
Najlepszym sposobem nauki jest praktyka – weź swój bieżący projekt i spróbuj go skonteneryzować. Napotkasz trudności, ale każda z nich nauczy Cię czegoś nowego o tym, jak naprawdę działa Twoja aplikacja. Docker to inwestycja, która zwraca się bardzo szybko.