前言
最近在 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 存储任务元数据,首先需要执行官方提供的初始化脚本。
bash
# 下载 2.4.0 版本的 SQL 脚本
wget https://raw.githubusercontent.com/xuxueli/xxl-job/2.4.0/doc/db/tables_xxl_job.sql
# 将脚本导入已运行的 MySQL 容器
docker exec -i mysql mysql -uroot -proot < tables_xxl_job.sql
验证表是否创建成功:
bash
docker exec -it mysql mysql -uroot -proot -e "USE xxl_job; SHOW TABLES;"
若看到 xxl_job_info、xxl_job_log 等表,说明数据库初始化完成。
第二阶段:启动调度中心(首次尝试,方案一)
按照常规思路,我将 MySQL 端口映射到了宿主机 3306,于是尝试让 XXL-Job 容器通过 127.0.0.1:3306 连接数据库:
bash
docker run -d \
--name xxl-job-admin \
--restart=always \
-p 8080:8080 \
-v /data/docker/xxl-job/logs:/data/applogs \
-e PARAMS="--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. 查看容器日志
bash
docker logs -f --tail=100 xxl-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. 创建自定义桥接网络
bash
docker network create xxl-net
2. 将已运行的 MySQL 容器连接到该网络
bash
docker network connect xxl-net mysql
3. 启动 XXL-Job 容器并加入同一网络
注意数据库连接地址直接使用 MySQL 容器名 mysql:
bash
# 如果之前启动过 xxl-job-admin,先停止并删除
docker stop xxl-job-admin
docker rm xxl-job-admin
# 使用新网络启动
docker run -d \
--name xxl-job-admin \
--network xxl-net \
--restart=always \
-p 8080:8080 \
-v /data/docker/xxl-job/logs:/data/applogs \
-e PARAMS="--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,也为你处理类似的容器网络问题提供一个清晰的思路。