Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Docker Compose - multi-service networking i wolumeny

Twoja aplikacja to już nie jeden proces. Jest API, jest MongoDB, jest Redis - i żeby uruchomić całość lokalnie, trzeba wywołać trzy razy

docker run
z długą listą flag, w odpowiedniej kolejności, pamiętając o sieci i o tym, że baza potrzebuje chwili na start. Przy każdym nowym członku zespołu ta instrukcja jest przekazywana ustnie i za każdym razem trochę inaczej.

Legion nie wymaszerowywał na podstawie ustnych ustaleń. Miał rozkaz dzienny: spisaną listę oddziałów, ich kolejność i to, co każdy ze sobą zabiera. Docker Compose jest takim rozkazem - służy do zarządzania wieloma kontenerami jednocześnie.

Nie myl go z sąsiednimi narzędziami: budowaniem obrazów z kodu źródłowego zajmuje się

docker build
i Dockerfile, testowaniem - Twój pipeline, a monitorowaniem zużycia zasobów - narzędzia, które poznałeś przy obserwowalności.

Rozkaz dzienny

Cała konfiguracja mieści się w jednym pliku

docker-compose.yml
:

1services:
2  mongodb:
3    image: mongo:7
4    volumes:
5      - mongo-data:/data/db
6    healthcheck:
7      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
8      interval: 10s
9      retries: 5
10
11  redis:
12    image: redis:7-alpine
13
14  api:
15    build: .
16    ports:
17      - "3000:3000"
18    environment:
19      DATABASE_URL: mongodb://mongodb:27017/legions
20      REDIS_HOST: redis
21    depends_on:
22      mongodb:
23        condition: service_healthy
24      redis:
25        condition: service_started
26
27volumes:
28  mongo-data:

Każdy wpis pod

services
to jeden kontener.
image
bierze gotowy obraz,
build: .
każe zbudować go z Dockerfile'a w tym katalogu.

Kolejność startu

Pole

depends_on
zapewnia kolejność uruchamiania serwisów. Nasze trzy serwisy wstają więc tak: najpierw MongoDB, potem Redis, na końcu API, które czeka na oba.

Ale tu jest pułapka, o którą rozbija się większość pierwszych konfiguracji: samo

depends_on
czeka tylko na uruchomienie kontenera, nie na jego gotowość. MongoDB potrzebuje kilku sekund na wczytanie danych - kontener już działa, a baza jeszcze nie przyjmuje połączeń. API startuje, próbuje się połączyć i pada.

Dlatego przy bazie stoi

healthcheck
, a w
depends_on
warunek
condition: service_healthy
. Teraz Compose czeka, aż polecenie z
healthcheck
zacznie kończyć się powodzeniem. Redis wstaje szybko i wystarcza mu
service_started
- domyślne zachowanie, czyli „kontener ruszył".

Zapamiętaj tę różnicę, @name: uruchomiony to nie to samo co gotowy. Zdecydowana większość błędów „connection refused" przy starcie środowiska bierze się właśnie stąd.

Sieć bez konfiguracji

Zwróć uwagę na adres bazy:

mongodb://mongodb:27017/legions
. Nie ma tam
localhost
ani żadnego adresu IP - jest nazwa serwisu.

Compose tworzy dla wszystkich serwisów wspólną sieć i rejestruje w niej każdy pod jego nazwą. Kontener

api
łączy się z bazą, pisząc po prostu
mongodb
- reszta dzieje się sama. Nie musisz znać adresów IP ani niczego konfigurować.

Zauważ też, czego nie ma w naszym pliku: MongoDB i Redis nie mają sekcji

ports
. To celowe. Bez niej usługa jest widoczna tylko wewnątrz sieci Compose - API do niej dosięgnie, ale nikt z zewnątrz już nie. Port wystawiamy wyłącznie tam, gdzie naprawdę trzeba, czyli przy API.

Wolumeny - dane, które przeżyją kontener

Kontener jest z założenia nietrwały: usuwasz go i znika wszystko, co zapisał. Dla bazy danych to katastrofa, więc wolumeny służą do przechowywania danych trwałych poza kontenerem.

Wpis

mongo-data:/data/db
mówi: to, co MongoDB zapisuje w katalogu
/data/db
, trzymaj w wolumenie o nazwie
mongo-data
, żyjącym niezależnie od kontenera. Usunięcie i odtworzenie kontenera nie tknie tych danych.

Wolumeny nie ograniczają zużycia procesora, nie szyfrują komunikacji i nie skalują niczego - przechowują dane, i tyle.

Uruchomienie

Cały rozkaz wykonuje jedno polecenie:

1docker compose up --build -d

Trzy człony po

up
warto rozdzielić.
--build
przebudowuje obrazy przed startem - bez tego Compose użyje poprzednio zbudowanego, choćbyś zmienił kod.
-d
(od detached) puszcza wszystko w tle i oddaje Ci terminal.

Zatrzymanie ma dwa warianty i różnica między nimi bywa kosztowna.

docker compose down
usuwa kontenery i sieć, zostawiając wolumeny.
docker compose down -v
usuwa także wolumeny - czyli dane bazy. Pierwsze robisz codziennie, drugie wtedy, gdy naprawdę chcesz zacząć od pustej bazy.

Podsumowanie

Rozkaz dzienny spisany, oddziały wyruszają w kolejności:

  • Docker Compose służy do zarządzania wieloma kontenerami jednocześnie - nie buduje obrazów z kodu, nie testuje i nie monitoruje zużycia,
  • konfiguracja mieszka w
    docker-compose.yml
    , a każdy wpis pod
    services
    to jeden kontener,
  • depends_on
    zapewnia kolejność uruchamiania serwisów
    ,
  • uruchomiony to nie gotowy -
    depends_on
    czeka na start kontenera, a nie na gotowość usługi,
  • healthcheck
    plus
    condition: service_healthy
    każe czekać na faktyczną gotowość;
    service_started
    wystarcza dla szybkich usług,
  • kontenery łączą się po nazwie serwisu -
    mongodb://mongodb:27017
    , bez adresów IP i bez konfiguracji sieci,
  • brak sekcji
    ports
    znaczy, że usługa jest widoczna tylko wewnątrz sieci Compose,
  • wolumeny przechowują dane trwałe poza kontenerem - nie ograniczają CPU, nie szyfrują i nie skalują,
  • uruchomienie:
    docker compose up --build -d
    , gdzie
    --build
    przebudowuje obrazy, a
    -d
    puszcza w tle,
  • down
    zostawia wolumeny,
    down -v
    usuwa je razem z danymi
    .

W następnej lekcji zajmiemy się sekretami - hasłami i kluczami, które w tym pliku wpisaliśmy jeszcze wprost. A na razie zapamiętaj: Compose to rozkaz dzienny - jeden plik zastępuje ustną instrukcję, którą każdy zapamiętał inaczej.

Przejdź do CodeWorlds