Ответ
Проблема с обрывом 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.