Jak zacząć z Dockerem: praktyczny przewodnik dla administratorów i programistów

0
78
2.7/5 - (3 votes)

Z tej publikacji dowiesz się:

Po co w ogóle Docker? Kryteria decyzji zamiast hype’u

Kontenery w praktyce: izolacja procesów bez ciężkiej wirtualizacji

Docker opiera się na prostym założeniu: izolować procesy, a nie całe systemy operacyjne. Kontener to po prostu proces uruchomiony z dodatkowymi ograniczeniami (namespaces, cgroups), który współdzieli jądro systemu z hostem. Dzięki temu startuje w sekundy, zużywa mniej zasobów i łatwiej go przenieść między środowiskami.

W tradycyjnym podejściu instalujesz aplikację bezpośrednio na serwerze: pakiety, biblioteki, konfiguracje – wszystko miesza się w jednym systemie. Docker pozwala zamknąć cały stos aplikacji (runtime, zależności, konfigurację) w obrazie, a potem uruchamiać go wielokrotnie jako niezależne kontenery. Każdy działa w swoim namespace’ie, ma własny system plików (oparty o warstwy obrazu) i oddzielne zmienne środowiskowe.

Z punktu widzenia admina oznacza to mniej konfliktów pakietów, szybsze przywracanie usług (restart kontenera trwa moment) i powtarzalne wdrożenia. Dla programisty kontenery to wygodne „pudełko” z gotowym środowiskiem – identycznym lokalnie, na serwerze testowym i produkcyjnym.

Jeżeli głównym bólem są ciągłe różnice konfiguracji między maszynami i „niewidzialne” zależności w systemie, konteneryzacja zwykle rozwiązuje problem na poziomie architektury, a nie pojedynczych skryptów konfiguracyjnych.

Kontener vs maszyna wirtualna: praktyczne różnice

Maszyna wirtualna symuluje pełny hardware i uruchamia osobny system operacyjny. Kontener wykorzystuje system hosta, izolując tylko procesy. Ta różnica przekłada się na codzienną pracę admina i dewelopera.

CechaKontenery (Docker)Maszyny wirtualne
Czas startuSekundyDziesiątki sekund / minuty
Zużycie RAM / CPUNiewielki narzut ponad proces aplikacjiPełny OS + narzut hypervisora
Aktualizacje systemuHost + ewentualnie obrazy bazoweKażda VM aktualizowana osobno
IzolacjaPoziom procesu (namespaces, cgroups)Pełna izolacja systemu operacyjnego
ScenariuszeAplikacje, mikrousługi, CI/CDLegacy OS, ciężkie systemy, sandbox

Programista często woli kontener, bo szybciej podnosi środowisko testowe i łatwo sprawdza różne wersje zależności. Administrator z kolei używa VM tam, gdzie potrzebna jest twarda granica bezpieczeństwa lub inny system operacyjny niż host. Kontenery nie zastępują hypervisora – raczej uzupełniają go, dając lekki poziom izolacji wewnątrz istniejącej infrastruktury.

Jeśli podstawową potrzebą jest elastyczna praca z aplikacjami i zależnościami, a nie wielosystemowe środowisko, Docker daje lepszy stosunek wysiłku do zysku niż klasyczne VM.

Typowe scenariusze użycia Dockera w projektach IT

Kontenery nie są odpowiedzią na wszystko, ale świetnie sprawdzają się w kilku powtarzalnych scenariuszach.

  • Środowiska deweloperskie i testowe – szybkie podnoszenie bazy danych, cache, brokera kolejki czy całego stosu aplikacji. Zamiast instrukcji „zainstaluj PostgreSQL, Redis, RabbitMQ” jest plik konfiguracyjny i jedno polecenie.
  • Mikrousługi – każda usługa w swoim obrazie, niezależne release’y, osobne zależności. Łatwiej skalować poziomo i izolować awarie poszczególnych komponentów.
  • CI/CD – budowanie artefaktów w powtarzalnym środowisku, testy w kontenerach, pipeline’y, które nie zależą od stanu agenta buildującego.
  • Narzędzia pomocnicze – jednorazowe uruchamianie narzędzi administracyjnych (np. klienta bazy) bez instalowania niczego na hoście.

Kiedy Docker jest przerostem formy nad treścią

Są sytuacje, w których konteneryzacja dodaje warstwę złożoności bez realnego zysku. Typowe przykłady:

  • Małe, jednoużytkownikowe skrypty – prosty backup czy raport uruchamiany raz dziennie na jednym serwerze. Tworzenie obrazu, rejestru i pipeline’u często kosztuje więcej niż zwykły cron.
  • Stary monolit „działający od lat” na jednej maszynie – jeśli nie ma planu skalowania, refaktoryzacji czy migracji, wrzucenie go do kontenera nie naprawi architektury. Dojdzie tylko warstwa Dockera do utrzymania.
  • Ciasne zasoby – serwery z bardzo ograniczonym RAM/CPU, na których nawet lekki narzut może zaboleć. Zwłaszcza, gdy brakuje kompetencji do tuningu.
  • Brak zespołu utrzymaniowego – jeśli nikt realnie nie odpowiada za obrazy, rejestr i aktualizacje, kontenery bardzo szybko zamienią się w niekontrolowany bałagan.

Sam fakt, że „wszyscy przechodzą na Dockera”, nie jest argumentem. Jeśli system ma prostą infrastrukturę, niewielką dynamikę zmian i brak planów rozwoju, konteneryzacja bywa typowym przykładem przerostu formy nad treścią.

Punkty kontrolne przed decyzją o wejściu w Dockera

Decyzję o Dockerze warto oprzeć na konkretnych kryteriach. Prosta checklista minimalizuje ryzyko decyzji pod wpływem mody:

  • Powtarzalność środowisk – czy dziś konfiguracja dev/test/prod jest rękodziełem, czy da się ją uruchomić z jednego opisu?
  • Częstotliwość zmian – jak często wychodzą nowe wersje aplikacji i zależności? Im częściej, tym bardziej opłaca się automatyzacja w kontenerach.
  • Skala zespołu – czy więcej niż jedna osoba rozwija/utrzymuje dany system? Im większy zespół, tym większy zysk z ujednolicenia środowisk.
  • Wymagania bezpieczeństwa – czy istnieją polityki, które można odzwierciedlić w obrazach, skanach i podpisach? Czy są już narzędzia do audytu?
  • Dostępna wiedza – czy zespół ma minimum kompetencji w zakresie Dockera, czy to pierwszy kontakt i brak dedykowanego czasu na naukę?

Jeśli główną bolączką jest „u mnie działa”, środowiska różnią się między zespołami i występują powtarzalne problemy z konfiguracją, Docker jest logicznym kierunkiem. Gdy system jest stabilnym monolitem na jednej maszynie bez perspektyw rozwoju, wdrożenie Dockera często generuje dodatkowe ryzyko bez równoważących korzyści.

Pracownik na placu budowy w Helsingør widziany z góry
Źródło: Pexels | Autor: Kai Pilger

Fundamenty Dockera: pojęcia, które trzeba rozumieć przed pierwszym docker run

Obraz, kontener i rejestr: trzy filary konteneryzacji

Obraz to nieuruchomiony szablon aplikacji – zawiera system plików, binaria, biblioteki, konfigurację startową i instrukcję (CMD/ENTRYPOINT), co ma zostać uruchomione. Jest niezmienny: jeśli chcesz coś zmienić, budujesz nowy obraz.

Kontener to uruchomiona instancja obrazu – działający proces z przypisanym systemem plików, siecią i zasobami. Z jednego obrazu możesz wystartować dowolną liczbę kontenerów; każdy ma własny stan (np. pliki w katalogach, które nie są wolumenami).

Rejestr to miejsce przechowywania obrazów. Może być publiczny (Docker Hub, GitHub Container Registry) lub prywatny (np. Harbor, GitLab Container Registry, ECR). Do rejestru pushujesz zbudowane obrazy, a potem zaciągasz je (pull) na inne hosty.

Jeśli w zespole ktoś używa słów „obraz” i „kontener” zamiennie, to sygnał ostrzegawczy: diagnoza problemów staje się wtedy chaotyczna, bo nikt nie wie, czy mówimy o definicji, czy o instancji. Dopiero jasne rozróżnienie tych pojęć pozwala sensownie rozmawiać o wersjonowaniu i błędach na produkcji.

Warstwy obrazu i tagi: jak Docker przechowuje wersje

Obrazy Dockera składają się z warstw. Każda instrukcja w Dockerfile (FROM, RUN, COPY, ADD itd.) tworzy nową warstwę. Warstwy są współdzielone między obrazami, co oszczędza miejsce na dysku. Na przykład kilka obrazów bazujących na tej samej wersji ubuntu dzieli wspólne warstwy systemowe.

Każdy obraz identyfikowany jest przez nazwę repozytorium i tag: nazwa-repozytorium:tag (np. nginx:1.23-alpine). Tag to po prostu etykieta wskazująca na konkretny zestaw warstw. Brak tagu oznacza domyślnie :latest.

Tutaj pojawia się ważny sygnał ostrzegawczy: używanie :latest w środowiskach testowych i produkcyjnych. Taki tag jest ruchomy – może w każdej chwili wskazywać na inną wersję. Trudniej wtedy odtworzyć incydent, bo nie wiadomo, jaka wersja obrazu działała na danym serwerze w konkretnym momencie.

Jeśli nazwy obrazów i tagi są projektowane świadomie (np. uwzględniają wersję aplikacji, datę buildu, środowisko), audyt wersji i analiza zdarzeń stają się znacznie prostsze. Jeśli wszystko jest „latest”, każda awaria zamienia się w dochodzenie „co my właściwie uruchomiliśmy”.

Klient i daemon Dockera: co się naprawdę dzieje przy pull/run/stop

Docker składa się z dwóch głównych elementów: klienta (polecenie docker) i daemona (dockerd). Klient wysyła do daemona żądania przez API (zwykle Unix socket), a daemon zarządza obrazami, kontenerami, sieciami i wolumenami.

  • docker pull – klient prosi daemon o pobranie obrazu z rejestru. Daemon sprawdza, które warstwy ma już lokalnie, i dociąga tylko brakujące.
  • docker run – w jednej operacji robiony jest ewentualny pull, tworzony jest kontener na bazie obrazu, a następnie uruchamiany jest proces wewnątrz kontenera.
  • docker stop – daemon wysyła do głównego procesu w kontenerze sygnał SIGTERM, czeka określony czas, a potem w razie potrzeby SIGKILL.
  • docker rm – usuwa kontener (metadane i system plików kontenera). Obraz pozostaje na dysku, dopóki nie usuniesz go poleceniem docker rmi.

Rozumienie tej architektury pomaga przy diagnostyce problemów. Jeśli klient „wisi”, a daemon ma problemy z dyskiem czy siecią, źródło kłopotów zwykle leży po stronie daemona, a nie samej komendy.

Oficjalne i prywatne rejestry obrazów

Publiczne rejestry (Docker Hub, GitHub Container Registry) kuszą wygodą. Jedno docker pull postgres i baza jest gotowa. Z perspektywy bezpieczeństwa i stabilności to jednak nie wszystko.

  • Oficjalne obrazy – publikowane przez zaufanych dostawców. Warto sprawdzać politykę wsparcia i częstotliwość aktualizacji.
  • Obrazy społecznościowe – tworzone przez użytkowników. Często wygodne, ale obarczone ryzykiem nieznanych modyfikacji i braku aktualizacji.
  • Prywatne rejestry – kontrola nad tym, jakie obrazy mogą trafić na produkcję, możliwość skanowania bezpieczeństwa, podpisywania obrazów, nadawania uprawnień.

Jeśli organizacja dopuszcza dowolne obrazy z publicznego Docker Huba bez procesu akceptacji, to czytelny sygnał ostrzegawczy w audycie bezpieczeństwa. Minimum to jasna lista dozwolonych baz oraz mechanizm regularnego skanowania i aktualizacji.

Minimum terminologiczne przed diagnozowaniem problemów

Przed próbą debugowania warto, aby każdy w zespole rozumiał te pojęcia na identycznym poziomie:

  • Image – szablon, niezmienny pakiet z aplikacją i środowiskiem.
  • Container – działająca instancja obrazu, proces z izolacją.
  • Registry – serwer przechowujący obrazy.
  • Volume – mechanizm trwałego przechowywania danych poza warstwą kontenera.
  • Network – wirtualna sieć łącząca kontenery i hosta.
  • Docker daemon – proces zarządzający kontenerami.

Jeśli zespół ma wspólny słownik, analiza incydentów jest szybsza i mniej konfliktowa. Gdy każdy inaczej rozumie pojęcia, nawet proste błędy zamieniają się w długie spory o to, co faktycznie się wydarzyło.

Plac budowy z dźwigiem i wywrotką w zachodniej Jawie, Indonezja
Źródło: Pexels | Autor: Dio Hasbi Saniskoro

Instalacja i podstawowa konfiguracja: stabilny punkt startowy

Opcje instalacji na Linux, Windows i macOS

Linux: rekomendowana platforma dla środowisk produkcyjnych

Na serwery linuksowe Docker trafia najczęściej. To tam widać pełnię możliwości – brak dodatkowej wirtualizacji, przewidywalne zachowanie, mniejszy narzut.

Podstawowe opcje instalacji:

  • Repozytoria dystrybucji – pakiety docker.io, docker lub podobne. Zaletą jest prostota, wadą często starsze wersje i brak pełnego wsparcia producenta.
  • Oficjalne repozytoria Dockera – instalacja docker-ce według dokumentacji producenta. Aktualniejsze wydania, stabilne kanały (stable/test) i przewidywalny cykl wersji.
  • Pakiety wbudowane w platformy chmurowe – np. gotowe obrazy VM z preinstalowanym Dockerem. Szybki start, ale nie zawsze pełna kontrola nad wersjami.

Punkt kontrolny: na serwerach produkcyjnych lepiej unikać mieszaniny różnych źródeł pakietów. Jeśli część hostów ma Dockera z repozytoriów dystrybucji, a część z oficjalnego repo, diagnostyka rozbieżnych zachowań staje się znacznie trudniejsza.

Minimalny zestaw po instalacji:

Przy tych zastosowaniach kontenery rozwiązują realne problemy: skracają czas konfiguracji, redukują różnice między środowiskami i ułatwiają automatyzację. W wielu organizacjach Docker naturalnie łączy się z narzędziami typu Ansible czy Terraform, tworząc spójny ekosystem automatyzacji; w takich przypadkach warto znać też więcej o informatyka, by całość architektury nie była zbiorem przypadkowych narzędzi.

  • sprawdzenie wersji docker version i docker info,
  • dodanie właściwych użytkowników do grupy docker (lub świadoma decyzja o sudo docker),
  • konfiguracja startu usługi przez systemd (np. systemctl enable --now docker).

Jeśli host produkcyjny ma inne wersje Dockera niż środowiska testowe, każdy incydent wymaga dodatkowego pytania: „czy to błąd aplikacji, czy różnica w silniku kontenerów?”. Ujednolicenie wersji między środowiskami eliminuje tę zmienną.

Windows: Docker Desktop, WSL2 i serwerowe ograniczenia

Na Windowsie sytuacja jest bardziej złożona. Dla stacji deweloperskich najczęściej używa się Docker Desktop, który pod spodem uruchamia lekką maszynę linuksową (WSL2 lub Hyper-V) i tam startuje kontenery.

  • Docker Desktop – wygodne GUI, integracja z WSL2, aktualizacje z jednego kanału. Wymaga jednak odpowiednich licencji w organizacji (sprawdzenie polityki licencyjnej jest obowiązkowym punktem kontrolnym).
  • WSL2 + docker-ce w dystrybucji Linux – bardziej „manualne” podejście. Docker instalowany wewnątrz WSL2, z linii poleceń. Mniej wygodny dla początkujących, ale daje większą kontrolę i uniezależnia się od Desktopa.
  • Windows Server + kontenery Windows – osobny świat, obrazy Windows, inne ograniczenia i zgodność wersji jądra. Kontenery Windows i linuksowe to nie jest to samo, co często bywa mylnym założeniem.

Sygnał ostrzegawczy: zespół miesza w jednym projekcie kontenery linuksowe i windowsowe bez rozumienia różnic (np. próbuje uruchamiać ten sam obraz na obu platformach). Prowadzi to do trudnych do wyjaśnienia błędów i fragmentacji pipeline’ów CI.

Jeśli programiści pracują na Windowsie, a produkcja działa na Linuksie, minimalnym krokiem jest ustawienie WSL2 i używanie linuksowych obrazów już na etapie developmentu. Inaczej „u mnie działa” będzie pojawiać się regularnie przy przejściu na serwery.

macOS: środowisko developerskie, nie produkcja

Na macOS Docker zawsze działa przez warstwę wirtualizacji. Docker Desktop dostarcza gotową maszynę linuksową i integrację z systemem, co jest wystarczające dla większości programistów.

Główne punkty kontrolne przy konfiguracji:

  • limity przydzielonych zasobów (CPU, RAM, dysk) w ustawieniach Docker Desktop – zbyt niski limit powoduje „dziwne” przycinki kontenerów, zbyt wysoki spowalnia cały system,
  • foldery współdzielone między macOS a VM Dockera – przy intensywnym I/O potrafią stać się wąskim gardłem,
  • spójność wersji Dockera z Linuxem na serwerach, aby uniknąć różnic w zachowaniu.

Jeśli macOS jest używany do developmentu systemów produkcyjnych na Linuksie, a konfiguracja Desktopa pozostaje „fabryczna”, często pierwsze symptomy problemów pojawiają się dopiero w CI lub na testach wydajnościowych. Przegląd ustawień zasobów powinien być jednym z pierwszych kroków po instalacji.

Podstawowe parametry daemona: gdzie kończą się domyślne ustawienia

Po samej instalacji pojawia się pytanie: czy domyślna konfiguracja Docker daemona jest akceptowalna do produkcji? W wielu przypadkach – nie. Minimum to świadoma edycja pliku /etc/docker/daemon.json (Linux) lub odpowiednich ustawień na innych systemach.

Kluczowe obszary konfiguracji:

  • Storage driver – np. overlay2 na współczesnych Linuksach. Niektóre kombinacje jądra i driverów (np. devicemapper w trybie loop-lvm) są sygnałem ostrzegawczym dla wydajności i stabilności.
  • Logowanie – domyślny driver json-file bez rotacji potrafi zapełnić dysk. Konfiguracja rotacji (np. max-size, max-file) lub przejście na zewnętrzny system logowania (syslog, fluentd) często jest niezbędne.
  • Rejestr prywatny / mirror – definicja lokalnego mirroru Docker Huba lub prywatnego registry, aby ograniczyć zależność od zewnętrznych usług i przyspieszyć buildy/pulle.
  • Ograniczenia eksperymentalne – wyłączenie trybów experimental w środowiskach produkcyjnych, jeśli nie są używane świadomie.

Punkt kontrolny: jeśli dyski serwerów regularnie się zapełniają, a nikt nie wie, gdzie leżą dziesiątki gigabajtów logów i warstw, brak konfiguracji log rotation w Dockerze jest jednym z pierwszych miejsc do sprawdzenia.

Utworzenie użytkownika i zasady dostępu: kto może uruchamiać kontenery

Domyślna konfiguracja, gdzie każdy użytkownik z grupy docker ma pełną kontrolę nad daemonem, jest w praktyce równoważna z rootem. W środowiskach produkcyjnych nie powinno się dodawać użytkowników „dla wygody” bez analizy skutków.

Podstawowe podejście:

  • na serwerach produkcyjnych dostęp do docker ograniczony do technicznych kont CI/CD oraz administratorów,
  • na stacjach developerskich – świadome dodanie użytkownika do grupy docker, ale z jasnymi zasadami, że kontenery nie są „piaskownicą bez konsekwencji”,
  • rozważenie użycia zewnętrznych narzędzi (np. sudo z ograniczeniami) zamiast masowego dodawania do grupy docker.

Jeśli w logach bezpieczeństwa pojawia się wielu użytkowników uruchamiających losowe obrazy z Internetu bez żadnego nadzoru, to nie jest „eksperymentowanie z Dockerem”, tylko poważny sygnał ostrzegawczy dla działu bezpieczeństwa.

Spójne wersje i kanały aktualizacji

Różne hosty z różnymi wersjami Dockera to proszenie się o niespójności. W jednym środowisku działa konkretny parametr, w innym ta sama flaga ma inne znaczenie lub nie istnieje. Standaryzacja wersji powinna być elementem podstawowej polityki utrzymania.

Minimalny zestaw zasad:

  • wybór kanału aktualizacji (np. stable),
  • zaplanowany cykl aktualizacji (np. kwartalny) testowany najpierw na środowiskach nieprodukcyjnych,
  • prowadzenie inwentaryzacji hostów z Dockera wraz z wersjami (może być proste zestawienie z docker info zbierane przez CMDB lub skrypt).

Jeśli organizacja nie jest w stanie odpowiedzieć na proste pytanie „jaką wersję Dockera mamy na produkcji i które hosty różnią się od standardu?”, to znaczy, że minimum zarządzania konfiguracją nie jest spełnione.

Dłonie obracające koreks podczas wywoływania kliszy fotograficznej
Źródło: Pexels | Autor: Annushka Ahuja

Pierwsze kontenery: od docker run do prostego środowiska

Najprostszy test: uruchomienie kontenera i sprawdzenie logów

Zanim przejdzie się do własnych aplikacji, sensowne jest uruchomienie prostego, oficjalnego obrazu. Pozwala to sprawdzić, czy daemon działa, sieć jest poprawnie skonfigurowana, a dysk ma wystarczająco miejsca.

Przykładowy start:

docker run --rm hello-world

Jeśli ten kontener startuje bez problemu, można przejść do prostszych usług sieciowych:

docker run --rm -p 8080:80 nginx:alpine

Kilka minut testowania:

  • sprawdzenie docker ps, czy kontener działa,
  • wejście na http://localhost:8080 i weryfikacja odpowiedzi,
  • analiza logów przez docker logs <id_kontenera>.

Jeśli tak prosty test nie działa (brak odpowiedzi HTTP, błędy DNS, konflikty portów), przechodzenie do skomplikowanych scenariuszy z własnymi obrazami jest jak budowa piętra bez fundamentów. Najpierw trzeba ustalić, czy problem leży w sieci, firewallu, czy błędnej konfiguracji Dockera.

Dobrym uzupełnieniem będzie też materiał: Ansible 101: automatyzujemy konfigurację wielu serwerów — warto go przejrzeć w kontekście powyższych wskazówek.

Mapowanie portów: jak wystawić usługę na zewnątrz

Standardowy błąd przy pierwszych uruchomieniach to założenie, że serwis w kontenerze jest od razu dostępny na porcie hosta. Tymczasem bez opcji -p / --publish usługa pozostaje zamknięta w sieci kontenerowej.

Podstawowy wzór:

docker run -d -p <port_hosta>:<port_kontenera> obraz

Przykład:

docker run -d -p 5432:5432 --name pg-test postgres:16-alpine

Punkty kontrolne przy mapowaniu portów:

  • czy port na hoście nie jest już zajęty (np. inna instancja serwera na bare metal),
  • czy firewall systemowy lub sieć między hostem a klientem nie blokuje ruchu,
  • czy aplikacja wewnątrz kontenera nasłuchuje na właściwym interfejsie (nie tylko localhost w środku kontenera).

Jeśli przy próbie połączenia z usługą w kontenerze odpowiedź jest „connection refused” lub „timeout”, sprawdzenie mapowania portów i firewalli zwykle rozwiązuje 80% przypadków. Szukanie przyczyny w kodzie aplikacji przed tym krokiem jest stratą czasu.

Wolumeny: gdzie trzymać trwałe dane

Domyślny system plików kontenera jest ulotny. Usunięcie kontenera (np. docker rm lub użycie --rm) oznacza utratę danych zapisanych wewnątrz. Dlatego pojawia się pojęcie wolumenów (volumes), które pozwalają na trwałe przechowywanie danych poza warstwą kontenera.

Podstawowe typy:

  • wolumeny zarządzane przez Dockera – tworzone przez docker volume create lub automatycznie przy -v volume_name:/sciezka,
  • bind mounts – bezpośrednie mapowanie katalogu hosta: -v /dane/pg:/var/lib/postgresql/data.

Przykład z postgres:

docker volume create pg-data
docker run -d 
  -p 5432:5432 
  -v pg-data:/var/lib/postgresql/data 
  --name pg-db 
  -e POSTGRES_PASSWORD=sekret 
  postgres:16-alpine

Punkty kontrolne przy danych:

  • jasna decyzja, które dane mają być trwałe (bazy, uploady, konfiguracje) a które mogą być ulotne (cache, tymczasowe pliki),
  • backup i odtwarzanie wolumenów – bez testu odtworzenia kopii bezpieczeństwa „backup” jest tylko deklaracją,
  • uprawnienia do katalogów przy bind mounts – niezgodność UID/GID między hostem a kontenerem generuje błędy zapisu.

Jeśli kontener z bazą czy inną usługą stanową jest uruchamiany bez wolumenów, a ma trafiać na produkcję, to jest wyraźny sygnał ostrzegawczy. Pojedynczy restart lub usunięcie kontenera spowoduje utratę danych.

Zmienne środowiskowe i konfiguracja aplikacji

Konfiguracja aplikacji w kontenerach powinna być w miarę możliwości zewnętrzna względem obrazu. Najprostszy mechanizm to zmienne środowiskowe (-e / --env) lub plik --env-file.

Przykład prostego uruchomienia aplikacji:

docker run -d 
  -p 8080:8080 
  --name app 
  -e APP_ENV=prod 
  -e DB_HOST=pg-db 
  myorg/app:1.0.0

W praktyce opłaca się przyjąć standard:

  • konfiguracje wrażliwe (hasła, tokeny) nie trafiają do obrazu, tylko są wstrzykiwane przy uruchomieniu,
  • zmienne środowiskowe mają przewidywalne nazwy i są udokumentowane,
  • różnice między dev/test/prod sprowadzają się głównie do innych wartości env, nie do innych obrazów.

Jeśli aplikacja wymaga przekompilowania obrazu przy każdej zmianie adresu bazy lub endpointu zewnętrznego API, to sygnał, że granica między obrazem (kod + runtime) a konfiguracją została źle poprowadzona.

Interaktywna praca w kontenerze: diagnostyka i szybkie testy

Kontener można uruchomić w trybie interaktywnym, aby podejrzeć jego wnętrze, zdiagnozować pliki, sieć czy zależności. To szczególnie użyteczne przy pierwszych próbach uruchomienia złożonej aplikacji.

Przykłady:

Interaktywna praca w kontenerze: diagnostyka i szybkie testy (cd.)

Tryb interaktywny przydaje się w dwóch sytuacjach: gdy nie wiemy, jak dokładnie działa obraz pobrany z zewnątrz, oraz gdy własna aplikacja „nie wstaje” i potrzeba wglądu do środka.

Podstawowy wzór z powłoką:

docker run --rm -it --entrypoint /bin/sh alpine:3.19

lub jeśli obraz ma tylko /bin/bash:

docker run --rm -it --entrypoint /bin/bash ubuntu:22.04

Do kontenera już działającego można wejść przez:

docker exec -it <nazwa_lub_id> /bin/sh

Podstawowe czynności diagnostyczne:

  • sprawdzenie procesów (ps aux),
  • weryfikacja plików konfiguracji (cat, ls -l),
  • test połączeń sieciowych (ping, curl do innych kontenerów),
  • kontrola uprawnień do katalogów z danymi.

Jeśli do diagnozy każdego problemu od razu modyfikowany jest Dockerfile i budowany nowy obraz, zamiast choć raz zajrzeć do kontenera interaktywnie, to sygnał, że brakuje podstawowego warsztatu pracy z Dockerem.

Sprzątanie po testach: obrazy, kontenery, wolumeny

Po kilku godzinach eksperymentów środowisko potrafi być zasypane starymi obrazami, nieużywanymi wolumenami i zatrzymanymi kontenerami. Mechaniczne usuwanie wszystkiego bez zrozumienia to prosty przepis na utratę danych.

Podstawowe polecenia porządkowe:

  • lista kontenerów (także zatrzymanych): docker ps -a,
  • usunięcie zatrzymanego kontenera: docker rm <id>,
  • lista obrazów: docker images,
  • usunięcie obrazu: docker rmi <id_obrazu>,
  • lista wolumenów: docker volume ls,
  • usunięcie wolumenu: docker volume rm <nazwa>.

Dla bardziej automatycznego podejścia istnieją polecenia typu:

# usunięcie zatrzymanych kontenerów, wiszących obrazów i nieużywanych sieci
docker system prune

# włącznie z nieużywanymi wolumenami (OSTROŻNIE: usuwa dane)
docker system prune --volumes

Punkty kontrolne przed sprzątaniem:

  • czy któryś z wolumenów nie jest jedyną kopią danych testowej bazy lub uploadów,
  • czy obrazy nie są wykorzystywane przez CI/CD lub inne środowiska (tagi typu latest często maskują zależności),
  • czy nie ma kontenerów zatrzymanych tymczasowo w celu diagnostyki (logi, pliki).

Jeśli docker system prune --volumes jest wykonywane automatycznie na produkcji „żeby oszczędzić miejsce”, to sygnał ostrzegawczy, że gospodarka wolumenami i backupami jest nieuporządkowana.

Sieci Dockera: lokalne środowisko wielokontenerowe bez Compose

Nawet bez użycia Dockera Compose można zbudować proste środowisko złożone z kilku kontenerów, jeśli świadomie zarządza się sieciami.

Domyślna sieć bridge jest tworzona automatycznie, ale dla czytelności lepiej utworzyć własną:

docker network create app-net

Następnie przy uruchamianiu kontenerów:

docker run -d 
  --name pg-db 
  --network app-net 
  -e POSTGRES_PASSWORD=sekret 
  postgres:16-alpine

docker run -d 
  --name app 
  --network app-net 
  -p 8080:8080 
  -e DB_HOST=pg-db 
  myorg/app:1.0.0

W takiej konfiguracji kontenery rozpoznają się po nazwach (pg-db, app) przez wewnętrzny DNS, bez konieczności podawania adresów IP.

Punkty kontrolne przy sieciach:

  • czy kontenery, które mają się widzieć, są w tej samej sieci Dockerowej,
  • czy niepotrzebnie nie wystawiamy bazy danych na port hosta, jeśli ma być dostępna tylko dla aplikacji,
  • czy nie mieszamy środowisk (dev/test) w jednej sieci, co utrudnia analizę ruchu.

Jeśli w konfiguracjach aplikacji nadal wpisywane są twarde adresy IP kontenerów, zamiast korzystać z nazw usług w sieci Dockerowej, to znak, że integracja jest zrobiona „po staremu” i będzie generowała problemy przy każdym restarcie.

Prosty stos: aplikacja + baza + narzędzie administracyjne

Dla wielu zespołów rozsądny pierwszy krok to zbudowanie prostego, ale kompletnego środowiska: aplikacja, baza danych oraz narzędzie do podglądu/administracji (np. pgAdmin, Adminer).

Jeśli chcesz pójść krok dalej, pomocny może być też wpis: Jakie dane są chronione przy użyciu szyfrowania.

Przykładowa konfiguracja w oparciu o sieć i wolumeny:

docker network create demo-net
docker volume create demo-pg-data

docker run -d 
  --name demo-pg 
  --network demo-net 
  -v demo-pg-data:/var/lib/postgresql/data 
  -e POSTGRES_PASSWORD=sekret 
  -e POSTGRES_DB=demo 
  postgres:16-alpine

docker run -d 
  --name demo-adminer 
  --network demo-net 
  -p 8081:8080 
  -e ADMINER_DEFAULT_SERVER=demo-pg 
  adminer:latest

docker run -d 
  --name demo-app 
  --network demo-net 
  -p 8080:8080 
  -e DB_HOST=demo-pg 
  -e DB_NAME=demo 
  myorg/app:1.0.0

W takim zestawie:

  • aplikacja łączy się z bazą po nazwie demo-pg,
  • Adminer jest dostępny na http://localhost:8081,
  • dane bazy są trwałe w wolumenie demo-pg-data.

Jeśli pierwsze środowisko demo jest zbudowane z gołych binarek instalowanych ręcznie na serwerze, a kontenery mają „pojawić się później”, to sygnał, że wdrożenia będą dublowane i niespójne. Lepiej od razu trenować z użyciem Dockera w pełnym przepływie.

Prosta orkiestracja lokalna z Docker Compose

Przy więcej niż dwóch–trzech kontenerach pojedyncze komendy docker run szybko stają się uciążliwe. Minimum porządku zapewnia Docker Compose, który opisuje środowisko w jednym pliku YAML.

Przykładowy docker-compose.yml dla prostego stosu:

version: "3.9"

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: sekret
      POSTGRES_DB: demo
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net

  adminer:
    image: adminer:latest
    ports:
      - "8081:8080"
    environment:
      ADMINER_DEFAULT_SERVER: db
    networks:
      - app-net
    depends_on:
      - db

  app:
    image: myorg/app:1.0.0
    ports:
      - "8080:8080"
    environment:
      APP_ENV: dev
      DB_HOST: db
      DB_NAME: demo
    networks:
      - app-net
    depends_on:
      - db

volumes:
  db-data:

networks:
  app-net:

Uruchomienie:

docker compose up -d

Podstawowe korzyści:

  • jedno źródło prawdy o usługach, portach, wolumenach i sieciach,
  • prosty start/stop całego środowiska (docker compose down),
  • możliwość wersjonowania konfiguracji w repozytorium.

Jeśli każdy deweloper ma własny zestaw skryptów bash uruchamiających kontenery w różnej kolejności i z różnymi parametrami zamiast wspólnego pliku Compose, to znak, że minimum standaryzacji nie zostało osiągnięte.

Profile środowiskowe w Docker Compose

Compose umożliwia różnicowanie konfiguracji środowisk bez utrzymywania wielu prawie identycznych plików. Jedną z metod są profile, które włączają określone usługi tylko w wybranych kontekstach.

Przykład z profilami dev i prod:

version: "3.9"

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: sekret
      POSTGRES_DB: demo
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-net

  adminer:
    image: adminer:latest
    ports:
      - "8081:8080"
    environment:
      ADMINER_DEFAULT_SERVER: db
    networks:
      - app-net
    depends_on:
      - db
    profiles:
      - dev

  app:
    image: myorg/app:${APP_TAG:-1.0.0}
    ports:
      - "8080:8080"
    environment:
      APP_ENV: ${APP_ENV:-dev}
      DB_HOST: db
      DB_NAME: demo
    networks:
      - app-net
    depends_on:
      - db

volumes:
  db-data:

networks:
  app-net:

Uruchomienie w trybie developerskim:

docker compose --profile dev up -d

Uruchomienie w trybie zbliżonym do produkcji (bez Adminera):

docker compose up -d

Jeśli do odtworzenia produkcji na środowisku testowym potrzebne są dwa równoległe pliki Compose kopiowane ręcznie z różnych katalogów, to sygnał ostrzegawczy, że podejście do konfiguracji jest nieskalowalne.

Minimalne praktyki CI/CD dla obrazów Dockera

Nawet proste użycie Dockera zyskuje na jakości, gdy proces budowania obrazów jest zautomatyzowany. Budowanie ręcznie na stacji deweloperskiej i wypychanie obrazów bez kontroli wersji to droga do trudnych do odtworzenia błędów.

Minimum pipeline’u dla obrazu:

  1. pobranie kodu z repozytorium,
  2. budowa obrazu z jednoznacznie wersjonowanym tagiem (np. app:1.2.3),
  3. uruchomienie testów jednostkowych/integracyjnych w kontenerze,
  4. push obrazu do prywatnego registry (np. registry.example.com/myorg/app:1.2.3),
  5. oznaczenie obrazu tagiem środowiskowym (dev, test, prod) poprzez dodatkowe tagi, nie nadpisywanie tych samych.

Przykładowe kroki budowy (symbolicznie):

docker build -t registry.example.com/myorg/app:${CI_COMMIT_TAG} .
docker push registry.example.com/myorg/app:${CI_COMMIT_TAG}
docker tag registry.example.com/myorg/app:${CI_COMMIT_TAG} 
           registry.example.com/myorg/app:dev
docker push registry.example.com/myorg/app:dev

Punkty kontrolne przy CI/CD:

  • czy obraz budowany na CI jest identyczny z tym używanym na produkcji (tag, commit),
  • czy istnieje możliwość odtworzenia obrazu na podstawie tagu/commitu sprzed miesiąca,
  • czy pipeline sprawdza minimalne kryteria jakości (testy, linters, skan bezpieczeństwa).

Jeśli jedynym tagiem używanym w organizacji jest latest, a źródło jego pochodzenia nie jest nigdzie zapisane, to znak, że podstawowe wymagania śledzenia wersji nie są spełnione.

Bezpieczne korzystanie z zewnętrznych obrazów

Przy pierwszych krokach łatwo wykonywać docker run na dowolnym obrazie znalezionym w Internecie. Na stacjach testowych to mniejsze ryzyko, ale na serwerach firmowych każde takie działanie wprowadza potencjalny wektor ataku.

Podstawowe kryteria oceny obrazu:

  • czy obraz pochodzi z oficjalnego źródła (np. library/postgres, obrazy vendorów),
  • czy repozytorium ma wiarygodnego maintainer’a i aktualne opisy,
  • czy obraz był aktualizowany w rozsądnym czasie (brak aktualizacji przez lata to czerwone światło),
  • czy dostępne są informacje o polityce bezpieczeństwa lub skanowaniu podatności.

Kilka praktyk minimalizujących ryzyko:

  • ograniczenie użycia do zaufanych registries,
  • lokalne mirrorowanie zatwierdzonych obrazów (przez prywatne registry),
  • użycie skanerów obrazów (np. trivy, narzędzia wbudowane w registry) w pipeline CI.

Jeśli serwery produkcyjne mają otwarty dostęp do Docker Huba, a administratorzy uruchamiają przypadkowe obrazy „do testu” bez żadnych wytycznych, to sygnał ostrzegawczy dla działu bezpieczeństwa i compliance.

Standard uruchamiania kontenerów: checklist dla administratora

Faza „pierwszych kontenerów” szybko zamienia się w chaos, jeśli każdy administrator stosuje własny, nieudokumentowany zestaw parametrów. Pomocny jest prosty standard, który staje się punktem wyjścia przy każdym nowym serwisie.

Przykładowa lista elementów, które powinny zostać świadomie określone przed pierwszym docker run na produkcji:

  • Obraz: źródło, wersja, zasady aktualizacji (kto, kiedy i jak je wykonuje).
  • Porty: które muszą być wystawione na zewnątrz, które powinny pozostać wewnątrz sieci Dockerowej.
  • Dane: wolumeny, strategia backupu i odtwarzania, test przynajmniej jednego pełnego restore.
  • Konfiguracja: lista zmiennych środowiskowych, rozdzielenie konfiguracji środowiskowej od obrazu.
  • Logi: driver logów, rotacja, integracja z systemem logowania (ELK, Loki, syslog).
  • Sieć: nazwy sieci, reguły firewall, zależności między usługami.
  • Najczęściej zadawane pytania (FAQ)

    Po co używać Dockera, skoro mam już działające serwery i maszyny wirtualne?

    Docker rozwiązuje inny problem niż klasyczna wirtualizacja. Zamiast stawiać pełne systemy operacyjne, izoluje same procesy, opakowując aplikację wraz z jej zależnościami w obraz. Efekt: szybszy start (sekundy), mniejszy narzut zasobów i możliwość uruchomienia identycznego środowiska na laptopie dewelopera, serwerze testowym i produkcyjnym.

    Jeśli głównym bólem są różnice konfiguracji między maszynami, „magiczne” pakiety dogrywane ręcznie i sytuacje typu „u mnie działa”, konteneryzacja porządkuje środowisko na poziomie architektury, a nie pojedynczego serwera. Jeśli natomiast system jest stabilnym monolitem na jednej VM bez planu rozwoju, Docker często doda tylko dodatkową warstwę do utrzymania.

    Czym Docker różni się od maszyn wirtualnych w praktyce?

    Maszyna wirtualna emuluje cały sprzęt i uruchamia osobny system operacyjny. Kontener współdzieli jądro hosta i izoluje jedynie procesy (namespaces, cgroups). Dlatego kontener startuje w sekundy, a VM w dziesiątki sekund lub minuty; kontener zużywa tylko niewielki narzut ponad samą aplikację, a VM wymaga pełnego OS i hypervisora.

    Punkt kontrolny: jeśli potrzebujesz twardej granicy bezpieczeństwa, innego systemu operacyjnego niż host lub środowiska „piaskownicy” dla legacy – to domena VM. Jeśli kluczowa jest elastyczna praca z aplikacjami, szybkie podnoszenie środowisk i łatwe skalowanie, kontenery dają lepszy stosunek wysiłku do zysku.

    Kiedy Docker ma sens, a kiedy jest przerostem formy nad treścią?

    Docker dobrze sprawdza się tam, gdzie liczy się powtarzalność i automatyzacja: środowiska deweloperskie i testowe, mikrousługi, pipeline’y CI/CD, jednorazowe narzędzia uruchamiane „na chwilę” bez instalacji na hoście. W takich scenariuszach minimalizujesz ręczną konfigurację i zyskujesz spójne środowiska dla całego zespołu.

    Sygnał ostrzegawczy to m.in. małe, jednoużytkownikowe skrypty na jednym serwerze, stary monolit bez planu rozwoju, bardzo ciasne zasoby lub brak zespołu, który realnie utrzyma obrazy i rejestr. Jeśli system jest prosty, mało zmienny i nie wymaga skalowania, wprowadzenie Dockera może zwiększyć złożoność bez proporcjonalnych korzyści.

    Jakie są typowe zastosowania Dockera w projektach IT?

    Najczęstsze scenariusze to: szybkie podnoszenie środowisk deweloperskich (bazy, cache, kolejki, cały stos aplikacji z jednego pliku konfiguracyjnego), mikrousługi (każda usługa w swoim obrazie, niezależne wydania i zależności) oraz CI/CD (build i testy w zdefiniowanym obrazie, niezależne od stanu agenta). Dodatkowo Docker dobrze nadaje się do jednorazowego uruchamiania narzędzi administracyjnych, np. klienta bazy.

    Jeśli widzisz powtarzalny schemat: nowa osoba w zespole i dzień na ustawienie środowiska, różne wersje bibliotek na różnych maszynach, trudności z odtworzeniem błędu z produkcji – to sygnał, że konteneryzacja może znacząco uprościć codzienną pracę.

    Co to jest obraz Dockera, kontener i rejestr – i czym się różnią?

    Obraz to niezmienny szablon aplikacji: system plików z binariami, bibliotekami, konfiguracją startową i komendą, którą Docker uruchamia. Kontener to działająca instancja obrazu – proces z przypisanym systemem plików, siecią i zasobami; z jednego obrazu można uruchomić wiele kontenerów, każdy z własnym stanem.

    Rejestr jest magazynem obrazów (np. Docker Hub, GitHub Container Registry, prywatny Harbor). Do rejestru pushujesz obrazy z pipeline’u, a z innych hostów je pullujesz. Sygnał ostrzegawczy: w zespole nikt nie rozróżnia „obrazu” od „kontenera” i używa tych słów zamiennie – wtedy trudno precyzyjnie diagnozować błędy i śledzić, która wersja faktycznie działa na produkcji.

    Dlaczego tag latest w Dockerze bywa niebezpieczny na produkcji?

    Tag latest jest ruchem etykietą – w dowolnym momencie może wskazywać na inny zestaw warstw obrazu. Jeśli użyjesz latest w środowisku testowym czy produkcyjnym, tracisz możliwość jednoznacznego wskazania, jaka wersja aplikacji była uruchomiona podczas konkretnego incydentu. Odtworzenie błędu staje się loterią.

    Punkt kontrolny: dla stabilnych środowisk stosuj jednoznaczne tagi (np. numery wersji, daty buildów, shorthash commita). latest możesz używać co najwyżej lokalnie lub w piaskownicy, gdzie akceptujesz, że środowisko jest „ruchome” i nie musi być w pełni odtwarzalne.

    Jak ocenić, czy mój zespół jest gotowy, żeby wejść w Dockera?

    Podstawowa ocena powinna objąć kilka kryteriów: powtarzalność środowisk (czy da się je zdefiniować w jednym miejscu), częstotliwość zmian (im częstsze releasy, tym większy sens automatyzacji), skalę zespołu (im więcej osób dotyka systemu, tym więcej zysku ze standaryzacji) oraz wymagania bezpieczeństwa (czy masz procesy, które można odzwierciedlić w obrazach i skanach).

    Kluczowy punkt kontrolny to dostępna wiedza i czas na naukę. Jeśli zespół nie ma minimum kompetencji kontenerowych i nikt nie jest odpowiedzialny za obrazy, rejestr i aktualizacje, Docker szybko zamieni się w trudny do opanowania bałagan. Jeżeli jednak problemy kręcą się wokół „u mnie działa” i ręcznego ustawiania środowisk, inwestycja w Dockera zwykle się zwraca.

    Kluczowe Wnioski

  • Docker izoluje procesy zamiast całych systemów operacyjnych, co daje szybki start kontenerów, niższe zużycie zasobów i powtarzalne środowiska – jeśli głównym problemem są konflikty zależności i „u mnie działa”, to jest pierwszy kandydat do wdrożenia.
  • Kontenery i maszyny wirtualne pełnią różne role: kontenery zapewniają lekki poziom izolacji dla aplikacji i mikrousług, a VM są właściwym wyborem przy wymaganej twardej izolacji, innym systemie operacyjnym lub środowiskach legacy – sygnałem ostrzegawczym jest traktowanie Dockera jako zamiennika hypervisora.
  • Najbardziej opłacalne scenariusze użycia Dockera to środowiska deweloperskie i testowe, mikrousługi, pipeline’y CI/CD oraz narzędzia pomocnicze uruchamiane „na żądanie” – jeśli często stawiasz od zera bazy, cache czy kolejki, kontenery upraszczają ten proces do jednego pliku i komendy.
  • Docker jest przerostem formy nad treścią przy prostych, rzadko uruchamianych skryptach, stabilnych monolitach bez planów rozwoju, na serwerach z bardzo ograniczonymi zasobami oraz tam, gdzie brak osoby odpowiedzialnej za obrazy i rejestr – to typowe sygnały ostrzegawcze przed bezrefleksyjną konteneryzacją.
  • Decyzję o wejściu w Dockera należy oprzeć na punktach kontrolnych: stopniu powtarzalności środowisk, częstotliwości zmian aplikacji, wielkości i współpracy zespołu, wymaganiach bezpieczeństwa oraz realnie dostępnych kompetencjach w zespole.
  • Źródła informacji

  • Docker Overview. Docker, Inc. – Oficjalne wprowadzenie do Dockera, obrazy, kontenery, rejestry
  • Docker Engine Overview. Docker, Inc. – Architektura Dockera, obrazy, kontenery, warstwy systemu plików
  • Kubernetes Documentation – Containers. Cloud Native Computing Foundation – Opis kontenerów, izolacji procesów i zastosowań w chmurze
  • OCI Runtime Specification. Open Container Initiative – Standard uruchamiania kontenerów, definicje obrazu i kontenera
  • Control Groups v2 Documentation. The Linux Kernel Documentation Project – Opis cgroups, zarządzanie zasobami procesów i kontenerów
  • The Docker Book. James Turnbull (2014) – Praktyczne wprowadzenie do Dockera, obrazy, kontenery, workflow
  • Using Docker. O’Reilly Media (2015) – Zastosowania Dockera w dev, testach, CI/CD i produkcji
  • NIST Application Container Security Guide (SP 800-190). National Institute of Standards and Technology (2017) – Zalecenia bezpieczeństwa dla kontenerów i ich zastosowań
  • Microsoft Docs – Containers vs Virtual Machines. Microsoft – Porównanie kontenerów i maszyn wirtualnych, scenariusze użycia
  • Google Cloud – Introduction to containers, Kubernetes, and GKE. Google Cloud – Wprowadzenie do kontenerów, obrazy, rejestry, typowe zastosowania