前言
最近在 Ubuntu 服务器上使用 Docker 部署 XXL-Job 分布式任务调度平台时,遇到了一个典型但容易踩坑的网络问题:调度中心容器与 MySQL 容器无法正常通信,导致登录界面点击后毫无反应。本文将复盘整个部署过程,并重点分享如何通过 Docker 自定义网络彻底解决该问题。
如果你也正准备在 Linux 环境下用 Docker 搭建 XXL-Job,或者遇到类似的容器互联问题,相信这篇文章能帮你少走弯路。
环境说明
- 操作系统:Ubuntu 20.04 / 22.04(Intel 芯片)
- Docker 版本:20.10+
- MySQL:已通过 Docker 运行
amd64/mysql:5.7.36容器,端口映射3306:3306,容器名mysql,root 密码root - XXL-Job 版本:2.4.0
- 数据挂载路径:
/data/docker/xxl-job/logs
第一阶段:初始化数据库
XXL-Job 依赖 MySQL 存储任务元数据,首先需要执行官方提供的初始化脚本。
# 下载 2.4.0 版本的 SQL 脚本wgethttps://raw.githubusercontent.com/xuxueli/xxl-job/2.4.0/doc/db/tables_xxl_job.sql# 将脚本导入已运行的 MySQL 容器dockerexec-imysql mysql-uroot-proot<tables_xxl_job.sql验证表是否创建成功:
dockerexec-itmysql mysql-uroot-proot-e"USE xxl_job; SHOW TABLES;"若看到xxl_job_info、xxl_job_log等表,说明数据库初始化完成。
第二阶段:启动调度中心(首次尝试,方案一)
按照常规思路,我将 MySQL 端口映射到了宿主机3306,于是尝试让 XXL-Job 容器通过127.0.0.1:3306连接数据库:
dockerrun-d\--namexxl-job-admin\--restart=always\-p8080:8080\-v/data/docker/xxl-job/logs:/data/applogs\-ePARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai \ --spring.datasource.username=root \ --spring.datasource.password=root \ --xxl.job.accessToken=default_token"\xuxueli/xxl-job-admin:2.4.0容器启动正常,访问http://<服务器IP>:8080/xxl-job-admin也能看到登录页。然而输入账号admin密码123456点击登录后,页面毫无反应,浏览器控制台也没有明显报错。
第三阶段:问题排查
1. 查看容器日志
dockerlogs-f--tail=100xxl-job-admin日志中抛出了关键异常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up.很明显,调度中心根本无法连接到 MySQL 数据库。
2. 分析网络隔离原因
问题的根源在于 Docker 容器的网络隔离机制:
- XXL-Job 容器内使用的
127.0.0.1指向的是容器自身,而不是宿主机。 - MySQL 虽然通过
-p 3306:3306将端口映射到了宿主机,但容器想要访问宿主机网络,不能直接使用127.0.0.1。
3. 尝试方案一:host.docker.internal
在 Mac/Windows 的 Docker Desktop 中,可以使用特殊域名host.docker.internal来访问宿主机。我尝试在 Linux 下也使用该方式,将连接地址改为:
jdbc:mysql://host.docker.internal:3306/xxl_job?...重新启动容器后,问题依旧。查阅文档发现,在 Linux 系统上,Docker 默认并不支持host.docker.internal(除非在docker run时添加--add-host参数手动映射)。因此方案一失败。
第四阶段:最终解决方案(方案二)—— 使用 Docker 自定义网络
最标准、最稳定的做法是:将两个容器置于同一个自定义 Docker 网络中,直接通过容器名互相访问。
步骤详解
1. 创建自定义桥接网络
dockernetwork create xxl-net2. 将已运行的 MySQL 容器连接到该网络
dockernetwork connect xxl-net mysql3. 启动 XXL-Job 容器并加入同一网络
注意数据库连接地址直接使用 MySQL 容器名mysql:
# 如果之前启动过 xxl-job-admin,先停止并删除dockerstop xxl-job-admindockerrmxxl-job-admin# 使用新网络启动dockerrun-d\--namexxl-job-admin\--networkxxl-net\--restart=always\-p8080:8080\-v/data/docker/xxl-job/logs:/data/applogs\-ePARAMS="--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai \ --spring.datasource.username=root \ --spring.datasource.password=root \ --xxl.job.accessToken=default_token"\xuxueli/xxl-job-admin:2.4.0原理说明
- 同一个自定义网络内的容器,Docker 会提供内置的 DNS 服务。
- 容器名
mysql会被解析为 MySQL 容器的内部 IP,无需关心端口映射,直接通过容器的3306端口通信。 - 这种方式避免了宿主机端口暴露带来的安全隐患,也绕过了
127.0.0.1的歧义问题。
4. 验证结果
再次访问http://<服务器IP>:8080/xxl-job-admin,输入admin/123456,登录成功!
如果遇到密码错误,可以进入 MySQL 容器手动将
xxl_job_user表中admin用户的密码改为 MD5 值e10adc3949ba59abbe56e057f20f883e(对应123456),因为 2.4.0 版本存在密码加密方式不一致的已知问题。
总结
本次部署的核心卡点在于Docker 容器间的网络通信。通过自定义网络xxl-net,我们实现了:
- 调度中心与 MySQL 的稳定互联
- 配置简洁,无需关心宿主机 IP 变化
- 符合 Docker 最佳实践,易于后续扩展(如增加更多执行器容器)
完整部署架构图(简化版)
一些经验教训
- 在 Linux 上不要依赖
host.docker.internal,除非你手动在docker run中添加--add-host host.docker.internal:host-gateway。 - 优先使用 Docker 自定义网络来处理容器间通信,这是官方推荐的方式。
- 遇到问题时,第一时间查看容器日志
docker logs <容器名>,往往能直接定位错误。
希望这篇记录能帮助你在 Docker 上顺利部署 XXL-Job,也为你处理类似的容器网络问题提供一个清晰的思路。