资讯动态

XXL-JOB容器化部署实战:K8S集群落地与避坑指南

发布时间:2026/10/15 12:16:54 来源:尧图企业网站定制
简介面向需要在Kubernetes集群中快速部署XXL-JOB定时任务调度平台的运维与开发人员这份经过验证的YAML部署文件可省去自行编写配置的繁琐过程直接应用到集群即可完成基础环境搭建。资源包内共1个文件为YAML格式清单整体仅约782B轻量小巧便于下载与审阅适合直接纳入现有集群或作为部署模板二次调整。目前已有646人学习下载说明该方案被不少同类场景使用者认可。该YAML将XXL-JOB容器化部署所需的关键配置集中整合涵盖集群模式下的资源对象定义读者既能直接拿来落地也可作为理解K8s服务编排与xxljob部署细节的参考样例降低环境搭建与排错成本。相比从零编写这份经验沉淀能明显缩短从镜像到服务可用的验证周期尤其适合生产前快速验证与CI/CD流水线集成。1. XXL-JOB 容器化部署本地能跑不代表集群能用调度中心在本地 IDE 里一点就起复制到 K8S 集群却怎么调都起不来卡了一整天最后发现是连接串少了时区参数——这种经历部署过 XXL-JOB 的人多少都撞上过。XXL-JOB 是最常见的分布式任务调度框架容器化部署本身不复杂真正劝退人的是 YAML 里的隐性约定执行器注册地址怎么写、Pod IP 漂移了怎么办、admin 和 executor 版本能不能对上、MySQL 到底放不放集群里。这些问题不提前理清YAML 一遍遍 apply 下去报错全是玄学。这篇笔记以 XXL-JOB 2.4.0 为例记录一套从 MySQL、调度中心到执行器在 K8S 集群里完整落地的部署方案版本、镜像、参数、验证拆开讲。2. 版本与部署前准备镜像、存储、命名空间一次定清2.1 镜像版本选择admin 与 executor 必须保持同一分支动手写 YAML 之前先确认一件事调度中心xxl-job-admin和执行器xxl-job-executor的镜像版本要保持在同一条大版本线上。我见过有人在集群里 admin 用 2.4.0执行器却拉了个 2.2.0 的镜像结果执行器管理页能看到心跳点执行就一直超时。XXL-JOB 调度器和执行器之间的通信字段在不同版本里有变化老执行器注册上来后admin 经常解析不出地址信息日志里又看不出明显报错只表现为任务调度超时。这种问题排查起来非常费劲因为表面迹象和实际原因隔了一层。版本确定后镜像要提前准备好。常见做法是把 xxl-job-admin 和 xxl-job-executor 对应版本的镜像拉到本地再推到你集群节点能访问的私有仓库YAML 里的 image 字段直接写私有仓库地址。如果集群节点拉镜像不方便这一步必须在部署前做完否则 apply 的时候会卡在 ImagePullBackOff 上。我一般会顺手把镜像 tag 固定下来比如 xxl-job-admin:2.4.0不写 latest避免过段时间拉下来一个行为不一致的新镜像。2.2 命名空间与资源规划PVC、副本数、StorageClass 预先定好这套部署我建议单独开一个命名空间叫 xxl-job和业务应用隔离。集群里如果多个项目共用默认空间Deployment、Service 重名冲突不说后面看日志、清理资源都乱。命名空间本身是一个很轻量的资源单独建一个不亏。下面是我常用的资源规划方式。组件副本数资源建议存储要求MySQL1测试用CPU 500m / 内存 1Gi需要 PVC容量建议 10Gi 起xxl-job-admin2CPU 500m / 内存 1Gi不需要持久化存储xxl-job-executor按业务量扩CPU 200m / 内存 512Mi日志按需挂 emptyDir 或 PVCMySQL 放进集群还是用外部实例取决于你那边的基础设施情况。如果公司已经有 RDS 或者统一的 MySQL 服务直接用外部地址就行省掉一个要维护的组件如果没有就在集群里起一个单实例 MySQL 做元数据库测试环境足够。PVC 的 storageClass 要提前确认K8S 集群默认 storageClass 没配好的话PVC 会一直 Pending后面 Pod 也起不来。2.3 核心环境变量清单部署前先把参数表吃透XXL-JOB 两个镜像本质上是 Spring Boot 应用官方镜像默认支持用环境变量 PARAMS 直接透传启动参数不用进容器改配置文件。部署前把下面这张表吃透后面写 YAML 基本不会慌。参数作用推荐值spring.datasource.url调度中心连库串jdbc:mysql://xxl-job-mysql:3306/xxl_job?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalsespring.datasource.username / password数据库账号密码root / 自定义强密码xxl.job.admin.addresses执行器注册调度中心的完整地址http://xxl-job-admin:8080/xxl-job-adminxxl.job.accessToken调度中心与执行器通信鉴权自定义随机字符串xxl.job.executor.appname执行器注册的应用名对应管理台执行器名称xxl-job-executorxxl.job.executor.ip执行器向调度中心注册的 IP动态注入 Pod IP见 3.3 节xxl.job.executor.port执行器暴露的端口9999xxl.job.log.path执行器运行日志落盘路径/data/applogs/xxl-job/jobhandler这里最容易被忽略的是时区参数 serverTimezone。MySQL 连接串里不加它admin 启动时大概率报 The server time zone value 之类的时区异常。另外 accessToken 默认是空的但集群环境下我会强制设一个防止内网里其他服务误连调度中心。这个值调度中心配置和执行器配置必须完全一致一个小数点不对执行器就注册不上。3. 逐项落地 YAML从 MySQL 到调度中心再到执行器3.1 MySQL把调度中心的元数据库放进集群先写 MySQL 的 Deployment 和 Service。这一层比较标准关键是把数据目录挂到 PVC 上Pod 重启后数据不丢。下面这份 YAML 可以直接存成 01-mysql.yaml。apiVersion: apps/v1 kind: Deployment metadata: name: xxl-job-mysql namespace: xxl-job spec: replicas: 1 selector: matchLabels: app: xxl-job-mysql template: metadata: labels: app: xxl-job-mysql spec: containers: - name: mysql image: your-registry/mysql:5.7 ports: - containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD value: XxlJob2024 - name: MYSQL_DATABASE value: xxl_job volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumes: - name: mysql-data persistentVolumeClaim: claimName: xxl-job-mysql-pvc --- apiVersion: v1 kind: Service metadata: name: xxl-job-mysql namespace: xxl-job spec: selector: app: xxl-job-mysql ports: - port: 3306 targetPort: 3306容器启动时 MYSQL_DATABASExxl_job 会自动建库数据目录挂在 PVC 上Pod 删了重建数据还在。MySQL 5.7 的镜像比较成熟XXL-JOB 官方文档里对 MySQL 版本没有特别苛刻的要求5.7 是足够稳的选择。PVC 部分需要你提前在集群里准备好 storageClass创建方式通常是维护同学已经配好的不需要你手动建 PV。建完库之后表结构需要手动初始化。XXL-JOB 官方提供了一个建表 SQL 脚本 tables_xxl_job.sql这步不能跳过。把脚本拷进 Pod 再执行MYSQL_POD$(kubectl get pod -n xxl-job -l appxxl-job-mysql -o jsonpath{.items[0].metadata.name}) kubectl cp ./tables_xxl_job.sql xxl-job/$MYSQL_POD:/tmp/tables_xxl_job.sql kubectl exec -n xxl-job $MYSQL_POD -- sh -c mysql -uroot -pXxlJob2024 xxl_job /tmp/tables_xxl_job.sql第一条命令先动态拿到 MySQL Pod 的名字再用 kubectl cp 把本地的 SQL 脚本传进 Pod 的 /tmp 目录最后在 Pod 里执行导入。这里没有把 Pod 名写死因为 Deployment 管理的 Pod 名带随机后缀每次重建都会变。如果导入时报错先检查脚本路径是否传对再检查 mysql 客户端版本兼容性。3.2 调度中心 adminDeployment、探针与 Service 三件套调度中心是核心组件建议开两个副本。它的状态都存在 MySQL 里多副本不会产生数据不一致的问题反而能避免单点故障。探针很重要admin 启动比较慢探针配置不当会出现明明还在启动却被误杀的情况后面避坑章节专门讲。这份存成 02-admin.yaml。apiVersion: apps/v1 kind: Deployment metadata: name: xxl-job-admin namespace: xxl-job spec: replicas: 2 selector: matchLabels: app: xxl-job-admin template: metadata: labels: app: xxl-job-admin spec: containers: - name: admin image: your-registry/xxl-job-admin:2.4.0 ports: - containerPort: 8080 env: - name: PARAMS value: --spring.datasource.urljdbc:mysql://xxl-job-mysql:3306/xxl_job?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalse --spring.datasource.usernameroot --spring.datasource.passwordXxlJob2024 readinessProbe: httpGet: path: /xxl-job-admin/ port: 8080 initialDelaySeconds: 30 periodSeconds: 10 livenessProbe: httpGet: path: /xxl-job-admin/ port: 8080 initialDelaySeconds: 60 periodSeconds: 20 --- apiVersion: v1 kind: Service metadata: name: xxl-job-admin namespace: xxl-job spec: type: ClusterIP selector: app: xxl-job-admin ports: - port: 8080 targetPort: 8080PARAMS 环境变量把数据库连接串一次性透传进去多个参数之间用空格分开每个参数前加 -- 前缀这是 Spring Boot 标准的命令行参数写法。连接串里必须带 serverTimezoneAsia/Shanghai。镜像默认端口是 8080context path 是 /xxl-job-admin所以探针的 path 写成 /xxl-job-admin/不要把路径漏掉。两个探针的 initialDelaySeconds 我给得比较保守。readinessProbe 等 30 秒才开始探测livenessProbe 等 60 秒因为 admin 第一次启动要初始化连接池。如果设得太小探针在应用还没就绪时就判失败Pod 会被反复重启日志里又看不出业务异常。3.3 执行器 executorIP 注入、Token 与日志路径执行器的部署比 admin 多了两个关键点IP 注入和端口暴露。很多人在这里翻车直接把 IP 写死成某个固定值Pod 重建之后就彻底失联。正确做法是用 Kubernetes 的 fieldRef 机制把当前 Pod 的 IP 注入到环境变量里再传给启动参数。这份存成 03-executor.yaml。apiVersion: apps/v1 kind: Deployment metadata: name: xxl-job-executor namespace: xxl-job spec: replicas: 1 selector: matchLabels: app: xxl-job-executor template: metadata: labels: app: xxl-job-executor spec: containers: - name: executor image: your-registry/xxl-job-executor:2.4.0 ports: - containerPort: 9999 env: - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PARAMS value: --xxl.job.admin.addresseshttp://xxl-job-admin:8080/xxl-job-admin --xxl.job.accessTokenXxlJobToken2024 --xxl.job.executor.appnamexxl-job-executor --xxl.job.executor.ip$(POD_IP) --xxl.job.executor.port9999 --xxl.job.log.path/data/applogs/xxl-job/jobhandler volumeMounts: - name: logs mountPath: /data/applogs/xxl-job volumes: - name: logs emptyDir: {}POD_IP 环境变量通过 fieldRef 从 status.podIP 取值这是 K8S 原生的字段注入方式Pod 每次重建都会拿到新 IP不需要你手工改 YAML。这里有个顺序要求POD_IP 必须定义在 PARAMS 之前这样 PARAMS 里的 $(POD_IP) 才能被正确展开。如果顺序写反容器启动后 IP 是空的执行器注册的地址就是 :9999。accessToken 我在这里写的是 XxlJobToken2024你在实际部署时替换成自己的随机字符串。这个值要和 admin 侧保持一致后面有时间我再写一篇怎么用 Secret 管理 token——部署阶段用明文写在 YAML 里是常见做法记得到生产环境要换掉。3.4 apply 顺序与一次过检查从空集群到 XXL-JOB 启动三个 YAML 文件都准备好之后apply 的顺序有讲究。我的习惯是先建命名空间和 MySQL再导 SQL最后起 admin 和 executor。上面已经建好表的话直接执行kubectl create namespace xxl-job kubectl apply -n xxl-job -f 01-mysql.yaml kubectl apply -n xxl-job -f 02-admin.yaml kubectl apply -n xxl-job -f 03-executor.yaml如果命名空间已经存在kubectl create 会报 AlreadyExists这个错误不影响后续操作直接继续 apply 就行。想避免这条报错的话也可以把 namespace 写成一个单独的 YAML 文件用 kubectl apply -f namespace.yaml 来创建。apply 的顺序是先 MySQL 后 admin因为 admin 启动时就要连数据库executor 放最后它启动时需要能访问 admin。apply 完成后先看 Pod 状态kubectl get pod -n xxl-job -o wide正常的话三个 Pod 都会是 Running 且 Ready 为 1/1。如果看到 CrashLoopBackOff先看日志kubectl logs -n xxl-job deployment/xxl-job-admin --tail100日志里出现数据库连接异常优先排查 MySQL 的 Service 名、端口和密码出现时区相关报错检查连接串里的 serverTimezone 参数。executor 的日志则要特别注意注册地址打印出来的内容确认 IP 不是空的。4. 避坑指南XXL-JOB 容器化部署的五个典型翻车现场4.1 数据库连不上时区参数与 DNS 解析都要看现象admin Pod 启动后一直 CrashLoopBackOffkubectl logs 里反复刷 Communications link failure 或者 Access denied。原因多数情况是连接串写错。最常见的两个坑一是忘了加 serverTimezoneAsia/ShanghaiMySQL 驱动在时区不明确时会直接抛异常二是 Service 名称写错比如把 xxl-job-mysql 写成了 mysql或者漏了端口。解决先核对连接串里的 Service 名、端口和密码。用 kubectl exec 进 admin Pod 里测试连通性再用 kubectl logs 看完整异常栈。把连接串改成 jdbc:mysql://xxl-job-mysql:3306/xxl_job?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalse基本能解决九成问题。4.2 执行器永远离线Token 与 admin 地址要对齐现象admin 管理台能正常打开执行器管理页面也看得到 xxl-job-executor 这个名字但状态一直离线注册地址显示不出来。原因调度中心侧配置的 accessToken 和执行器侧配置的不一致。XXL-JOB 在 token 不匹配时不会拒绝注册而是默默丢弃心跳数据表现就是执行器一直离线或者在线状态反复横跳。另一个原因是 admin.addresses 写错比如把 http 写成了 https或者路径漏了 /xxl-job-admin。解决两端 token 改成一样的重启 executor。admin.addresses 必须是执行器容器内能访问到的地址也就是 http://xxl-job-admin:8080/xxl-job-admin不要写成 localhost。4.3 Pod 重启后任务失联IP 漂移才是最隐蔽的坑现象刚部署完一切正常某天节点维护或者 Deployment 滚动更新后执行器管理页里的注册地址变成了一个旧 IP任务全部调度失败。原因执行器 YAML 里把 xxl.job.executor.ip 写死了。K8S 里 Pod 的 IP 是动态分配的重建后会变写死 IP 等于断了后路。解决用 3.3 节的方式通过 fieldRef 注入 status.podIP让执行器每次启动都拿当前 Pod 的真实 IP。这也是 K8S 部署 XXL-JOB 最推荐的方案。从那以后我再也没手写过执行器 IP全走注入。4.4 探针误杀admin 启动慢被误判为不健康现象admin Pod 反复重启kubectl get pod 看到 RESTARTS 一直涨但日志里没有明显异常最后一条日志停在 Spring Boot 的启动阶段。原因livenessProbe 的 initialDelaySeconds 设太小admin 第一次启动要初始化连接池和定时线程还没就绪就被探针判定失败K8S 直接杀掉重启。这是个很典型的配置节奏问题。解决readinessProbe 的 initialDelaySeconds 调到 30 秒以上livenessProbe 调到 60 秒以上。如果集群节点性能一般再往上加 10 到 20 秒都不过分。探针是保护机制不是考勤打卡延迟大一点问题不大。4.5 建表脚本没执行任务配置页反复报错现象admin 启动成功登录管理台也正常但进到任务管理或者执行器管理页面时页面报错后台日志出现表不存在的 SQL 异常。原因只建了数据库没有导入 tables_xxl_job.sql。XXL-JOB 的 admin 启动时不会自动建表这是很多人都踩过的坑。解决按 3.1 节的命令把 SQL 脚本导入然后重启 admin Pod 让连接池重新建立。导入之后可以用一条命令验证表是否存在kubectl exec -n xxl-job deploy/xxl-job-mysql -- sh -c mysql -uroot -pXxlJob2024 -e USE xxl_job; SHOW TABLES;能看到 xxl_job_qrtz 开头的定时任务表以及 xxl_job_info、xxl_job_registry 这些核心表才算真正完成。5. 部署后验证把一个定时任务完整跑通5.1 从 Pod 状态看到调度中心启动部署完成后第一件事是看 Pod 状态和日志kubectl get pod -n xxl-job -o wide kubectl logs -n xxl-job deployment/xxl-job-admin --tail50 kubectl logs -n xxl-job deployment/xxl-job-executor --tail50admin 的日志里出现监听 8080 端口的记录executor 的日志里出现注册成功的地址信息比如类似 xxl-job-executor-xxx:9999 的注册内容说明两个组件通信正常。如果 executor 日志里没有出现注册地址直接跳到第 4 章的避坑清单查 token 和 admin 地址。5.2 浏览器登录管理台确认执行器自动注册验证阶段最简单的暴露方式是用 port-forward不用改 Service 类型kubectl port-forward -n xxl-job svc/xxl-job-admin 8080:8080浏览器打开 http://localhost:8080/xxl-job-admin用镜像自带的默认管理员账号 admin / 123456 登录首次登录后记得改密码。在左侧菜单进入“执行器管理”正常情况下能看到一个名为 xxl-job-executor 的执行器注册地址列显示的是 Pod IP 加 9999 端口。这里要确认地址不是空也不是 localhost而是 10.x.x.x:9999 这种形式。5.3 创建一个真实任务验证调度闭环最后一步是创建一个最简单的任务确认调度中心能下发指令、执行器能执行并回传结果。进入“任务管理”新增任务执行器选 xxl-job-executorJobHandler 填 demoJobHandler这是镜像自带的示例处理器Cron 填 0/30 * * * * ?运行模式选 BEAN启动状态选“启动”保存后回到任务列表勾选该任务点击“执行一次”然后点“调度日志”。日志里能看到调度成功、执行成功的记录点进去看执行详情能看到完整的执行链路。如果你用的是自己开发的执行器JobHandler 就填代码里 XxlJob 注解对应的 value。这套验证走完调度中心、元数据库、执行器、任务分发整条链路全部打通。从那以后我每次在集群里部署 XXL-JOB都会强制自己走一遍从建库、导表、核对 token、看注册地址到跑一个真实任务的完整流程一步不跳。部署本身不难难的是把每个坑的位置提前标出来。希望这篇记录能帮你在集群里少踩几个 XXL-JOB 的暗坑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑