Уявіть типовий веб-застосунок: фронтенд на React, бекенд на .NET, база даних PostgreSQL, Redis для кешування. У традиційному deployment кожен компонент знає IP-адресу іншого — фронтенд звертається до http://192.168.1.10:5000 для API, бекенд підключається до бази за адресою 192.168.1.20:5432. Все працює, поки ви не вирішите перенести один з компонентів на інший сервер — доведеться змінювати конфігурацію скрізь.
Тепер уявіть той самий застосунок у Docker. Кожен компонент — окремий контейнер. Контейнери ізольовані один від одного: вони мають власні файлові системи, процеси, і... власні мережеві простори (network namespaces). За замовчуванням контейнер не може "побачити" інший контейнер — вони існують у різних мережевих ізоляціях, наче на різних планетах.
Як же організувати комунікацію? Як фронтенд-контейнер має знайти бекенд-контейнер? Як бекенд підключиться до PostgreSQL-контейнера? Чи потрібно знати IP-адреси, які Docker динамічно призначає? Чи можна використовувати DNS-імена замість IP? Як забезпечити, щоб контейнери бачили один одного, але залишалися ізольованими від зовнішнього світу?
Саме для вирішення цих питань Docker надає мережеву підсистему (networking subsystem) — гнучку систему віртуальних мереж, що дозволяє контейнерам спілкуватися між собою, з хост-машиною та зовнішнім світом. У цій статті ми детально розглянемо архітектуру Docker networking, типи мереж (bridge, host, overlay, macvlan), механізм DNS-резолюції імен контейнерів, та побудуємо реальний multi-container застосунок з правильною мережевою ізоляцією.
Network Namespace — це механізм ізоляції мережевого стеку на рівні ядра Linux. Кожен namespace має власний набір мережевих інтерфейсів, таблицю маршрутизації, правила firewall (iptables), сокети. Процеси всередині одного namespace "бачать" лише мережеві ресурси цього namespace — вони не можуть безпосередньо взаємодіяти з процесами в іншому namespace.
Docker використовує network namespaces для ізоляції контейнерів. Коли ви запускаєте контейнер, Docker створює новий network namespace і розміщує процеси контейнера всередині нього. Це означає, що:
lo) — 127.0.0.1 всередині контейнера вказує на сам контейнер, а не на хостeth0) з унікальною IP-адресоюifconfig всередині контейнера показує лише інтерфейси контейнераЗапустимо контейнер і подивимося на його мережеві інтерфейси:
# Запустити контейнер Alpine Linux
docker run -it --rm alpine sh
# Всередині контейнера: подивитися мережеві інтерфейси
ip addr show
Вивід всередині контейнера:
Що ми бачимо:
lo (loopback): Локальний інтерфейс з адресою 127.0.0.1 — ізольований всередині контейнераeth0: Віртуальний Ethernet-інтерфейс з IP-адресою 172.17.0.2 — це адреса контейнера у Docker-мережіwlan0, enp0s3 чи інших інтерфейсів хост-машиниТепер подивимося на інтерфейси на хості (у новому терміналі, не всередині контейнера):
# На хості: подивитися Docker-інтерфейси
ip addr show | grep docker
Вивід на хості:
Що ми бачимо:
docker0: Віртуальний bridge (міст) з IP-адресою 172.17.0.1 — це шлюз (gateway) для контейнерівveth1a2b3c4: Virtual Ethernet pair — один кінець у хості, інший (eth0) всередині контейнераDocker з'єднує контейнер з хостом через veth pair (Virtual Ethernet pair) — пару віртуальних мережевих інтерфейсів, що працюють як "труба": пакет, відправлений в один кінець, з'являється на іншому.
Потік даних:
eth0veth1a2b3c4 на хостіveth1a2b3c4 підключений до docker0 bridgeDocker підтримує кілька типів мереж (network drivers), кожен з яких вирішує специфічні завдання. Вибір типу мережі залежить від архітектури застосунку, вимог до ізоляції та deployment-середовища (single host vs cluster).
| Тип мережі | Призначення | Use Case | Ізоляція |
|---|---|---|---|
| bridge | Приватна мережа на одному хості | Локальна розробка, multi-container додатки | Контейнери бачать один одного, ізольовані від зовнішнього світу |
| host | Контейнер використовує мережу хоста | Високопродуктивні додатки, мінімальна латентність | Немає ізоляції — контейнер бачить всі інтерфейси хоста |
| overlay | Мережа через кілька хостів | Docker Swarm, Kubernetes | Контейнери на різних машинах бачать один одного |
| macvlan | Контейнер отримує MAC-адресу | Legacy додатки, що потребують фізичної адреси | Контейнер виглядає як фізичний пристрій у мережі |
| none | Немає мережі | Повна ізоляція, тестування | Контейнер не має мережевого доступу |
У цій статті ми детально розглянемо bridge (найпоширеніший) та host (для специфічних сценаріїв). Overlay та macvlan — теми для просунутих статей про orchestration.
Bridge network — це приватна віртуальна мережа, створена Docker на хості. Контейнери, підключені до однієї bridge-мережі, можуть спілкуватися один з одним через IP-адреси або DNS-імена. Контейнери в різних bridge-мережах ізольовані — вони не бачать один одного без явного з'єднання.
Docker автоматично створює default bridge network (docker0) при встановленні. Коли ви запускаєте контейнер без явного вказання мережі, він підключається до цієї default bridge.
Docker надає два типи bridge-мереж:
1. Default Bridge (docker0):
--link (deprecated)2. User-Defined Bridge:
docker network createПереглянути існуючі мережі:
docker network ls
Вивід:
Створити user-defined bridge:
# Створити мережу з назвою "myapp-network"
docker network create myapp-network
# Переглянути деталі мережі
docker network inspect myapp-network
Вивід inspect (скорочено):
[
{
"Name": "myapp-network",
"Driver": "bridge",
"Scope": "local",
"IPAM": {
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Containers": {}
}
]
Що ми бачимо:
172.18.0.0/16 — діапазон IP-адрес для контейнерів у цій мережі (65534 можливих адреси)172.18.0.1 — IP-адреса bridge на хості, через яку контейнери виходять назовні--subnet при створенні мережі.Спосіб 1: При запуску контейнера
# Запустити контейнер у мережі myapp-network
docker run -d \
--name web \
--network myapp-network \
nginx:alpine
Спосіб 2: Підключити існуючий контейнер
# Запустити контейнер без мережі
docker run -d --name api nginx:alpine
# Підключити до мережі
docker network connect myapp-network api
Перевірити підключення:
docker network inspect myapp-network
Вивід (фрагмент):
"Containers": {
"a1b2c3d4...": {
"Name": "web",
"IPv4Address": "172.18.0.2/16"
},
"f6e5d4c3...": {
"Name": "api",
"IPv4Address": "172.18.0.3/16"
}
}
Тепер обидва контейнери (web та api) знаходяться в одній мережі і можуть спілкуватися.
У попередньому прикладі контейнер web отримав IP 172.18.0.2, а api — 172.18.0.3. Але ці адреси динамічні — Docker призначає їх автоматично при запуску контейнера. Якщо ви перезапустите контейнер, він може отримати іншу IP-адресу.
Проблема: Як контейнер web має підключитися до api, якщо IP-адреса api може змінитися? Хардкодити IP у конфігурації — антипатерн.
Рішення: Docker надає вбудований DNS-сервер для user-defined bridge networks. Контейнери можуть звертатися один до одного за іменами контейнерів замість IP-адрес.
Коли ви створюєте user-defined bridge network, Docker автоматично запускає вбудований DNS-сервер на адресі 127.0.0.11:53 всередині кожного контейнера. Цей DNS-сервер:
google.com) до DNS-серверів хостаЗапустимо два контейнери у user-defined bridge та перевіримо DNS:
# Створити мережу
docker network create demo-network
# Запустити контейнер "backend"
docker run -d \
--name backend \
--network demo-network \
nginx:alpine
# Запустити контейнер "frontend" та перевірити DNS
docker run -it --rm \
--name frontend \
--network demo-network \
alpine sh
Всередині контейнера frontend:
# Перевірити резолюцію імені "backend"
nslookup backend
# Вивід:
# Server: 127.0.0.11
# Address: 127.0.0.11:53
#
# Name: backend
# Address: 172.18.0.2
# Перевірити з'єднання через HTTP
wget -qO- http://backend
Що відбулося:
nslookup backend відправила DNS-запит до 127.0.0.11 (Docker DNS)backend у мережі demo-network172.18.0.2wget підключився до http://backend (Docker автоматично резолвив ім'я в IP)--network, вони підключаються до default bridge (docker0), де DNS-резолюція імен не працює. Потрібно використовувати IP-адреси або deprecated --link.Іноді потрібно, щоб контейнер був доступний під кількома іменами. Наприклад, контейнер з базою даних може мати ім'я postgres-primary, але ви хочете, щоб він був доступний також як db для зручності.
Додати алиас при запуску:
docker run -d \
--name postgres-primary \
--network myapp-network \
--network-alias db \
--network-alias database \
postgres:16
Тепер контейнер доступний за трьома іменами: postgres-primary, db, database.
Перевірка:
docker run -it --rm \
--network myapp-network \
alpine sh -c "nslookup db && nslookup database"
Обидва запити повернуть ту саму IP-адресу.
Контейнери у bridge network можуть спілкуватися між собою через приватні IP-адреси (наприклад, 172.18.0.2). Але ці адреси недоступні ззовні хоста — ви не можете відкрити браузер на своєму комп'ютері та зайти на http://172.18.0.2.
Чому? Тому що 172.18.0.0/16 — це приватна мережа, створена Docker всередині хоста. Зовнішній світ (ваш браузер, інші машини в локальній мережі) не знає про існування цієї мережі.
Рішення: Docker надає механізм port mapping (проброс портів) — прив'язку порту контейнера до порту хоста. Коли ви відкриваєте http://localhost:8080 на хості, Docker перенаправляє трафік до контейнера.
Формат: -p HOST_PORT:CONTAINER_PORT
# Запустити Nginx, пробросити порт 80 контейнера на порт 8080 хоста
docker run -d \
--name web \
-p 8080:80 \
nginx:alpine
Що відбувається:
80localhost:8080 → перенаправити до 172.18.0.2:80http://localhost:8080 у браузері — побачите NginxПеревірка:
curl http://localhost:8080
# Вивід: <!DOCTYPE html><html>...
1. Прив'язка до конкретного інтерфейсу:
# Слухати лише на localhost (недоступно з інших машин)
docker run -d -p 127.0.0.1:8080:80 nginx:alpine
# Слухати на всіх інтерфейсах (доступно з локальної мережі)
docker run -d -p 0.0.0.0:8080:80 nginx:alpine
2. Автоматичний вибір порту хоста:
# Docker сам вибере вільний порт на хості
docker run -d -p 80 nginx:alpine
# Подивитися, який порт призначено
docker port <container_id>
# Вивід: 80/tcp -> 0.0.0.0:32768
3. Кілька портів:
# Пробросити HTTP (80) та HTTPS (443)
docker run -d \
-p 8080:80 \
-p 8443:443 \
nginx:alpine
4. UDP-порти:
# За замовчуванням -p пробросить TCP. Для UDP:
docker run -d -p 53:53/udp dns-server
# Для конкретного контейнера
docker port web
# Вивід:
# 80/tcp -> 0.0.0.0:8080
Або через docker ps:
docker ps
Вивід:
Host network — це режим, у якому контейнер не отримує власний network namespace. Замість цього контейнер використовує мережевий стек хоста безпосередньо. Це означає:
eth0, wlan0, docker0, тощоПереваги:
Недоліки:
Запустити контейнер у host network:
docker run -d \
--name web-host \
--network host \
nginx:alpine
Що відбувається:
8080 — це порт хоста, а не контейнера-p 8080:80 — контейнер вже на хостіПеревірка:
# На хості (не всередині контейнера)
curl http://localhost:80
# Вивід: <!DOCTYPE html><html>...
# Подивитися процеси, що слухають на порту 80
sudo netstat -tlnp | grep :80
# Вивід: tcp 0 0.0.0.0:80 0.0.0.0:* LISTEN 12345/nginx
Порівняння з bridge:
# Всередині контейнера з host network
docker exec web-host ip addr show
# Вивід: ТІ Ж інтерфейси, що й на хості
# eth0, wlan0, docker0, lo — все доступно
Рекомендовані сценарії:
НЕ рекомендується:
Тепер застосуємо знання на практиці. Побудуємо типовий веб-застосунок з трьома компонентами:
Вимоги до мережі:
8080Створимо дві bridge-мережі для ізоляції:
Пояснення архітектури:
# Створити frontend-network
docker network create frontend-network
# Створити backend-network
docker network create backend-network
# Запустити PostgreSQL у backend-network
docker run -d \
--name db \
--network backend-network \
-e POSTGRES_PASSWORD=mysecret \
-e POSTGRES_DB=myapp \
postgres:16
Важливо: Контейнер db доступний за іменем db всередині backend-network. Немає проброса портів — база недоступна ззовні.
# Запустити .NET API
docker run -d \
--name backend \
--network frontend-network \
-e ConnectionStrings__Default="Host=db;Database=myapp;Username=postgres;Password=mysecret" \
myapp-api:latest
# Підключити backend до другої мережі (backend-network)
docker network connect backend-network backend
Пояснення:
frontend-network (може спілкуватися з Frontend)backend-network (може спілкуватися з Database)Host=db — Docker DNS резолвить це в IP PostgreSQL# Запустити Nginx у frontend-network
docker run -d \
--name frontend \
--network frontend-network \
-p 8080:80 \
myapp-frontend:latest
Конфігурація Nginx (всередині образу myapp-frontend):
server {
listen 80;
# Статичні файли
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# Проксування API-запитів до Backend
location /api/ {
proxy_pass http://backend:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Що відбувається:
80 всередині контейнера80 пробросено на 8080 хоста/api/* проксуються до http://backend:5000 — Docker DNS резолвить backend# Відкрити у браузері
curl http://localhost:8080
# Перевірити API через Frontend
curl http://localhost:8080/api/health
# Спробувати підключитися до Database ззовні (має не вдатися)
psql -h localhost -U postgres -d myapp
# Помилка: Connection refused (порт не пробросено)
Перевірка ізоляції:
# Frontend НЕ може підключитися до Database
docker exec frontend ping db
# Помилка: bad address 'db' (немає в frontend-network)
# Backend МОЖЕ підключитися до Database
docker exec backend ping db
# Успіх: PING db (172.19.0.2)
Docker networking за замовчуванням забезпечує ізоляцію між мережами — контейнери в різних bridge-мережах не можуть спілкуватися один з одним без явного з'єднання. Це фундаментальний принцип безпеки: якщо контейнер скомпрометовано, атакуючий не може автоматично отримати доступ до інших контейнерів.
Рівні ізоляції:
Docker використовує iptables (firewall Linux) для реалізації мережевої ізоляції та NAT. Коли ви створюєте мережу або пробросуєте порт, Docker автоматично додає правила в iptables.
Подивитися правила Docker:
# Переглянути NAT-правила (port mapping)
sudo iptables -t nat -L -n
# Переглянути filter-правила (ізоляція)
sudo iptables -t filter -L DOCKER-ISOLATION-STAGE-1 -n
Приклад правила для port mapping:
Chain DOCKER (2 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
Пояснення: Дозволити TCP-трафік з будь-якої адреси (0.0.0.0/0) до контейнера 172.17.0.2 на порт 80.
Приклад правила ізоляції:
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target prot opt source destination
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
Пояснення: За замовчуванням DROP (відкинути) весь трафік між мережами, якщо немає явного дозволу.
1. Не пробросуйте порти без потреби
Кожен пробросений порт (-p) — це потенційна точка атаки. Якщо контейнер не потребує доступу ззовні, не використовуйте -p.
Погано:
# База даних доступна ззовні — небезпечно!
docker run -d -p 5432:5432 postgres:16
Добре:
# База даних доступна лише всередині Docker-мережі
docker run -d --network backend-network postgres:16
2. Використовуйте 127.0.0.1 для локальних сервісів
Якщо контейнер потрібен лише на хості (не з інших машин), прив'яжіть порт до localhost:
# Доступно лише з хоста, не з локальної мережі
docker run -d -p 127.0.0.1:5432:5432 postgres:16
3. Створюйте окремі мережі для різних застосунків
Не запускайте всі контейнери в одній мережі. Якщо у вас два незалежні застосунки (наприклад, app1 та app2), створіть окремі мережі:
docker network create app1-network
docker network create app2-network
Це запобігає випадковому або зловмисному доступу між застосунками.
За замовчуванням Docker дозволяє контейнерам у одній bridge-мережі спілкуватися один з одним. Це називається ICC (Inter-Container Communication).
Вимкнути ICC для default bridge:
# Зупинити Docker daemon
sudo systemctl stop docker
# Відредагувати /etc/docker/daemon.json
sudo nano /etc/docker/daemon.json
Додати:
{
"icc": false
}
# Перезапустити Docker
sudo systemctl start docker
Тепер контейнери в default bridge не можуть спілкуватися без явних --link (deprecated) або user-defined networks.
Docker bridge networks не шифрують трафік між контейнерами — дані передаються у відкритому вигляді через віртуальний bridge. Для більшості локальних сценаріїв це прийнятно, оскільки трафік не виходить за межі хоста.
Коли потрібне шифрування:
Overlay network з шифруванням:
# Створити overlay-мережу з шифруванням (Docker Swarm)
docker network create \
--driver overlay \
--opt encrypted \
secure-network
Трафік між контейнерами шифрується через IPsec.
Симптоми:
docker exec frontend curl http://backend:5000
# Помилка: Could not resolve host: backend
Можливі причини:
docker network inspectРішення:
# Перевірити, в яких мережах контейнери
docker inspect frontend --format='{{json .NetworkSettings.Networks}}'
docker inspect backend --format='{{json .NetworkSettings.Networks}}'
# Підключити до спільної мережі
docker network connect myapp-network frontend
docker network connect myapp-network backend
Симптоми:
curl http://localhost:8080
# Помилка: Connection refused
Можливі причини:
docker ps127.0.0.1 — має слухати на 0.0.0.0Рішення:
# Перевірити, чи контейнер запущений
docker ps | grep web
# Перевірити логи контейнера
docker logs web
# Перевірити, чи додаток слухає на правильному порту
docker exec web netstat -tlnp
# Має бути: 0.0.0.0:80, а не 127.0.0.1:80
Виправлення для .NET:
// Program.cs — слухати на всіх інтерфейсах
builder.WebHost.UseUrls("http://0.0.0.0:5000");
Симптоми:
docker exec web ping google.com
# Помилка: Network is unreachable
Можливі причини:
Рішення:
# Перевірити DNS всередині контейнера
docker exec web cat /etc/resolv.conf
# Має бути: nameserver 8.8.8.8 або IP хоста
# Перевірити маршрутизацію
docker exec web ip route
# Має бути default route через docker0
# Перевірити NAT на хості
sudo iptables -t nat -L POSTROUTING -n
# Має бути MASQUERADE для Docker-мереж
Вказати кастомний DNS при створенні мережі:
docker network create \
--dns 8.8.8.8 \
--dns 1.1.1.1 \
myapp-network
1. docker network inspect — детальна інформація про мережу
docker network inspect myapp-network
Показує: subnet, gateway, підключені контейнери з IP-адресами.
2. docker exec + мережеві утиліти
# Встановити утиліти в Alpine-контейнері
docker exec -it web apk add curl bind-tools iputils
# Перевірити DNS
docker exec web nslookup backend
# Перевірити з'єднання
docker exec web ping backend
# Перевірити HTTP
docker exec web curl http://backend:5000
3. tcpdump для аналізу трафіку
# На хості: перехопити трафік на docker0
sudo tcpdump -i docker0 -n
# Всередині контейнера (якщо tcpdump встановлено)
docker exec web tcpdump -i eth0 -n
4. Логи Docker daemon
# Переглянути логи Docker
sudo journalctl -u docker -f
# Або (залежно від системи)
sudo tail -f /var/log/docker.log
docker run -it --rm \
--network myapp-network \
nicolaka/netshoot
nicolaka/netshoot містить 30+ мережевих інструментів.Чому: Default bridge (docker0) не підтримує автоматичну DNS-резолюцію імен контейнерів. User-defined bridge надає DNS, кращу ізоляцію та гнучкість.
Погано:
# Контейнери в default bridge — немає DNS
docker run -d --name web nginx
docker run -d --name api myapp
# api не може знайти web за іменем
Добре:
# Створити мережу та запустити контейнери
docker network create myapp-network
docker run -d --name web --network myapp-network nginx
docker run -d --name api --network myapp-network myapp
# api може звертатися до http://web
Принцип: Розділяйте presentation layer, business logic layer та data layer у різні мережі. Це реалізує defense in depth — навіть якщо один рівень скомпрометовано, атакуючий не має прямого доступу до інших.
Архітектура:
frontend-network: [Frontend] ←→ [Backend]
backend-network: [Backend] ←→ [Database]
Frontend не бачить Database. Backend — єдина точка доступу до даних.
Правило: Пробросуйте порти (-p) лише для контейнерів, що мають бути доступні ззовні (зазвичай лише frontend/reverse proxy).
Погано:
docker run -d -p 5432:5432 postgres # База доступна ззовні
docker run -d -p 5000:5000 api # API доступний ззовні
docker run -d -p 8080:80 frontend # Frontend доступний ззовні
Добре:
docker run -d --network backend-net postgres # Немає -p
docker run -d --network app-net api # Немає -p
docker run -d --network app-net -p 8080:80 frontend # Лише frontend
Сценарій: У вас є primary та replica бази даних. Додаток підключається до db, але ви хочете мати можливість переключити його між primary та replica без зміни коду.
# Primary database
docker run -d \
--name postgres-primary \
--network backend-net \
--network-alias db \
postgres:16
# Replica database (standby)
docker run -d \
--name postgres-replica \
--network backend-net \
postgres:16
# Додаток підключається до "db" — резолвиться в primary
docker run -d \
--network backend-net \
-e DATABASE_URL=postgres://db:5432/myapp \
myapp
Якщо primary падає, ви можете зупинити його, додати алиас db до replica, і додаток автоматично переключиться.
Створіть діаграму мережі у README проєкту або docker-compose.yml. Це допомагає новим розробникам зрозуміти, як компоненти спілкуються.
Приклад документації:
## Network Architecture
- **frontend-network** (172.18.0.0/16)
- nginx (frontend) — 172.18.0.2
- api (backend) — 172.18.0.3
- **backend-network** (172.19.0.0/16)
- api (backend) — 172.19.0.2
- postgres (db) — 172.19.0.3
- redis (cache) — 172.19.0.4
**Access:**
- Frontend → Backend: http://api:5000
- Backend → Database: postgres://db:5432
- Backend → Cache: redis://cache:6379
Замість ручного створення мереж та запуску контейнерів, використовуйте Docker Compose для декларативного опису мережевої архітектури.
docker-compose.yml:
version: '3.8'
services:
frontend:
image: nginx:alpine
ports:
- "8080:80"
networks:
- frontend-net
backend:
image: myapp-api:latest
networks:
- frontend-net
- backend-net
environment:
- DATABASE_URL=postgres://db:5432/myapp
database:
image: postgres:16
networks:
- backend-net
environment:
- POSTGRES_PASSWORD=mysecret
volumes:
- db-data:/var/lib/postgresql/data
networks:
frontend-net:
driver: bridge
backend-net:
driver: bridge
volumes:
db-data:
Запуск:
docker-compose up -d
Docker Compose автоматично створить мережі, запустить контейнери у правильному порядку та налаштує DNS.
У цій статті ми детально розглянули мережеву підсистему Docker — від базових концепцій до практичних сценаріїв та troubleshooting.
Ключові концепції:
http://container-name замість IP-адрес.-p HOST_PORT:CONTAINER_PORT. Дозволяє доступ до контейнера ззовні. Мінімізуйте кількість пробросених портів для безпеки.127.0.0.11) для user-defined networks. Контейнери можуть звертатися один до одного за іменами замість IP-адрес.Найкращі практики:
Troubleshooting:
docker network inspect — перевірити підключення контейнерів до мережіdocker exec <container> nslookup <name> — перевірити DNS-резолюціюdocker logs <container> — переглянути логи для діагностики проблем з'єднанняnicolaka/netshoot — debugging-контейнер з 30+ мережевими утилітамиЩо далі:
У наступній статті ми розглянемо Docker Compose — інструмент для декларативного опису multi-container застосунків. Ви навчитеся описувати складні мережеві архітектури у YAML-файлі, керувати життєвим циклом кількох контейнерів однією командою, та організовувати development/production environments.
Завдання 1.1: Дослідження Network Namespaces
Запустіть контейнер Alpine та порівняйте його мережеві інтерфейси з інтерфейсами хоста.
# Запустити контейнер
docker run -it --rm alpine sh
# Всередині контейнера
ip addr show
exit
# На хості
ip addr show
Питання:
wlan0)?Завдання 1.2: DNS-резолюція
Створіть user-defined bridge network та два контейнери. Перевірте DNS-резолюцію.
docker network create test-network
docker run -d --name web --network test-network nginx:alpine
docker run -it --rm --network test-network alpine sh
# Всередині другого контейнера
nslookup web
ping web
wget -qO- http://web
Питання:
web?ping? Чи працює HTTP-запит?Завдання 1.3: Port Mapping
Запустіть Nginx з пробросом порту та перевірте доступ ззовні.
docker run -d --name web -p 8080:80 nginx:alpine
curl http://localhost:8080
docker port web
Питання:
http://localhost:8080 у браузері?Завдання 2.1: Multi-container застосунок
Створіть простий застосунок з двома контейнерами: Nginx (frontend) та httpbin (backend для тестування API).
Вимоги:
app-networkkennethreitz/httpbin як backend8080:80/api/* до backendПідказка для Nginx config:
server {
listen 80;
location /api/ {
proxy_pass http://backend:80/;
}
}
Перевірка:
curl http://localhost:8080/api/get
# Має повернути JSON від httpbin
Завдання 2.2: Ізоляція мереж
Створіть три контейнери у двох різних мережах та перевірте ізоляцію.
docker network create net1
docker network create net2
docker run -d --name container1 --network net1 alpine sleep 3600
docker run -d --name container2 --network net2 alpine sleep 3600
docker run -d --name container3 --network net1 alpine sleep 3600
Питання:
container1 пінгувати container3? (обидва в net1)container1 пінгувати container2? (різні мережі)container2 до net1, щоб він міг спілкуватися з container1?Завдання 2.3: Troubleshooting
Запустіть контейнер з .NET API, що слухає на 127.0.0.1:5000 (неправильно). Діагностуйте проблему та виправте.
# Dockerfile з помилкою
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY app /app
WORKDIR /app
ENV ASPNETCORE_URLS=http://127.0.0.1:5000
ENTRYPOINT ["dotnet", "MyApp.dll"]
Питання:
curl http://localhost:5000 на хості не працює після -p 5000:5000?ASPNETCORE_URLS?Завдання 3.1: Three-tier архітектура
Побудуйте повноцінний three-tier застосунок з правильною мережевою ізоляцією:
Компоненти:
Вимоги:
frontend-net, backend-net, cache-netfrontend-net, доступний на порту 8080frontend-net та backend-netbackend-netcache-net та backend-netdb, Redis як cacheПеревірка:
# Frontend може звернутися до Backend
docker exec frontend curl http://backend:5000/health
# Backend може звернутися до Database
docker exec backend pg_isready -h db
# Backend може звернутися до Redis
docker exec backend redis-cli -h cache ping
# Frontend НЕ може звернутися до Database
docker exec frontend ping db
# Має бути: bad address 'db'
Завдання 3.2: Load Balancing через DNS
Створіть два backend-контейнери з однаковим network alias та перевірте round-robin DNS.
docker network create lb-network
docker run -d --name backend1 --network lb-network --network-alias api nginx:alpine
docker run -d --name backend2 --network lb-network --network-alias api nginx:alpine
docker run -it --rm --network lb-network alpine sh
# Всередині:
nslookup api
# Має повернути дві IP-адреси
Питання:
api?backend1 та backend2?Завдання 3.3: Мережева безпека
Налаштуйте застосунок з максимальною мережевою ізоляцією:
Вимоги:
localhost:8080 (не з локальної мережі)Перевірка безпеки:
# З іншої машини в локальній мережі
curl http://<host-ip>:8080
# Має бути: Connection refused
# З хоста
curl http://localhost:8080
# Має працювати
# Спроба підключитися до Database ззовні
psql -h <host-ip> -U postgres
# Має бути: Connection refused
docker network inspect <network> для перевірки підключених контейнерів та їхніх IP-адрес. Використовуйте docker logs <container> для діагностики проблем з'єднання.