简介:这份资源面向需要在 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/jobhandlerxxl.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-jobkubectl 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: 5initialDelaySeconds给 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 省掉的是写配置的时间,省不掉的是对依赖顺序和注册链路的理解。希望帮到你。
本文还有配套的精品资源,点击获取