Serwery

Docker Compose środowisko: jak zbudować lokalne dev/test

autor:

Docker Compose poradnik dla programistów i administratorów: Stwórz swój pierwszy projekt wielokontenerowy.
W tym Docker Compose poradniku dowiesz się, jak skonfigurować i uruchomić złożone aplikacje.

Klonujesz repozytorium, wpisujesz docker compose up i dostajesz sieć app1_default, choć w dokumentacji projekt nazywał się inaczej. Winna jest nazwa katalogu. Ten sam plik docker-compose.yml na laptopie i na serwerze CI potrafi wygenerować dwa zupełnie inne środowiska – z innymi sieciami, wolumenami i zmiennymi z .env.

Do tego dorzuć bazę danych, backend i plik override.yml. Każdy zespół inaczej rozumie, gdzie kończy się kontener, a zaczyna całe środowisko docker Compose. Bez jasnych zasad nazewnictwa i separacji plików .env taki projekt szybko staje się nieprzewidywalny na produkcji. Ten tekst rozkłada je na czynniki pierwsze – od budowy usług i sieci, przez zmienne .env, po różnice dev/produkcja w override.yml i zarządzanie cyklem życia projektu.

TL;DR: Środowisko w docker Compose to cały projekt zdefiniowany w jednym pliku – usługi, sieci i wolumeny – nie pojedynczy kontener. Nazwa projektu i sieci pochodzi z katalogu, o ile nie nadpiszesz jej przez COMPOSE_PROJECT_NAME. Usługi łączą się przez wspólną sieć bridge po nazwie serwisu jako DNS, a docker-compose.override.yml pozwala trzymać jedną definicję dla dev i produkcji bez duplikacji plików.

Co to jest środowisko w docker Compose i czym różni się od kontenera?

Środowisko w docker Compose to logiczna jednostka złożona z usług, sieci i wolumenów opisanych w jednym pliku konfiguracyjnym, natomiast kontener jest tylko jedną z uruchomionych instancji obrazu działających wewnątrz tego projektu (CPModa.pl). Ta różnica w poziomie abstrakcji decyduje o tym, jak zarządzasz cyklem życia aplikacji wielokontenerowej.

Środowisko jako zestaw usług i konfiguracji

Projekt Compose grupuje wszystkie zdefiniowane usługi pod wspólną nazwą i automatycznie tworzy dla nich jedną sieć logiczną o nazwie default, jeśli nie zdefiniujesz własnych sieci w pliku (Docker Official Docs). Dzięki temu każda usługa – baza danych, backend, frontend – widzi pozostałe kontenery po nazwie usługi, bez ręcznego łączenia sieci docker. To właśnie ta wspólna sieć, wolumeny i zmienne środowiskowe razem tworzą to, co nazywamy środowiskiem – nie sam plik YAML, ale wynikający z niego zestaw działających zasobów.

Różnica między kontenerem a środowiskiem

Kontener istnieje tylko tak długo, jak trwa proces w nim uruchomiony, i można go usunąć bez wpływu na resztę projektu. Środowisko przetrwa restart pojedynczego kontenera, ponieważ sieć i wolumeny są przypisane do projektu, nie do konkretnej instancji. Pełna nazwa sieci projektu powstaje według wzorca <COMPOSE_PROJECT_NAME>_<network_name>, a przy domyślnej konfiguracji bez własnych sieci przyjmuje postać <COMPOSE_PROJECT_NAME>_default. Gdy zmienna COMPOSE_PROJECT_NAME nie jest ustawiona, Compose sięga po nazwę katalogu projektu, więc sieć otrzymuje formę <nazwa_katalogu>_default (Stack Overflow). To pokazuje, że tożsamość środowiska jest ściśle związana z nazwą projektu, nie z pojedynczym kontenerem.

Poziom Zakres Trwałość / nazewnictwo
Środowisko (projekt) usługi, sieci, wolumeny sieć default, wzorzec <projekt>_default
Kontener jedna instancja obrazu dziedziczy sieć i nazwę projektu

Docker Compose a inne technologie konteneryzacji

Compose operuje na poziomie orkiestracji kontenerów aplikacyjnych – definiuje relacje między usługami, ale nie zarządza samym hostem ani izolacją systemową. To inny model niż lżejsza wirtualizacja systemowa, w której pojedyncza jednostka bliżej przypomina osobny system operacyjny; różnice te dobrze widać przy konteneryzacji LXC w Proxmox, gdzie kontener działa bardziej autonomicznie niż usługa w projekcie Compose. Dla programisty budującego środowisko deweloperskie oznacza to prostszy model mentalny: jeden plik opisuje cały stos aplikacji, a silnik Compose dba o sieć i zależności między usługami, zamiast konfigurować każdy kontener osobno.

Jak zbudować środowisko deweloperskie w docker Compose krok po kroku?

Budowa środowiska deweloperskiego w docker Compose to cztery etapy: instalacja silnika kontenerów, ułożenie struktury katalogów projektu, zdefiniowanie usług w pliku docker-compose.yml oraz pierwsze uruchomienie stosu z weryfikacją stanu kontenerów. Każdy z tych etapów wpływa na to, jak Compose nazywa sieć, wolumeny i kontenery w ramach jednego projektu.

Wymagania docker Engine i docker Compose

Docker Compose działa jako wtyczka docker Engine, dlatego bez działającego demona dockerd, polecenie docker compose nie wystartuje żadnej usługi. Zanim zaczniesz definiować usługi, zajrzyj do przewodnika, jak zacząć z Dockerem, żeby poprawnie zainstalować silnik i CLI Compose na swoim systemie. Na stacji roboczej wystarczy jedna instalacja silnika, żeby obsłużyć wiele niezależnych projektów równolegle.

Struktura katalogów projektu i plik yml

Każdy projekt Compose trzymasz w osobnym katalogu z plikiem docker-compose.yml w katalogu głównym – to nie tylko porządek, bo nazwa katalogu staje się domyślną nazwą projektu i trafia do nazw sieci, wolumenów i kontenerów. Compose bierze nazwę katalogu jako identyfikator projektu, jeśli nie nadpiszesz go flagą –project-name lub zmienną COMPOSE_PROJECT_NAME – dwa projekty o identycznej nazwie katalogu na jednej maszynie zaczną kolidować.

Definiowanie usług, obrazów i portów

W pliku yml każda usługa dostaje własny blok z kluczami image (lub build), ports i volumes – to minimalny zestaw, który pozwala Compose wystartować kontener i podłączyć go do reszty stosu. Mapowanie portów w formacie host:kontener decyduje, pod którym adresem lokalnym zobaczysz aplikację w przeglądarce, a sekcja volumes odpowiada za trwałość danych między restartami. Nazwy usług w pliku stają się jednocześnie nazwami hostów w wewnętrznej sieci Compose, więc backend odwołuje się do bazy po nazwie usługi, nie po adresie IP. Dla środowiska deweloperskiego warto od razu zamontować katalog z kodem jako wolumin, żeby zmiany w plikach były widoczne w kontenerze bez przebudowy obrazu.

Pierwsze uruchomienie i sprawdzenie stanu

Polecenie docker compose up -d tworzy sieć projektu, wolumeny i kontenery w jednym przebiegu, a docker compose ps pokazuje, które usługi wystartowały poprawnie. Dla katalogu o nazwie app1, bez ustawionej zmiennej COMPOSE_PROJECT_NAME, Compose nazwie domyślną sieć app1_default i podłączy do niej wszystkie kontenery projektu – świadomość tej konwencji ułatwia debugowanie problemów z siecią między usługami.

Jak skonfigurować zmienne środowiskowe i plik .env w docker Compose?

Docker Compose automatycznie podstawia zmienne środowiskowe z pliku .env dla zapisu ${ZMIENNA} w docker-compose.yml, dzięki czemu porty, hasła czy nazwy baz danych nie są zaszyte na trwałe w kodzie usługi. To rozwiązuje problem przenoszenia jednego pliku compose między maszyną deweloperską, serwerem CI i produkcją bez ręcznej edycji.

Plik .env i jego domyślna lokalizacja

Compose szuka pliku .env w tym samym katalogu, z którego wywołujesz polecenie Compose – najczęściej tam, gdzie leży docker-compose.yml. Wpisy mają format KLUCZ=wartość bez cytowania i bez spacji wokół znaku równości.

Sekcja environment w docker-compose.yml

Sekcja environment ustawia zmienne widoczne wewnątrz kontenera, niezależnie od tego, czy pochodzą z .env, shella czy są wpisane na sztywno. Alternatywą jest env_file, który wskazuje osobny plik z listą zmiennych ładowanych do środowiska usługi bez ich wpisywania linia po linii.

Zmienne środowiskowe dla różnych etapów

Dla środowiska deweloperskiego i testowego rozdziel wartości na osobne pliki, np. .env.dev i .env.test, i wskazuj je flagą --env-file przy starcie. Taki podział eliminuje ryzyko, że dane testowe trafią do konfiguracji zbliżonej do produkcyjnej.

Jak połączyć bazę danych, backend i frontend w jednym środowisku?

Trzy usługi łączy jedna sieć Compose, w której nazwa serwisu działa jak adres DNS. Backend łączy się z bazą przez nazwę usługi, a frontend z backendem tak samo, bez sztywnych adresów IP.

Sieć docker Compose i komunikacja usług

Domyślna sieć bridge nadaje każdej usłudze alias zgodny z jej nazwą w pliku yml. Backend łączy się z bazą danych pod nazwą serwisu, np. db, bez mapowania portów na hosta.

Wolumeny i trwałość danych bazy

Wolumen nazwany przechowuje dane bazy danych poza cyklem życia kontenera, więc restart usługi nie usuwa tabeli. Ścieżki montowania warto planować zgodnie z konwencją katalogów opisaną w poradniku o strukturze katalogów Linux.

Healthcheck i depends_on w praktyce

Depends_on ustala jedynie kolejność startu, a nie gotowość usługi. Healthcheck sprawdza faktyczny stan bazy danych, a backend startuje po pozytywnym wyniku, co ogranicza błędy połączenia przy pierwszym uruchomieniu.

Czym różni się środowisko deweloperskie od produkcyjnego w override.yml?

Plik docker-compose.override.yml pozwala trzymać dwa profile tej samej aplikacji bez duplikowania definicji serwisów. Środowisko deweloperskie dostaje bind mounty, otwarte porty i pełne logi, a środowisko produkcyjne zostaje przy ograniczonym dostępie sieciowym i wolumenach zarządzanych przez Dockera. Różnica nie leży w liczbie kontenerów, tylko w tym, co każdy z plików nadpisuje.

Rola pliku docker-compose.override.yml

Komenda docker compose up najpierw odczytuje docker-compose.yml, a następnie doczytuje docker-compose.override.yml i scala oba pliki, przy czym override działa jako warstwa nadpisująca ustawienia bazowe (Uprogramisty.pl). Ta sama usługa bazy danych może w override mieć zamontowany katalog z kodem lub dodatkowy port, a w bazowym pliku produkcyjnym pozostać bez tych wpisów. Mechanizm ten eliminuje potrzebę trzymania dwóch kompletnych definicji serwisów – baza w docker-compose.yml pozostaje wspólna dla obu środowisk, a override dokleja tylko to, czego wymaga lokalna praca.

Mapowanie portów w dev i w produkcji

W środowisku deweloperskim override zwykle publikuje port bazy danych i cache na hosta, żeby klient SQL albo GUI cache mógł się podłączyć bez wchodzenia do kontenera. Środowisko produkcyjne chowa te porty za wewnętrzną siecią compose i wystawia na zewnątrz tylko port aplikacji web, najczęściej za reverse proxy. Bazowy plik nie zawiera żadnego mapowania portu bazy danych – dodaje je wyłącznie override, co realnie ogranicza powierzchnię ataku na produkcji.

Wolumeny i zmienne środowiskowe w dev vs prod

Poniższa tabela zestawia trzy obszary, które override zmienia najczęściej dla typowej aplikacji z backendem, frontendem i bazą danych.

Aspekt konfiguracji Środowisko deweloperskie (override) Środowisko produkcyjne (baza)
Porty Publikowane na hosta, dostęp z lokalnego klienta Zamknięte w sieci wewnętrznej compose
Wolumeny Bind mount kodu z hosta, wsparcie dla hot-reload Wolumeny nazwane, zarządzane przez Dockera
Zmienne środowiskowe Debug włączony, dane testowe, uproszczone hasła Debug wyłączony, sekrety z menedżera poświadczeń

Taki podział pozwala jednym poleceniem uruchamiać wersję deweloperską lokalnie, a na serwerze produkcyjnym startować z samego pliku bazowego – bez ryzyka, że bind mount z laptopa albo testowe hasło trafi na produkcję.

Docker compose czy docker-compose? Zarządzanie cyklem życia środowiska

Docker Compose V1 (docker-compose) i V2 (wtyczka docker compose) różnią się instalacją i wywołaniem, nie w pliku docker-compose.yml. V2 działa jako subkomenda docker CLI, V1 jako osobny skrypt Python. Dla cyklu życia środowiska liczy się kolejność poleceń: up, down, logs, restart.

Wersja Sposób wywołania Instalacja Status
docker-compose (V1) samodzielna komenda osobny skrypt Python wycofywany
docker compose (V2) subkomenda docker CLI wtyczka wbudowana w docker Engine domyślny

Różnice między docker-compose i docker compose

Starsze pipeline’y CI/CD wciąż wołają docker-compose, nowsze obrazy bazowe mają wtyczkę compose już wbudowaną w docker Engine.

Polecenia up, down, logs i restart

up buduje i uruchamia kontenery zdefiniowane w docker-compose.yml, down zatrzymuje je i usuwa sieci, logs pokazuje strumień wyjścia wybranego serwisu, a restart wymusza ponowne uruchomienie bez przebudowy obrazu.

Zatrzymanie i usunięcie środowiska

stop zatrzymuje kontenery, zachowując wolumeny do ponownego startu, natomiast down --volumes usuwa też dane persystentne – rozróżnienie chroni przed utratą bazy danych w środowisku testowym.

Najczęściej zadawane pytania o środowisko docker Compose

Czy na jednym hoście można trzymać kilka niezależnych środowisk Compose?

Tak, pod warunkiem że każde środowisko dostanie własną nazwę projektu (flaga -p lub zmienna COMPOSE_PROJECT_NAME). Compose prefiksuje nią nazwy sieci, wolumenów i kontenerów, dzięki czemu dwa katalogi z tym samym plikiem docker-compose.yml nie nadpiszą sobie zasobów.

Co dzieje się z danymi po komendzie down?

Sama komenda docker compose down usuwa kontenery, sieci i domyślne wolumeny anonimowe, ale wolumeny nazwane przypisane do bazy danych zostają na dysku. Żeby wyczyścić środowisko całkowicie, trzeba dodać flagę -v oraz --remove-orphans.

Czy plik .env powinien trafić do repozytorium?

Nie, plik .env zawiera zwykle sekrety i wartości lokalne, więc powinien lądować w .gitignore. Do repozytorium wchodzi jego szablon, na przykład .env.example, z pustymi lub przykładowymi wartościami zmiennych.

Jak zresetować środowisko deweloperskie do czystego stanu?

Najpewniejsza sekwencja to docker compose down -v --remove-orphans, a potem docker compose up --build. Taki reset wymusza przebudowanie obrazów i usuwa dane testowe, które mogłyby zafałszować kolejny przebieg testów.

Czy plik override.yml jest obowiązkowy?

Nie, Compose scala automatycznie docker-compose.yml z docker-compose.override.yml tylko wtedy, gdy ten drugi plik istnieje w katalogu projektu. Bez niego uruchamiana jest wyłącznie konfiguracja bazowa, co bywa wystarczające dla prostych środowisk testowych.