Пошук уроків, статей та іншого контенту
Створите ізольовані мережі Compose та налаштуєте взаємодію контейнерів за іменами сервісів.
Docker Compose створює окрему мережу для проєкту автоматично. Якщо в compose.yaml не вказати власні мережі, усі сервіси підключаються до спільної мережі за замовчуванням.
У такій мережі сервіси можуть звертатися один до одного за іменами сервісів:
http://api:8080Тут api — це ім’я сервісу в compose.yaml, а не ім’я контейнера та не localhost.
Кожен сервіс отримує DNS-ім’я всередині Docker-мережі. Docker Compose автоматично перетворює ім’я сервісу на IP-адресу відповідного контейнера.
Власні мережі оголошують у секції networks:
services:
api:
image: python:3.12-alpine
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend
networks:
frontend:
backend:
internal: trueУ цьому прикладі:
api підключений до мереж frontend і backend;
db підключений лише до backend;
api може звертатися до db;
сервіси, підключені лише до frontend, не бачать db;
internal: true забороняє контейнерам із цієї мережі напряму виходити назовні через зовнішній інтерфейс Docker.
Схема з двома мережами часто використовується для відокремлення публічних і внутрішніх сервісів:
client ───── frontend ───── api ───── backend ───── dbСтворимо проєкт із чотирма сервісами:
client — тимчасовий клієнт для перевірки з’єднання;
api — HTTP-сервіс;
db — PostgreSQL;
frontend — мережа для доступних сервісів;
backend — внутрішня мережа для api і db.
Структура файлів:
.
├── compose.yaml
└── public
└── index.htmlФайл public/index.html:
<!doctype html>
<html lang="uk">
<head>
<meta charset="utf-8">
<title>Docker Compose</title>
</head>
<body>
<h1>Відповідь від API</h1>
</body>
</html>Файл compose.yaml:
services:
client:
image: alpine:3.20
command: ["sh", "-c", "sleep infinity"]
networks:
- frontend
api:
image: python:3.12-alpine
command: ["python", "-m", "http.server", "8080", "--directory", "/srv"]
volumes:
- ./public:/srv:ro
networks:
- frontend
- backend
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
volumes:
postgres_data:
networks:
frontend:
backend:
internal: trueЗапустіть сервіси:
docker compose up -dПеревірте стан контейнерів:
docker compose psСервіс client підключений до мережі frontend, тому він може звертатися до api за іменем сервісу:
docker compose exec client wget -qO- http://api:8080У відповіді буде HTML-код із файлу index.html.
Порт 8080 не потрібно публікувати через ports. Сервіси в одній Docker-мережі спілкуються безпосередньо через внутрішній порт контейнера.
Спробуємо звернутися з client до db:
docker compose exec client wget -O- http://db:5432Команда завершиться помилкою на кшталт:
wget: bad address 'db:5432'Причина в тому, що client підключений лише до frontend, а db — лише до backend. DNS Docker не публікує ім’я db у мережах, до яких цей сервіс не підключений.
Натомість api підключений до обох мереж. Він може розв’язати ім’я db:
docker compose exec api python -c \
'import socket; print(socket.gethostbyname("db"))'Команда виведе внутрішню IP-адресу контейнера PostgreSQL.
Отже, мережі визначають не лише спосіб з’єднання, а й те, які сервіси взагалі можуть бачити один одного.
localhostУсередині контейнера localhost означає цей самий контейнер.
Наприклад, якщо api підключається до PostgreSQL, неправильним буде такий хост:
localhostlocalhost вказує на контейнер api, а не на контейнер db.
Правильним буде ім’я сервісу:
dbПриклад рядка підключення:
postgresql://app:secret@db:5432/appПорт 5432 — це порт PostgreSQL усередині мережі Docker. Він не потребує публікації на хост, якщо до бази звертаються лише інші контейнери.
Розглянемо різницю між expose і ports.
services:
api:
image: python:3.12-alpine
command: ["python", "-m", "http.server", "8080"]
expose:
- "8080"
networks:
- frontend
db:
image: postgres:16-alpine
networks:
- backendexpose документує внутрішній порт для взаємодії між контейнерами. Він не робить сервіс доступним із хостової системи.
Щоб звертатися до сервісу з браузера або термінала на хості, використовують ports:
services:
api:
image: python:3.12-alpine
command: ["python", "-m", "http.server", "8080"]
ports:
- "8080:8080"Тепер:
інші контейнери звертаються до http://api:8080;
хостова система звертається до http://localhost:8080.
Для внутрішньої бази даних зазвичай не потрібно додавати ports.
Сервіс може бути учасником кількох мереж:
services:
api:
image: python:3.12-alpine
networks:
- frontend
- backend
networks:
frontend:
backend:Це дає змогу api приймати запити від сервісів у frontend і водночас звертатися до внутрішніх сервісів у backend.
Такий сервіс фактично стає посередником між мережами. Тому до нього варто підключати лише ті мережі, які справді потрібні.
docker compose up запускає контейнери, але це не означає, що застосунок усередині кожного контейнера вже готовий приймати з’єднання.
Наприклад:
контейнер PostgreSQL може ще ініціалізувати базу;
API може ще запускати міграції;
HTTP-сервер може ще не почати слухати порт.
Мережева доступність і готовність застосунку — різні речі. Ім’я сервісу може вже розв’язуватися, але з’єднання все одно тимчасово не встановлюватиметься.
localhost для іншого сервісуНеправильно:
http://localhost:8080із контейнера client, якщо HTTP-сервер працює в api.
Правильно:
http://api:8080У конфігураціях застосунків потрібно використовувати стабільне ім’я сервісу:
dbа не згенероване ім’я контейнера на кшталт:
project-db-1Ім’я контейнера може залежати від назви проєкту та змінюватися.
Не потрібно публікувати порт бази даних лише для того, щоб api міг до неї звернутися.
Надлишкова конфігурація:
db:
image: postgres:16-alpine
ports:
- "5432:5432"Якщо PostgreSQL потрібен тільки іншим контейнерам, достатньо підключити сервіси до спільної внутрішньої мережі.
Таке рішення скасовує ізоляцію:
services:
client:
networks:
- frontend
- backend
db:
networks:
- frontend
- backendПідключайте сервіс лише до тих мереж, які потрібні для його роботи.
Якщо сервіс називається api, звернення до backend не спрацює, якщо для нього не оголошено відповідний мережевий псевдонім.
Перевірити фактичну конфігурацію Compose можна командою:
docker compose configЩоб зупинити та видалити контейнери й мережі:
docker compose downЩоб також видалити том із даними PostgreSQL:
docker compose down -vКоманду down -v використовуйте обережно: вона видалить дані з оголошеного тому.
Compose автоматично створює мережу для сервісів, але для ізоляції краще оголошувати власні мережі.
Сервіси в одній мережі знаходять один одного за іменами сервісів.
localhost усередині контейнера посилається на цей самий контейнер.
Сервіс може бути підключений до кількох мереж.
internal: true створює внутрішню мережу без прямого зовнішнього доступу.
ports потрібен для доступу з хоста, але не для взаємодії контейнерів у спільній мережі.
Розділення на frontend і backend дає змогу обмежити, які сервіси можуть бачити бази даних та інші внутрішні компоненти.