ПО в Docker-контейнере открывает соединение с БД в другом сетевом сегменте (не в Docker). Соединения периодически обрываются. Со стороны БД проблем нет. В чем могут быть причины?

«ПО в Docker-контейнере открывает соединение с БД в другом сетевом сегменте (не в Docker). Соединения периодически обрываются. Со стороны БД проблем нет. В чем могут быть причины?» — вопрос из категории Сети, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Проблема с обрывом long-lived соединений из контейнера во внешнюю сеть — классическая для распределенных систем. Вот основные причины и методы диагностики:

1. NAT Timeout на промежуточных устройствах (основной подозреваемый): Маршрутизаторы, файрволлы или облачные гейтвеи часто имеют таблицы трансляции адресов (NAT) с таймаутом для неактивных соединений (например, 300-600 секунд). Если между контейнером и БД есть такое устройство, оно может разорвать соединение.

Решение в приложении/контейнере: Принудительно использовать keepalive-пакеты TCP.

# В Dockerfile или при запуске контейнера настройте sysctl
sysctl -w net.ipv4.tcp_keepalive_time=60
sysctl -w net.ipv4.tcp_keepalive_intvl=10
sysctl -w net.ipv4.tcp_keepalive_probes=6

Это заставит ядро отправлять keepalive-пакеты каждые 60 секунд неактивности, поддерживая соединение "живым" для NAT.

2. Настройки таймаута на стороне БД: Проверьте параметры СУБД:

  • MySQL/MariaDB: wait_timeout, interactive_timeout.
  • PostgreSQL: tcp_keepalives_idle, tcp_keepalives_interval.
  • Убедитесь, что они не меньше, чем таймауты на сетевых устройствах.

3. Проблемы с сетевым стэком хоста Docker:

  • Конфликтующие iptables правила: Правила, управляемые Docker, могут конфликтовать с пользовательскими.
  • Сборщик мусора соединений (TCP TW_REUSE): На загруженном хосте могут агрессивно очищаться соединения в состоянии TIME_WAIT.

Диагностика:

# 1. Мониторинг соединений с хоста
ss -tanop | grep :<db_port>
netstat -tnop | grep :<db_port>

# 2. Логирование обрывов с помощью tcpdump
# Запустите на хосте Docker, где работает контейнер
tcpdump -i any -s 0 -w /tmp/db_conn.pcap host <db_ip> and port <db_port>

# 3. Проверка сетевых правил
iptables -L -n -v --line-numbers
iptables -t nat -L -n -v

# 4. Проверка логов ядра на предмет сетевых ошибок
dmesg | grep -i "drop|reset|timeout"

Общая рекомендация: Реализуйте в приложении механизм повторного подключения (reconnection logic) и пул соединений, который валидирует соединения перед использованием. Это обязательно для устойчивости в production.