Как протестировать связь между контейнерами Docker

DockerBeginner
Практиковаться сейчас

Введение

Контейнеры Docker стали фундаментальной частью современного разработки и развертывания приложений. При работе с несколькими контейнерами обеспечение правильной связи между ними имеет решающее значение для корректной работы ваших приложений.

В этом практическом занятии вы узнаете, как проверить и устранить проблемы с подключением между контейнерами Docker. Мы начнем с основных концепций контейнеров Docker, настроим контейнеры для тестирования, а затем изучим различные методы проверки и диагностики сетевых подключений между контейнерами.

По завершении этого руководства вы сможете уверенно тестировать, проверять и решать проблемы с подключением в ваших контейнеризованных приложениях.

Для этого практического занятия требуется подключение к Интернету для обучения, поэтому только пользователи Pro могут запустить виртуальную машину. Перейдите к тарифу Pro.

Настройка контейнеров Docker для тестирования

Прежде чем мы сможем протестировать связь между контейнерами, нам нужно создать несколько контейнеров для работы. В этом шаге мы настроим два простых контейнера Docker и узнаем о основных командах Docker.

Общие сведения о контейнерах Docker

Контейнеры Docker - это легковесные автономные пакеты, которые включают все, что нужно для запуска приложения: код, среду выполнения, системные инструменты, библиотеки и настройки. Контейнеры используют ядро операционной системы хост-машины, но работают в изолированных средах.

Создание тестовых контейнеров

Давайте начнем с создания двух простых контейнеров на основе образа Ubuntu. Мы будем использовать эти контейнеры на протяжении всего практического занятия для проверки связи между ними.

Сначала создадим наш первый контейнер:

docker run -d --name container1 ubuntu:22.04 sleep infinity

Теперь создадим второй контейнер:

docker run -d --name container2 ubuntu:22.04 sleep infinity

запуск контейнеров

Пояснение параметров команды:

  • -d: Запускает контейнер в отсоединенном режиме (в фоновом режиме)
  • --name: Назначает имя контейнеру
  • ubuntu:22.04: Docker-образ для использования (версия Ubuntu 22.04)
  • sleep infinity: Команда, которая запускает контейнер на бесконечное время

Проверка запуска контейнеров

Для проверки правильности запуска контейнеров используйте следующую команду:

docker ps

Вы должны увидеть вывод, похожий на этот:

CONTAINER ID   IMAGE          COMMAND            CREATED         STATUS         PORTS     NAMES
f8d97c645cce   ubuntu:22.04   "sleep infinity"   5 seconds ago   Up 4 seconds             container2
a2c516a57cb8   ubuntu:22.04   "sleep infinity"   18 seconds ago  Up 17 seconds            container1

Если вы не видите оба контейнера в списке, они, возможно, не запустились правильно. Попробуйте создать их снова.

Установка сетевых инструментов

По умолчанию образ контейнера Ubuntu очень минималистичный и не включает сетевые инструменты, которые нам понадобятся. Установим эти инструменты в обоих контейнерах:

Для контейнера1:

docker exec container1 apt-get update
docker exec container1 apt-get install -y iputils-ping net-tools iproute2 curl

Для контейнера2:

docker exec container2 apt-get update
docker exec container2 apt-get install -y iputils-ping net-tools iproute2 curl

Эти команды:

  1. Используют docker exec для выполнения команд внутри запущенного контейнера
  2. Обновляют список пакетов с помощью apt-get update
  3. Устанавливают сетевые инструменты (iputils-ping для проверки соединения, net-tools для netstat, iproute2 для ip-команд и curl)

Теперь наши контейнеры готовы к проверке связи в следующих шагах.

Общие сведения о сетях Docker

Контейнеры Docker общаются друг с другом через сети. Для тестирования и устранения проблем с подключением между контейнерами необходимо понимать, как работает сетевое взаимодействие Docker.

Основы сетей Docker

При установке Docker автоматически создаются несколько стандартных сетей. Чаще всего используются следующие:

  • bridge: Стандартная сеть, к которой подключаются контейнеры, если не указана другая сеть
  • host: Контейнеры напрямую используют сеть хоста (без изоляции)
  • none: Контейнеры не имеют доступа к сети

Проверим сети на вашей системе:

docker network ls

Вы должны увидеть вывод, похожий на этот:

NETWORK ID     NAME      DRIVER    SCOPE
1b95853bf83b   bridge    bridge    local
91199fc6ad2e   host      host      local
1078d2c781b6   none      null      local

Общие сведения о стандартной сети bridge

По умолчанию оба наших контейнера подключены к стандартной сети bridge. Проверим эту сеть:

docker network inspect bridge

Эта команда выводит подробную информацию о сети bridge, включая контейнеры, подключенные к ней, и их IP-адреса. В выводе найдите раздел "Containers", чтобы увидеть, что в нем перечислены оба контейнера1 и контейнер2 с их IP-адресами.

Поиск IP-адресов контейнеров

Для работы с подключением контейнеров нам нужно знать IP-адреса, назначенные нашим контейнерам. Есть несколько способов получить эту информацию:

Используя docker inspect:

docker inspect --format='{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container1

Или изнутри контейнера:

docker exec container1 hostname -i

Запишите IP-адреса обоих контейнеров:

docker exec container1 hostname -i
docker exec container2 hostname -i

Вывод покажет IP-адрес каждого контейнера, который обычно начинается с 172.17.0.x на стандартной сети bridge.

Создание пользовательской сети Docker

Хотя стандартная сеть bridge позволяет контейнерам общаться, создание пользовательской сети обеспечивает лучшую изоляцию и встроенное разрешение DNS (контейнеры могут обращаться друг к другу по имени, а не по IP).

Создадим пользовательскую сеть bridge:

docker network create --driver bridge my-network

Теперь подключим наши существующие контейнеры к этой новой сети:

docker network connect my-network container1
docker network connect my-network container2

Мы можем проверить, что контейнеры подключены к новой сети:

docker network inspect my-network

Теперь наши контейнеры подключены как к стандартной сети bridge, так и к нашей пользовательской сети my-network. В следующем шаге мы протестируем связь между ними.

Тестирование базовой связи между контейнерами

Теперь, когда наши контейнеры настроены и подключены к сетям, мы можем протестировать связь между ними. Мы используем несколько методов, чтобы проверить, могут ли контейнеры общаться друг с другом.

Использование ping для проверки связи

Самый простой способ проверить базовое сетевое взаимодействие - это использовать команду ping, которая отправляет запросы эхо ICMP на целевой хост.

Попробуем отправить ping из контейнера1 в контейнер2 по IP-адресу:

## Сначала получим IP-адрес контейнера2
CONTAINER2_IP=$(docker exec container2 hostname -i)
echo "IP-адрес контейнера2: $CONTAINER2_IP"

## Получим первый IP-адрес из CONTAINER2_IP
CONTAINER2_IP=$(echo $CONTAINER2_IP | cut -d' ' -f1)
echo "IP-адрес контейнера2: $CONTAINER2_IP"

## Теперь отправим ping из контейнера1 в контейнер2
docker exec container1 ping -c 4 $CONTAINER2_IP

Вы должны увидеть вывод, похожий на этот:

PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: icmp_seq=0 ttl=64 time=0.095 ms
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.067 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.090 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.087 ms
--- 172.17.0.3 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.067/0.085/0.095/0.000 ms

Тестирование разрешения DNS

Одним из преимуществ пользовательских сетей Docker является то, что они обеспечивают автоматическое разрешение DNS, позволяя контейнерам обращаться друг к другу по имени.

Протестируем разрешение DNS, отправив ping по имени контейнера:

docker exec container1 ping -c 4 container2

Это должно работать, потому что оба контейнера находятся на нашей пользовательской сети my-network, которая обеспечивает разрешение DNS. Вы должны увидеть ответы ping, похожие на предыдущий тест, но используя имя контейнера вместо IP-адреса.

Использование curl для проверки HTTP-связи

Для приложений, которые запускают веб-сервисы, часто нужно проверить HTTP-связь. Создадим простой HTTP-сервер в контейнере2 и протестируем связь с ним из контейнера1.

Сначала запустим базовый HTTP-сервер в контейнере2:

docker exec container2 apt-get install -y python3
docker exec -d container2 bash -c "echo 'Hello from container2' > /index.html && cd / && python3 -m http.server 8080"

Подождите несколько секунд, пока сервер запустится, затем протестируйте соединение из контейнера1:

docker exec container1 curl -s http://container2:8080

Вы должны увидеть вывод:

Hello from container2

Это подтверждает, что контейнер1 может подключиться к HTTP-сервису контейнера2, используя имя контейнера для разрешения DNS.

Тестирование соединения с использованием различных методов

Также полезно знать, как проверить связь с использованием других инструментов. Попробуем использовать nc (netcat), чтобы проверить, открыт ли определенный порт:

## Убедитесь, что netcat установлен в контейнере1
docker exec container1 apt-get install -y netcat

## Протестируем связь с HTTP-сервером на контейнере2
docker exec container1 nc -zv container2 8080

Вы должны увидеть вывод, указывающий на успешное соединение:

Connection to container2 (172.18.0.3) 8080 port [tcp/*] succeeded!

Эти тесты подтверждают, что наши контейнеры могут общаться друг с другом как на уровне сети (ping), так и на уровне приложения (HTTP).

Решение проблем с подключением контейнеров

Если у вас нет проблем с подключением, вы можете пропустить этот шаг.

Теперь, когда мы знаем, как тестировать связь между контейнерами, давайте изучим, как устранять общие проблемы с подключением.

Общие проблемы с подключением

При работе с сетями контейнеров Docker вы можете столкнуться с несколькими общими проблемами:

  1. Контейнеры на разных сетях без правильной маршрутизации
  2. Настройки брандмауэра или безопасности блокируют трафик
  3. Приложение не слушает ожидаемый IP/порт
  4. Ошибки в настройке сети
  5. Проблемы с разрешением DNS

Рассмотрим шаги по устранению каждой из этих потенциальных проблем.

Проверка конфигурации сети

Сначала рассмотрим конфигурацию сети наших контейнеров:

## Просмотр сетевых интерфейсов контейнера1
docker exec container1 ip addr show

## Просмотр сетевых интерфейсов контейнера2
docker exec container2 ip addr show

Вывод показывает все сетевые интерфейсы в каждом контейнере. Каждая подключенная сеть Docker отображается в виде интерфейса eth с назначенным IP-адресом.

Проверка маршрутов сети

Проверим конфигурацию маршрутизации в наших контейнерах:

docker exec container1 route -n

Это показывает таблицу маршрутизации для контейнера1, указывая, куда направляется сетевой трафик.

Проверка слушаемых портов

Для определения, правильно ли приложение слушает соединения, используйте:

docker exec container2 netstat -tuln

Это показывает все TCP и UDP порты, которые слушаются. Наш HTTP-сервер должен слушать порт 8080.

Диагностика с использованием tcpdump

Для более детального анализа сетевого трафика мы можем использовать tcpdump для захвата и анализа пакетов:

## Установить tcpdump в контейнер1
docker exec container1 apt-get install -y tcpdump dnsutils

## Захватить пакеты в течение 10 секунд
docker exec container1 timeout 10 tcpdump -i eth0 -n

Во время выполнения этого откройте другой терминал и сгенерируйте некоторый трафик:

docker exec container1 ping -c 3 container2

В выводе tcpdump вы должны увидеть захваченные пакеты ICMP.

Проверка разрешения DNS в Docker

Если у вас возникают проблемы с разрешением DNS между контейнерами:

## Проверить конфигурацию DNS
docker exec container1 cat /etc/resolv.conf

## Протестировать разрешение DNS
docker exec container1 nslookup container2

Симуляция проблемы сети

Протестируем проблему с подключением, временно отключив контейнер1 от нашей пользовательской сети:

## Отключить контейнер1 от my-network
docker network disconnect my-network container1

## Попробовать отправить ping по имени (это должно завершиться неудачей)
docker exec container1 ping -c 2 container2

Вы должны увидеть, что ping завершается неудачей, потому что контейнер1 не может больше разрешать имя контейнера2 после отключения от общей сети.

Подключим его снова:

## Переподключить контейнер1 к my-network
docker network connect my-network container1

## Проверить, восстановлено ли подключение
docker exec container1 ping -c 2 container2

Теперь ping должен работать снова, демонстрируя, что контейнеры должны находиться на одной сети для правильного разрешения имен.

Чек-лист по устранению неполадок

При возникновении проблем с подключением контейнеров следуйте этому чек-листу:

  1. Проверить, запущены ли контейнеры: docker ps
  2. Проверить, находятся ли они на одной сети: docker network inspect <сеть>
  3. Проверить, назначены ли IP-адреса: docker inspect <контейнер>
  4. Протестировать базовое подключение (ping): docker exec <контейнер> ping <целевой>
  5. Проверить, слушает ли приложение: docker exec <контейнер> netstat -tuln
  6. Проверить, работает ли разрешение DNS: docker exec <контейнер> nslookup <целевой>
  7. Проверить наличие проблем с брандмауэром: docker exec <контейнер> iptables -L
  8. Проверить логи контейнера: docker logs <контейнер>

Систематический подход поможет вам выявить и решить большинство проблем с подключением контейнеров.

Резюме

В этом практическом занятии вы узнали, как тестировать и устранять проблемы с подключением между контейнерами Docker. Вы:

  • Создали контейнеры Docker и настроили их для работы с сетью
  • Изучили основы сетевого взаимодействия Docker, включая стандартные и пользовательские сети
  • Использовали различные инструменты для тестирования базовой связи между контейнерами (ping, curl, netcat)
  • Исследовали общие проблемы с подключением и узнали систематические подходы к их устранению

Эти навыки необходимы для разработки и поддержки приложений на основе Docker, особенно в микросервисных архитектурах, где несколько контейнеров должны взаимодействовать друг с другом.

Для дальнейшего обучения вы можете изучить Docker Compose для приложений с несколькими контейнерами, Docker Swarm или Kubernetes для оркестрации контейнеров, а также более продвинутые концепции сети, такие как наложенные сети для развертывания на нескольких узлах.