Пошук уроків, статей та іншого контенту
Налаштуйте обмін даними між контейнерами через імена сервісів і внутрішні порти Docker-мережі.
Контейнери Docker ізольовані, але можуть обмінюватися даними через спільну Docker-мережу.
У Docker Compose всі сервіси за замовчуванням підключаються до спільної мережі. Кожен сервіс можна знайти за його назвою.
Наприклад, якщо в compose.yaml є сервіс із назвою backend, інший контейнер може звернутися до нього за адресою:
http://backend:8000Тут:
backend — ім’я сервісу, яке працює як ім’я хоста;
8000 — внутрішній порт, на якому застосунок слухає запити;
http:// — протокол взаємодії.
Для зв’язку між контейнерами не потрібно використовувати
localhostlocalhostВажливо розрізняти два типи портів:
внутрішній порт — порт, доступний іншим контейнерам у Docker-мережі;
зовнішній порт — порт, через який до контейнера звертається хостова система.
Якщо сервіс слухає порт 8000 усередині контейнера, інший контейнер звертається до нього так:
http://backend:8000Публікувати цей порт на комп’ютер за допомогою ports для взаємодії контейнерів не потрібно.
Наприклад:
ports:
- "9000:8000"означає:
порт 9000 на хостовій системі;
порт 8000 усередині контейнера.
Тоді:
з хостової системи можна звернутися до http://localhost:9000;
з іншого контейнера потрібно звертатися до http://backend:8000.
Створимо два сервіси:
backend — HTTP-сервер, який повертає JSON;
client — контейнер, який надсилає запит до backend.
Структура проєкту:
docker-example/
├── compose.yaml
└── backend/
└── app.pyСтворіть файл backend/app.py:
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != "/message":
self.send_response(404)
self.end_headers()
return
response = {
"message": "Дані отримано від backend",
"service": "backend"
}
body = json.dumps(response).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
server = HTTPServer(("0.0.0.0", 8000), Handler)
server.serve_forever()Адреса 0.0.0.0 важлива: вона дозволяє серверу приймати запити з інших контейнерів. Якщо сервер слухатиме лише 127.0.0.1, він буде доступний тільки всередині власного контейнера.
Створіть файл compose.yaml:
services:
backend:
image: python:3.12-alpine
working_dir: /app
volumes:
- ./backend:/app:ro
command: python app.py
expose:
- "8000"
client:
image: busybox:1.36
depends_on:
- backend
command: >
sh -c '
until wget -qO- http://backend:8000/message;
do
sleep 1;
done
'У цьому файлі:
сервіс називається backend;
сервер усередині контейнера використовує порт 8000;
expose описує внутрішній порт сервісу, але не відкриває його на хостовій системі;
сервіс client підключений до тієї самої мережі Compose;
http://backend:8000/message використовує ім’я сервісу backend;
цикл із until повторює запит, якщо сервер ще не встиг запуститися.
Запустіть проєкт:
docker compose upУ виведенні з’явиться JSON:
{"message": "Дані отримано від backend", "service": "backend"}Це означає, що контейнер client успішно звернувся до контейнера backend через його ім’я.
Зупинити та видалити контейнери можна командою:
docker compose downDocker Compose створює внутрішню мережу для проєкту. У цій мережі імена сервісів використовуються як DNS-імена.
У нашому прикладі:
backendавтоматично перетворюється на IP-адресу контейнера сервісу backend.
Тому цей запит працює:
http://backend:8000/messageА цей запит зазвичай неправильний:
http://localhost:8000/messageУ контейнері client адреса localhost вказує на сам контейнер client. Сервіс backend має окрему адресу, і для нього потрібно використовувати ім’я backend.
Якщо потрібно явно вказати мережу, її можна описати в compose.yaml:
services:
backend:
image: python:3.12-alpine
working_dir: /app
volumes:
- ./backend:/app:ro
command: python app.py
networks:
- app-network
client:
image: busybox:1.36
depends_on:
- backend
command: >
sh -c '
until wget -qO- http://backend:8000/message;
do
sleep 1;
done
'
networks:
- app-network
networks:
app-network:Обидва сервіси підключені до app-network, тому вони можуть взаємодіяти між собою за іменами сервісів.
Якщо два контейнери не мають спільної мережі, вони не зможуть звертатися один до одного за іменами.
expose і portsДиректива expose:
expose:
- "8000"показує, який внутрішній порт використовує сервіс. Вона не створює доступ із хостової системи.
Директива ports:
ports:
- "9000:8000"публікує порт контейнера на хостовій системі.
Для взаємодії контейнерів достатньо, щоб:
застосунок слухав правильний внутрішній порт;
контейнери були в одній мережі;
запит використовував ім’я сервісу та внутрішній порт.
Адреса сервісу всередині Docker-мережі зазвичай має такий вигляд:
http://ІМ'Я_СЕРВІСУ:ВНУТРІШНІЙ_ПОРТ/ШЛЯХДля нашого прикладу:
http://backend:8000/messageЯкщо в Compose сервіс називається database, звернення до нього також починатиметься з цього імені:
database:5432Порт у такій адресі має бути портом, який використовує сервіс усередині Docker-мережі, а не обов’язково портом хостової системи.
localhostНеправильно:
http://localhost:8000із контейнера client, якщо сервер працює в контейнері backend.
Правильно:
http://backend:8000Якщо вказано:
ports:
- "9000:8000"з іншого контейнера все одно потрібно використовувати:
http://backend:8000Порт 9000 призначений для доступу з хостової системи.
Ім’я в адресі має відповідати назві сервісу в compose.yaml:
services:
backend:Отже, використовуємо backend, а не назву образу чи довільне ім’я контейнера.
127.0.0.1Сервер, який слухає 127.0.0.1, може бути недоступним для інших контейнерів.
Для серверних застосунків у контейнерах зазвичай потрібно слухати:
0.0.0.0depends_on визначає порядок запуску контейнерів, але не гарантує, що застосунок усередині залежного контейнера вже готовий приймати запити.
Тому клієнту може знадобитися повторювати запит, як у прикладі з циклом until.
Контейнери в одній Docker-мережі можуть взаємодіяти між собою.
У Docker Compose сервіс доступний за своїм ім’ям.
Для звернення до іншого контейнера використовуйте ім’я_сервісу:внутрішній_порт.
localhost усередині контейнера вказує на цей самий контейнер.
ports потрібен для доступу з хостової системи, але не для зв’язку між контейнерами.
Застосунок у контейнері має слухати адресу 0.0.0.0, щоб приймати запити з інших контейнерів.