用 KEDA 按排队扩容 Worker,并用 GitLab 与 Argo CD 纳管工作负载
目录
本文记录这一阶段在现网上的做法:安装 KEDA 2.21.0,让 Dify 的 worker 在 vLLM 排队超过 10 时扩到最多 3 个副本;安装 Argo CD 3.5.4,把 llm、dify、observability 的工作负载收进 GitLab CE 的私有仓库,由 Application 自动同步。本章目录是 cicd。机器是 prod102,地址 10.0.1.47。集群、探针、GPU Operator 和 Vault 写在同级目录 k8s-从零部署、k8s-迁移、k8s-探针与密钥。流程对照页是同目录的《走一遍 GitOps:KEDA、GitLab 和 Argo CD.html》。
scripts/10、scripts/12、scripts/13 发现现网已经装过就会停住。不要在这台机器上再执行一遍。scripts/11 会短时间把 worker 扩到 3 再缩回 1,复习时看记录即可。
登录口令沿用现网值。Vault 根令牌、Langfuse 的 NEXTAUTH_SECRET、SALT、ENCRYPTION_KEY、Redis 口令、MinIO 口令、kubeadm 加入令牌,以及 Argo CD 仓库 Secret 里的个人访问令牌,不写进本文。不要把本文发到公网。
一、这三样各自做什么
请求路径没有变:浏览器打开 Dify,对话从 Dify 到 New-API,再到 vLLM 的 qwen3.6-27b。这一阶段在这条路径旁边加了两层控制。
1. KEDA
KEDA 按外部指标修改 Deployment 的副本数。本机的指标来自 Prometheus,查询是:
sum(vllm:num_requests_waiting{model_name="qwen3.6-27b"})Prometheus 地址是 http://prometheus.observability.svc.cluster.local:9090。threshold 和 activationThreshold 都是 "10"。排队人数小于或等于 10 时触发器不激活,dify/worker 保持 1 个副本。超过 10 之后,期望副本是排队人数除以 10 再向上取整,上限 3。伸缩对象是 worker,不包括 vLLM,也不包括 worker-beat。
KEDA 会创建 HPA keda-hpa-dify-worker。副本下限已经是 1,又没有空闲副本,KEDA 提示 pollingInterval 和 cooldownPeriod 不参与伸缩,改副本数的是这条 HPA。
2. Argo CD
Argo CD 把 Git 仓库里的 YAML 当成集群的期望状态。Application platform 盯着仓库的 manifests 目录,发现差异就提交到集群。selfHeal 为 true,有人直接改集群时会拉回 Git 上的内容。prune 为 false,Git 里没有的对象不会被删掉。
dify/worker 的 /spec/replicas 写在 ignoreDifferences 里。KEDA 把副本改成 2 或 3 时,Argo CD 不会把它改回 Git 里的 1。
3. GitLab CE
Git 仓库放在 GitLab Community Edition 19.4.2。这是 GitLab 的开源版本。网页是 http://10.0.1.47:8929,登录 root / R3po-Argo-2026。项目 gitops/platform 是私有的,默认分支 main。
Argo CD 在集群内使用:
http://gitlab.gitlab.svc.cluster.local:8929/gitops/platform.git拉仓库的用户名是 oauth2,口令是个人访问令牌,放在 Secret argocd/gitops-repo。令牌本身不写进本文。
二、动手前现网是什么样
vLLM、Dify、监控已经在跑,环境变量已经由 Vault 和 External Secrets 同步。节点可分配的 nvidia.com/gpu 是 1。vLLM、Xinference 和宿主机 Python 都不申请这个资源。
写 ScaledObject 和导出清单时,没有改 vLLM 镜像,没有给 vLLM 加上 nvidia.com/gpu,也没有重启 New-API。
三、安装 KEDA
Helm release 名 keda,命名空间 keda,chart 版本 2.21.0。三个镜像都在 ghcr.dockerproxy.net/kedacore,标签 2.21.0,拉取策略 IfNotPresent。安装时三个 Pod 各重启过一次,随后保持 1/1。
config/scaledobject-dify-worker.yaml 就是现网对象。scripts/10-安装KEDA.sh 在 prod102 上发现 release 已存在会退出。
四、模拟一次排队
scripts/11-模拟排队扩容.sh 把查询临时写成 vector(25),并把 HPA 的扩容、缩容稳定窗口设为 0。worker 从 1 个就绪变成 3 个就绪。HPA 先显示 25/10,三个副本就绪后显示约 8334m/10。脚本退出时把查询改回真实的 sum(),副本回到 1。vLLM 的 /health 保持 200。
现网查询已经是真实的 sum()。不要把 vector(25) 留在 ScaledObject 上。
五、安装 Argo CD
版本 v3.5.4。安装对象都在 config/argocd-install.yaml,这是官方 install.yaml 换过镜像之后的同一份,大约 3.4 万行,CRD 占了绝大部分。提交时用 kubectl apply --server-side --force-conflicts,因为客户端 apply 会在 applicationsets.argoproj.io 的注解长度上失败。镜像:
| 组件 | 镜像 |
|---|---|
| argocd | m.daocloud.io/quay.io/argoproj/argocd:v3.5.4 |
| dex | ghcr.dockerproxy.net/dexidp/dex:v2.45.1 |
| redis | docker.m.daocloud.io/redis:8.2.3-alpine |
上游清单里的 imagePullPolicy: Always 已改成 IfNotPresent。argocd-cmd-params-cm 里 server.insecure 为 "true"。argocd-cm 的调和超时是 30 秒,对外地址是 http://10.0.1.47:8080,管理员账号允许登录。
scripts/12-部署ArgoCD.sh 看到 argocd-server 已在就会退出。
网页不直接暴露 argocd-server。config/argocd-ui-proxy.yaml 用 nginx 把节点 8080 转到 argocd-server.argocd.svc.cluster.local:80。转发的头是 Host、X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Host。Deployment 策略是 Recreate,同一时间只有一个 Pod 占用 8080。
登录:http://10.0.1.47:8080 ,admin / Argocd-git-2026。这组账号的会话接口返回 200,浏览器也能进入 Applications。
六、把现有工作负载收进 Git
导出程序是 scripts/export_gitops.py。范围是三个命名空间里的 Deployment、Service、ExternalSecret、ScaledObject。Secret 不导出。对象上的 uid、resourceVersion、managedFields 和 status 会去掉。
创建 Application 之前,用 dry-run 对比过每个 Deployment 的 Pod 模板。模板一致,第一次同步没有新建 Pod。vLLM 的 Pod 仍是 vllm-67b8d8c9f6-vvfsq,uid 26326358-d9a7-4eec-8b13-df4d800d2ae5。
演示提交只改了导航页模板注解 gitops.local/demo=nav-1。硬刷新后,导航页 Pod 带上这行注解。导航页策略是 Recreate,所以换的是导航页自己的 Pod。vLLM 没有换。/health 仍是 200。
作业里更换 vLLM 镜像那一步没有做。那两个标签和现网镜像对不上,改镜像会重启正在服务的模型。
scripts/13-纳管GitOps.sh 看到 Application platform 已在就会退出。现网不要再跑它。
config/argocd-app-platform.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: platform
namespace: argocd
spec:
project: default
source:
repoURL: http://gitlab.gitlab.svc.cluster.local:8929/gitops/platform.git
targetRevision: main
path: manifests
directory:
recurse: true
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: false
selfHeal: true
syncOptions:
- ApplyOutOfSyncOnly=true
- RespectIgnoreDifferences=true
ignoreDifferences:
- group: apps
kind: Deployment
name: worker
namespace: dify
jsonPointers:
- /spec/replicas七、GitLab CE
镜像 docker.m.daocloud.io/gitlab/gitlab-ce:19.4.2-ce.0。数据目录 /usr/cluster/gitlab 挂到容器的 /var/opt/gitlab、/var/log/gitlab、/etc/gitlab。节点端口 8929。容器内 SSH 不对节点发布。捆绑 Prometheus 关闭。内存请求 4Gi,上限 8Gi。/dev/shm 用 512Mi 的内存卷。
/-/readiness 在这版、并且关闭捆绑监控之后返回 404。探针使用 /users/sign_in,HTTP 200 才算就绪。更新策略是 Recreate。
GitLab 拒绝了含有产品名的初始口令。网页账号是 root / R3po-Argo-2026。浏览器已经用这组账号打开 gitops/platform。注册已关闭。
仓库 main 上有两个提交,当前修订 e07c4aa6f11d5740bc1dd0759a0003d2b2d23814。Application 指向 GitLab 之后仍是 Synced、Healthy,vLLM 的 uid 没有变。
早先的 Gitea 工作负载已经删除。数据目录 /usr/cluster/gitea 还在磁盘上,当前同步不读它。
清单在 config/gitlab.yaml。prod102 上 GitLab 已经在跑,不要再提交一份会滚动它的安装。
八、做完时的核对
| 检查 | 结果 |
|---|---|
| KEDA | keda-operator、metrics-apiserver、admission-webhooks 均为 1/1 |
| ScaledObject | Ready 为 True,Active 为 False,查询为真实 sum() |
| worker | 副本 1,就绪 1 |
| Argo CD | 各组件 1/1。platform Synced、Healthy,修订 e07c4aa |
| 仓库 | GitLab 集群内地址。登录页 HTTP 200 |
| 代理 | argocd-ui-proxy 策略 Recreate,就绪 1 |
| vLLM | uid 26326358-d9a7-4eec-8b13-df4d800d2ae5,/health 为 200 |
| Gitea 命名空间 | 已删除 |
再看一眼:
kubectl -n dify get scaledobject dify-worker
kubectl -n dify get deploy worker
kubectl -n argocd get application platform
kubectl -n gitlab get pods
kubectl -n llm get pod -l app=vllm
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/health
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8929/users/sign_in九、文件
| 文件 | 作用 |
|---|---|
config/keda-values.yaml | KEDA 2.21.0 的镜像和副本数 |
config/scaledobject-dify-worker.yaml | 现网 ScaledObject,查询必须保持真实 sum() |
scripts/10-安装KEDA.sh | 安装 KEDA 并提交 ScaledObject。release 已在时退出 |
scripts/11-模拟排队扩容.sh | 用 vector(25) 扩到 3 再缩回 1,结束时恢复真实查询 |
config/argocd-install.yaml | Argo CD 3.5.4 的安装清单。镜像、明文访问和对外地址已经写在里面 |
config/argocd-ui-proxy.yaml | 节点 8080 到 argocd-server 的反代,策略 Recreate |
config/argocd-app-platform.yaml | Application platform,仓库指向 GitLab |
config/gitlab.yaml | GitLab CE。数据在 /usr/cluster/gitlab,端口 8929 |
scripts/12-部署ArgoCD.sh | 安装 Argo CD 3.5.4。server 已在时退出 |
scripts/13-纳管GitOps.sh | 早期纳管步骤。Application 已在时退出,现网不要再跑 |
scripts/export_gitops.py | 导出 Deployment、Service、ExternalSecret、ScaledObject |
走一遍 GitOps:KEDA、GitLab 和 Argo CD.html | 按真实结果整理的流程说明 |