Docker Swarm下SQLAlchemy应用空闲15分钟连接超时问题
你遇到的精确15分钟空闲后数据库连接断开问题,和MariaDB本身默认8小时的连接超时配置无关,根因是Docker Swarm overlay网络默认的TCP空闲连接老化规则:
- Docker Swarm模式下,跨服务的容器通信默认走VXLAN实现的overlay虚拟网络,宿主机Linux内核的netfilter连接跟踪(conntrack)模块,默认会将连续900秒(即15分钟)没有任何数据包传输的TCP连接标记为失效,直接从连接跟踪表中清除该条目,且不会向连接两端的容器发送RST断开通知,属于静默丢弃。
- 本地直接运行、普通
docker compose up部署时,容器间通信走默认的bridge网络,该网络没有配置15分钟的短空闲超时规则,空闲连接可以一直保持到MariaDB侧8小时超时才会被主动断开,因此不会复现该问题。
报错触发逻辑
SQLAlchemy默认会启动连接池,池内会缓存已经建立好的数据库连接供后续请求复用。当服务连续15分钟没有收到请求时,池内的所有空闲连接都会因为无流量传输,被Swarm overlay网络的conntrack规则判定为失效并清除跟踪记录。
此时新的请求进来,应用从连接池取出这个已经被网络层掐断的旧连接发送查询请求,数据包会被网络层直接丢弃,最终抛出Connection reset by peer、Lost connection to MySQL server during query的错误。
临时规避方案生效的原因
你配置pool_recycle=60*10(10分钟)时,SQLAlchemy会自动销毁连接池内存活时间超过10分钟的连接,下次请求到来时直接新建数据库连接,不会让连接存活到15分钟的网络超时阈值,因此可以绕过网络层的连接丢弃规则。
更稳妥的修复方案
推荐在代码侧创建数据库引擎时,同时配置连接预检测和合理的回收周期,不需要调整基础设施配置即可彻底解决问题:
engine = create_async_engine( "mysql+asyncmy://root:pw@mariadbhost/somedb", future=True, pool_recycle=600, # 10分钟回收,小于网络15分钟超时 pool_pre_ping=True # 取连接时先做连通性检测,失效连接自动替换 )
注意:不要尝试通过修改MariaDB的
wait_timeout参数解决该问题,连接是被中间网络层静默断开的,数据库本身感知不到连接失效,修改数据库侧超时参数完全不生效。
如果要从基础设施侧根治,可以修改所有Swarm节点的Linux内核conntrack参数,将TCP空闲连接超时调整为大于业务连接最大空闲周期的值,但该方案需要修改所有集群节点配置,运维成本较高,一般不推荐。
内容的提问来源于stack exchange,提问作者Mehmet Ates

