news 2026/10/8 23:51:34

XXL-JOB 容器化部署实战:K8S 集群 YAML 配置与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XXL-JOB 容器化部署实战:K8S 集群 YAML 配置与避坑指南

简介:这份资源面向需要在 Kubernetes 集群中落地 XXLJOB 的运维与后端开发人员,提供一份经过实际部署验证的 YAML 清单,解决容器化环境下任务调度平台快速搭建与配置落地的问题。压缩包内共 1 个文件,为单个 yaml 类型清单,包体仅 782B,体量轻巧,可直接通过 kubectl 应用完成部署,省去手工编写与反复调试的环节。目前已有 646 人学习下载,说明该部署方式在社区中具备一定参考价值。读者可借助这份验证版清单,快速理解 XXLJOB 在 K8S 中的资源对象组织方式,包括调度中心与执行器相关配置的编排思路,并以此为模板调整镜像、副本数、环境变量与端口等参数,适配自身集群环境。对于正在推进 XXLJOB 容器化部署、希望减少试错成本的团队而言,这份文件可作为可直接复用的部署起点,帮助缩短从零搭建到验证成功的时间。

1. 从一次调度中心迁移说起:XXL-JOB 上 K8S 到底值不值

去年底帮一个团队把跑了三年的 XXL-JOB 从三台物理机搬到 K8S,起因很朴素——调度中心单点、执行器扩容靠手工改配置、日志散在六台机器上没人愿意翻。迁移前他们最担心的是「调度中心这种有状态服务塞进容器会不会天天翻车」,结果跑下来最麻烦的既不是数据库也不是注册中心,而是 YAML 里几个默认值没改导致的执行器注册不上。这篇笔记就围绕一份能直接kubectl apply的 XXL-JOB K8S 集群部署 YAML 展开,把调度中心、执行器、MySQL、网络暴露这几块拆开讲清楚,顺带把 xxljob 容器化部署里那些玄学问题摊开说。适合正在做 k8s 学习、准备把定时任务平台容器化的后端和运维同学,也适合已经踩过坑想找一份可对照配置的人。

这份资源的核心价值在于「验证版」三个字:它不是一份理论模板,而是把调度中心xxl-job-admin、执行器xxl-job-executor、依赖的 MySQL 初始化、Service/Ingress 暴露、健康探针、资源限制这些一次性配齐,改掉镜像地址和数据库密码就能起。下面按「资源里有什么 → 怎么一步步部署 → 参数怎么调 → 坑在哪 → 怎么验证和进阶」的顺序推。

2. 拆开这份 YAML:调度中心、执行器与 MySQL 的编排逻辑

2.1 为什么调度中心要单独拆一个 Deployment

XXL-JOB 的架构里,xxl-job-admin是调度中心,负责触发任务、管理执行器注册、提供 Web 控制台;执行器是业务侧真正跑 job 的进程。两者职责完全不同,扩缩容策略也不一样:调度中心通常 1~2 副本做高可用,执行器按业务量横向扩。所以这份 YAML 把它们拆成两个独立的 Deployment,而不是塞进一个 Pod。

调度中心本身是无状态的(状态都在 MySQL 里),所以用 Deployment 而不是 StatefulSet 是合理的。常见做法是给它配 2 个副本,前面挂一个 Service,控制台通过 Ingress 暴露。这里有个容易忽略的点:多个 admin 副本之间不需要互相通信,它们共享同一个数据库,靠数据库行锁保证同一任务不被重复触发,所以副本数不用太多,2 个足够覆盖滚动更新时的可用性。

执行器这边,注册方式决定了 YAML 怎么写。XXL-JOB 支持自动注册和手动录入两种,容器化场景下推荐自动注册——执行器启动时把自身地址上报给调度中心。但容器 IP 是会变的,所以执行器注册时上报的地址必须是 K8S 内部能解析的地址,通常是Pod IP:端口或Service 名:端口。这份 YAML 用的是执行器 Deployment + Headless Service 的组合,让调度中心能通过稳定的 DNS 找到执行器。

2.2 数据库初始化与连接配置

XXL-JOB 依赖 MySQL 存任务、日志、执行器注册信息。这份 YAML 里 MySQL 用的是一个带初始化脚本的部署方式,首次启动时执行官方tables_xxl_job.sql建表。关键配置在调度中心的application.properties里,通过环境变量注入:

# xxl-job-admin Deployment 片段 env: - name: PARAMS value: >- --spring.datasource.url=jdbc:mysql://xxl-job-mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=xxljob123 --xxl.job.accessToken=default_token

这段PARAMS会作为启动参数传给 Spring Boot,覆盖镜像里的默认配置。spring.datasource.url里的serverTimezone=Asia/Shanghai必须带,否则日志时间会差 8 小时,排查任务执行时间时会被带偏。xxl.job.accessToken是调度中心和执行器之间的通信凭证,两边必须一致,不一致的表现是执行器注册成功但触发任务时报 401。

MySQL 的 Service 名要和 URL 里的主机名对上,这份 YAML 里统一叫xxl-job-mysql。如果你用外部 MySQL,把这段 URL 改成外部地址,同时把 YAML 里的 MySQL Deployment 整段删掉即可,不用改其他部分。

2.3 执行器注册与网络暴露

执行器 Deployment 的关键在于注册地址和端口。XXL-JOB 执行器默认端口 9999,注册时上报的地址由xxl.job.executor.address决定,不配的话它会用本机网卡 IP,在容器里就是 Pod IP。Pod IP 在 K8S 集群内可直达,所以调度中心能连上,但 Pod 重建后 IP 变,注册信息会短暂失效,等执行器重新注册就好。

# xxl-job-executor Deployment 片段 env: - name: PARAMS value: >- --xxl.job.admin.addresses=http://xxl-job-admin:8080/xxl-job-admin --xxl.job.accessToken=default_token --xxl.job.executor.appname=xxl-job-executor-sample --xxl.job.executor.port=9999 --xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler

xxl.job.admin.addresses指向调度中心的 Service,注意路径要带/xxl-job-admin,这是控制台的 context-path,漏了会注册失败。xxl.job.executor.logpath指向容器内路径,这个路径必须挂 PVC 或者至少挂 emptyDir,否则 Pod 重启日志全丢——xxljob 日志如何检索这个问题,根源就在日志没持久化。

网络暴露分两层:集群内用 Service,集群外用 Ingress。调度中心需要暴露 Web 控制台给运维用,执行器不需要对外暴露,只要调度中心能访问即可。这份 YAML 里 Ingress 只配了 admin 的规则,执行器没有 Ingress,这是对的。

3. 从零到跑通:YAML 部署的完整操作链路

3.1 前置检查与命名空间准备

动手前先确认集群版本和存储。XXL-JOB 对 K8S 版本不挑,1.20 以上都能跑,但要注意apps/v1的 Deployment 写法在 1.16 之后才稳定。存储方面,MySQL 需要 PVC,如果你的集群没有默认 StorageClass,得先建一个,否则 PVC 会一直 Pending。

# 确认集群可用与默认 StorageClass kubectl version --short kubectl get storageclass # 建独立命名空间,别塞 default kubectl create namespace xxl-job # 确认节点资源,调度中心+执行器+MySQL 至少留 2C4G kubectl top nodes

命名空间单独建是为了隔离,XXL-JOB 的 Service 名比较通用(xxl-job-admin、xxl-job-mysql),放 default 里容易和别的服务撞名。kubectl top nodes看的是节点剩余资源,如果节点已经很满,MySQL 的 PVC 挂载可能因为资源不足卡住。

3.2 按依赖顺序 apply,别一把梭

这份 YAML 如果是一个多文档文件,直接kubectl apply -f xxl-job-k8s.yaml -n xxl-job也能跑,但依赖顺序不对时调度中心会先起来然后连不上数据库反复重启。稳妥做法是按 MySQL → 调度中心 → 执行器的顺序分批 apply。

# 第一步:MySQL 与初始化 kubectl apply -f 01-mysql.yaml -n xxl-job kubectl wait --for=condition=ready pod -l app=xxl-job-mysql -n xxl-job --timeout=180s # 第二步:调度中心 kubectl apply -f 02-admin.yaml -n xxl-job kubectl rollout status deployment/xxl-job-admin -n xxl-job # 第三步:执行器 kubectl apply -f 03-executor.yaml -n xxl-job kubectl rollout status deployment/xxl-job-executor -n xxl-job

kubectl wait那行是等 MySQL Pod ready,--timeout=180s给足初始化建表时间。如果 MySQL 首次启动要拉镜像加建表,180 秒通常够,网络慢就调到 300。rollout status会阻塞到 Deployment 完成滚动,能第一时间发现镜像拉取失败或探针不过。

3.3 验证调度中心与执行器注册

三步都 apply 完,先看 Pod 状态,再进控制台确认执行器上线。

# 看 Pod 和 Service kubectl get pods,svc -n xxl-job # 看调度中心日志,确认数据库连接成功 kubectl logs -l app=xxl-job-admin -n xxl-job --tail=50 | grep -i "started\|error" # 端口转发到本地,打开控制台 kubectl port-forward svc/xxl-job-admin 8080:8080 -n xxl-job

浏览器打开http://localhost:8080/xxl-job-admin,默认账号admin/123456。进「执行器管理」看xxl-job-executor-sample是否在线。如果显示离线,先看执行器日志里有没有registry success,再看调度中心日志有没有收到注册请求。这一步是 xxljob 容器化部署最容易卡的地方,下面避坑章节细说。

3.4 跑一个示例任务验证全链路

执行器在线后,新建一个 GLUE 模式的任务,Cron 写0/30 * * * * ?,运行模式选 BEAN,JobHandler 填demoJobHandler(示例执行器自带),保存后点执行一次。看调度日志里「调度成功」和「执行成功」两条记录,再点「执行日志」看输出。

# 同时观察执行器日志,确认任务真的跑了 kubectl logs -l app=xxl-job-executor -n xxl-job -f --tail=20

如果调度日志显示成功但执行日志为空,多半是执行器日志路径没挂载,或者 JobHandler 名字对不上。示例执行器里demoJobHandler是内置的,自己写的执行器要确认@XxlJob("yourHandler")注解里的名字和页面上填的一致。

4. 参数调优与常见配置误区

4.1 资源限制与 JVM 参数

容器里跑 Java 服务,最容易翻车的是 JVM 不认 cgroup 限制,堆开到宿主机内存大小然后被 OOM Kill。这份 YAML 里给调度中心和执行器都配了resources.limits和requests,同时通过JAVA_OPTS显式限制堆。

resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "1000m" env: - name: JAVA_OPTS value: "-Xms512m -Xmx768m -XX:+UseG1GC"

-Xmx768m比 limit 的 1Gi 小,留出堆外内存和元空间。如果只配 limit 不配-Xmx,JDK 8 在某些基础镜像里会按宿主机内存算默认堆,直接超 limit 被杀。requests给 512Mi 是让调度器有依据地安排节点,给太小会导致节点内存碎片化。

4.2 健康探针的路径与时机

调度中心是 Spring Boot 应用,启动要连数据库、初始化线程池,冷启动可能 30 秒以上。探针配太激进会导致 Pod 反复重启,看起来像「起不来」。

livenessProbe: httpGet: path: /xxl-job-admin/actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /xxl-job-admin/actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 5

initialDelaySeconds给 60 秒是血泪经验,给 10 秒的话数据库稍慢就重启循环。注意 actuator 端点需要镜像里包含spring-boot-starter-actuator,如果镜像没带,把探针改成 TCP 检查port: 8080也能用,只是不够精确。

4.3 时区与日志持久化

前面提过serverTimezone,这里补一个容器时区。基础镜像默认 UTC,调度中心显示的触发时间会比北京时间少 8 小时,看 Cron 执行记录时非常迷惑。挂载宿主机时区或设TZ环境变量都行。

env: - name: TZ value: "Asia/Shanghai" volumeMounts: - name: executor-logs mountPath: /data/applogs volumes: - name: executor-logs persistentVolumeClaim: claimName: xxl-job-executor-logs

执行器日志挂 PVC 是为了后续检索,xxljob 日志如何检索的答案就是「先持久化,再用 kubectl exec 或日志采集工具捞」。不挂的话 Pod 一重建日志就没了,排查历史任务只能靠数据库里的调度记录,看不到业务输出。

5. 避坑排查:执行器注册不上与调度失败的六种情况

5.1 执行器显示离线,日志无注册记录

现象:控制台执行器列表里xxl-job-executor-sample一直离线,执行器 Pod 日志里没有registry success。 原因:xxl.job.admin.addresses配错,最常见是漏了/xxl-job-admin路径,或者 Service 名和实际不一致。 解决:进执行器 Pod 里curl http://xxl-job-admin:8080/xxl-job-admin看能否返回,返回 404 就是路径问题,Connection refused 就是 Service 名或端口问题。

5.2 注册成功但触发任务报 401

现象:执行器在线,手动执行任务时调度日志显示「调度失败」,调度中心日志有 401。 原因:调度中心和执行器的xxl.job.accessToken不一致。 解决:两边都检查PARAMS里的accessToken,改成同一个值后重启执行器。注意 token 不要带特殊字符,YAML 里&之类需要转义。

5.3 调度成功但执行日志为空

现象:调度日志两条都成功,点执行日志没内容。 原因:执行器logpath指向的目录没写权限或没挂载,日志写不进去。 解决:确认volumeMounts的mountPath和logpath一致,PVC 权限用kubectl exec进去touch一个文件测试。用 emptyDir 的话重启就丢,生产必须 PVC。

5.4 Pod 反复重启,探针失败

现象:kubectl get pods看到 RESTARTS 一直涨,describe 里 Liveness probe failed。 原因:initialDelaySeconds太短,应用还没起来探针就判死。 解决:调到 60 秒以上,或者临时把 liveness 去掉只留 readiness,确认能稳定启动后再加回来。

5.5 MySQL 连接超时,调度中心起不来

现象:调度中心日志报Communications link failure。 原因:MySQL Pod 还没 ready 调度中心就启动了,或者 Service 名对不上。 解决:按 3.2 的顺序 apply,用kubectl wait等 MySQL ready。如果 MySQL 在外部,检查网络策略和防火墙。

5.6 执行器扩容后任务重复执行

现象:执行器从 1 副本扩到 3 副本,同一个任务被触发三次。 原因:XXL-JOB 的路由策略默认是「第一个」或「轮询」,扩副本后每个执行器都注册了,调度中心按策略分发。如果业务任务不支持并发,会重复跑。 解决:在任务配置里把路由策略改成「分片广播」或「一致性 HASH」,并在执行器里用XxlJobHelper做幂等。扩副本前先确认任务是否可并发。

6. 进阶:用 Ingress 暴露控制台与日志检索的落地技巧

控制台默认用 port-forward 看,生产环境要走 Ingress。这份 YAML 里 Ingress 规则大致是这样,注意pathType和 rewrite 的配合:

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: xxl-job-admin namespace: xxl-job annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: xxl-job.example.com http: paths: - path: /xxl-job-admin pathType: Prefix backend: service: name: xxl-job-admin port: number: 8080

这里有个细节:XXL-JOB 控制台的 context-path 是/xxl-job-admin,Ingress 的 path 也是它,所以不要加rewrite-target把它重写掉,否则页面里的静态资源路径会 404。我一般会去掉 rewrite 注解,让路径原样透传。如果非要用子域名根路径访问,得在应用侧改server.servlet.context-path,比改 Ingress 麻烦。

日志检索这块,执行器日志落到 PVC 后,临时排查用kubectl exec进 Pod 直接 grep:

# 进执行器 Pod 按任务日志 ID 检索 kubectl exec -it deploy/xxl-job-executor -n xxl-job -- \ grep -r "jobLogId" /data/applogs/xxl-job/jobhandler/2025-01-01/

长期方案是接日志采集,把/data/applogs这个目录用 sidecar 或 DaemonSet 采集到集中存储。注意 XXL-JOB 的日志按日期分目录、按 jobLogId 分文件,采集时保留目录结构,否则检索时对不上调度记录里的日志 ID。我习惯在任务里用XxlJobHelper.log()输出关键上下文,这样日志文件里既有业务输出也有框架的调度信息,排查时不用两头翻。

从那以后我每次部署 XXL-JOB 到新集群,都强制走一遍「MySQL ready → admin 起来 → executor 注册 → 跑一个 demo 任务」这四步,不跳步。这套 YAML 省掉的是写配置的时间,省不掉的是对依赖顺序和注册链路的理解。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 23:49:05

HTML5 Input类型全解析:表单校验、移动端适配与兼容性实战

最近在做一个后台管理系统改造,表单这块让我花了不少时间。项目里既有老的登录注册页,也有新增的数据报表筛选区,各种输入需求混在一起,HTML5 新增的 Input 类型确实帮了大忙,但用不好也会给你挖坑。我从 HTML5 刚普及…

作者头像 李华
网站建设 2026/10/8 23:45:50

A.每日一题:1190. 反转每对括号间的子串

题目链接:1190. 反转每对括号间的子串(中等) 算法原理: 解法一:栈 时间复杂度O(N) 2ms击败65.24% 我们用 StringBuilder 记录当前括号内部字符 ①遇到 (:将当前 StringBuilder 内容压入栈,清空它…

作者头像 李华
网站建设 2026/10/8 23:44:24

GC5845/GC5849替代A4945的BLDC无感正弦波驱动实战指南

做电机驱动的工程师这两年都懂一件事:不管你是做风机、水泵、电动工具还是汽车电子,只要原设计里用了进口的BLDC驱动芯片,迟早会碰上"一颗料卡住整个产线"的情况。我也一样,手头一个量产项目原本用的Allegro A4945&…

作者头像 李华
网站建设 2026/10/8 23:39:49

AI Agent技能化改造:从Prompt到可复用Skills技能包

如果你第一眼看到skills这个项目名,第一反应还是简历上的“特长与技能”,那你可能要在 AI Agent 开发语境里换个角度理解它。最近大半年,我一直在折腾 Agent 的技能化改造,skills这个词在工程圈里已经成了一个相当具体的概念&…

作者头像 李华
网站建设 2026/10/8 23:38:47

AI日报制作全流程拆解:从信息源分层到筛选写作的工程实践

1. 当日期成为标题的一部分:这份日报到底在记录什么看到"2026-09-26 AI最新资讯日报"这个标题,很多人的第一反应可能是:这不就是个新闻聚合吗?有什么好拆解的。但如果你真的在AI行业待过一段时间,就会明白一…

作者头像 李华