Docker и Docker Compose — професионално ръководство
Част 6: Docker Networking
Контейнерите не са острови. Те трябва да комуникират помежду си, с host системата и, когато е необходимо, с външния свят. Тук разглеждам Docker Networking от основите до практическите схеми, които използвам в реална инфраструктура.
ℹ Бележка Най-важното правило при Docker Networking: не публикувай port, само защото приложението има такъв. Published port е механизъм за достъп отвън към container, а не изискване, за да могат два containers да комуникират помежду си.
1. Как мисля за Docker Networking
Docker не стартира контейнерите просто като процеси, които случайно са се оказали на една машина. Всеки container има собствен network namespace и собствена мрежова конфигурация. Docker Engine изгражда необходимата свързаност чрез network drivers, virtual interfaces и правила на host системата.
На практика това означава, че мога да имам nginx, application и database на един host, без да отварям database към локалната мрежа. Web контейнерът говори с database контейнера през Docker network, докато само nginx има публикуван port към host-а.
Client
HTTP/HTTPS
→
Nginx
Published port
→
Application
Docker network
↓
Database
Достъпна само през вътрешната Docker network
2. Основните Docker Network Drivers
Docker поддържа няколко network drivers. Не е необходимо да използвам всички, но трябва да разбирам разликата между тях, защото изборът влияе директно върху изолацията и начина, по който контейнерите комуникират.
| Driver | Предназначение | Практическа употреба |
|---|---|---|
| bridge | Изолирана мрежа за containers на един Docker host | Най-често срещаният избор |
| host | Container използва network stack-а на host-а | Специализирани случаи |
| none | Без нормална мрежова свързаност | Силно изолирани containers |
| overlay | Мрежа между Docker hosts | Docker Swarm и разпределени среди |
| macvlan | Container получава собствен MAC адрес | Специализирани L2 сценарии |
3. Bridge Network — работният кон
За обикновена инфраструктура с няколко containers на един host bridge network е основният инструмент. Docker създава виртуална мрежа и включва контейнерите в нея чрез виртуални Ethernet интерфейси.
Има важна разлика между автоматично създадената default bridge network и собствена user-defined bridge network. За реални приложения предпочитам втория вариант.
TERMINAL — USER-DEFINED NETWORK
docker network create app_net
docker network ls
docker network inspect app_net
✓ Успех User-defined bridge network дава по-добър контрол и позволява containers да се намират един друг по име чрез вградения Docker DNS.
4. Комуникация между Containers
Нека създам два containers в една network. Няма нужда да знам IP адреса на първия container, за да го достигна от втория.
TERMINAL
docker network create backend
docker run -d \
--name database \
--network backend \
postgres:16-alpine
docker run -it --rm \
--network backend \
alpine sh
От временния Alpine container мога да проверя резолюцията на името database. Docker DNS ще разреши това име към адреса на съответния container в network-а.
ВЪТРЕ В CONTAINER-А
getent hosts database
ping database
⚠ Предупреждение Не връзвай application към database по IP адрес. Използвай име на service или container в user-defined network. Container може да бъде унищожен и създаден отново с друг IP адрес.
5. Published Ports ≠ Container-to-Container Networking
Това е едно от най-важните разграничения в Docker.
ПРИМЕР
docker run -d \
--name web \
-p 8080:80 \
nginx:alpine
8080:80 означава: port 8080 на host-а се публикува към port 80 в container-а. Това е необходимо, ако клиент извън Docker network-а трябва да достигне nginx.
Ако два containers са в една Docker network, вторият не трябва да използва host port-а, за да достигне първия.
ПРАВИЛНО
http://web:80
✓ Успех Моят практичен принцип: публикувам само портовете, които трябва да бъдат достъпни извън съответната Docker network. Всичко останало оставям вътрешно.
6. Network Inspect — когато нещо не работи
Когато контейнерите „трябва“ да се виждат, но не се виждат, не започвам да гадая. Първата ми работа е да проверя network configuration.
DIAGNOSTICS
docker network ls
docker network inspect backend
docker inspect database
docker exec -it database sh
В изхода на docker network inspect особено ме интересува секцията Containers. Там мога веднага да видя кои containers са включени в network-а и какви адреси са получили.
6.1. Типичен диагностичен ред
- Проверявам дали и двата containers са running;
- проверявам дали са в една и съща user-defined network;
- проверявам DNS резолюцията по име;
- проверявам дали приложението слуша на правилния port;
- проверявам firewall и други мрежови политики;
- чак тогава търся проблем в самото приложение.
7. Един Container в Няколко Networks
Docker позволява един container да бъде включен едновременно в няколко мрежи. Това е полезно за разделяне на frontend и backend комуникацията.
proxy_net
Reverse proxy ↔ application
app
Участва и в двете networks
db_net
application ↔ database
Така database container-ът може да бъде извън frontend network-а. Той приема връзки само от containers, които имат достъп до db_net.
TERMINAL
docker network create proxy_net
docker network create db_net
docker run -d --name database --network db_net postgres:16-alpine
docker run -d --name app --network db_net myapp:latest
docker network connect proxy_net app
8. Docker Compose Networking
При Docker Compose ситуацията става още по-интересна, защото Compose автоматично създава network за проекта. Services, които са част от един Compose project, могат да комуникират помежду си по service name.
compose.yaml
services:
web:
image: nginx:alpine
depends_on:
- app
app:
image: myapp:latest
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
Application container-ът не трябва да използва localhost, за да се свърже с PostgreSQL. localhost означава текущия container.
ПРАВИЛНИЯТ DATABASE HOST
DB_HOST=db
DB_PORT=5432
При Compose service name е hostname в рамките на Compose network-а.
9. Когато Compose Network-ът трябва да е изричен
При по-голяма инфраструктура често искам да определя topology-то изрично.
compose.yaml
services:
proxy:
image: nginx:alpine
networks:
- frontend
app:
image: myapp:latest
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
Тук proxy няма директна връзка с database. Application container-ът е единственият, който участва и в двете мрежи.
ℹ Бележка Това вече е архитектура, а не просто Docker команда. Когато разделям networks по предназначение, контролирам кой компонент изобщо има възможност да говори с друг компонент.
10. Най-опасната дума: localhost
Един от най-честите проблеми при Docker Networking е configuration, която изглежда напълно логична:
ГРЕШНО В CONTAINER
DB_HOST=localhost
Ако application и database са в различни containers, това няма да работи. localhost сочи към network namespace-а на application container-а, а не към Docker host-а и не към database container-а.
ПРАВИЛНО
DB_HOST=db
11. Networking и Security
Docker Networking не е само въпрос на това „да тръгне“. Мрежовата архитектура е част от security модела на приложението.
- Не публикувам database ports без реална необходимост;
- разделям frontend и backend traffic, когато архитектурата го изисква;
- не приемам, че container network автоматично решава всички security проблеми;
- проверявам реалната network topology, вместо да разчитам на предположения.
⚠ Предупреждение Published port означава достъп. Преди да добавя -p 5432:5432 , трябва да имам конкретна причина. Ако application и PostgreSQL са в една Docker network, обикновено такъв publish не е необходим.
12. Когато Networking не работи
При проблем с комуникацията между containers не променям на случаен принцип IP адреси, ports и configuration файлове. Работя последователно.
МОЯТ БАЗОВ DIAGNOSTIC CHECK
docker ps
docker network ls
docker network inspect app_net
docker inspect app
docker exec -it app sh
# Вътре в container-а:
getent hosts db
Ако getent hosts db не върне адрес, проблемът е много вероятно в network membership или DNS. Ако името се резолвира, но връзката към port-а не работи, проверявам дали приложението слуша на правилния interface и port.
13. Практически правила, които следвам
- Използвам user-defined bridge networks за приложения на един host.
- Използвам service names, а не IP адреси между Compose services.
- Не използвам localhost за комуникация с друг container.
- Публикувам само необходимите ports.
- Разделям frontend и backend networks, когато архитектурата го оправдава.
- Проверявам network topology с docker network inspect, преди да започна да гадая.
- Не бъркам published port с вътрешна container комуникация.
✓ Успех Накратко: добрият Docker Networking не е този с най-много публикувани портове. Добър е този, при който всеки container има точно толкова мрежов достъп, колкото реално му е необходим.
Заключение
Docker Networking изглежда просто, докато не започнеш да изграждаш реална инфраструктура. Тогава разликата между host port, container port, service name, network namespace и Docker network става критична.
За мен правилният подход е да мисля първо за комуникационните зависимости, а след това за командите. Кой трябва да вижда кого? Кой port трябва да бъде достъпен отвън? Кои containers трябва да останат изолирани?
Когато тези въпроси са ясни, Docker Networking престава да бъде магия. Получава се нормална мрежова архитектура — само че реализирана върху containers.