本文记录我在个人 VM 上部署 Argo Workflows 的过程,是一份学习笔记。环境是两台 VirtualBox 虚拟机:102 运行 Harbor 和 PostgreSQL,118 运行 Kubernetes 集群。所有操作和命令均来自实际执行记录。
一、环境与架构
两台 VM,同一 Host-Only 网段 192.168.56.0/24:
┌─────────────────────────┐ ┌──────────────────────────────┐
│ 102: Harbor + PG │ │ 118: K8s v1.33.6 (sealos) │
│ │ │ │
│ ┌─────────────────┐ │ │ namespace: argo │
│ │ Harbor :80 │◄───┼───────┤ ┌────────────────────────┐ │
│ │ 镜像仓库 │ │ pull │ │ argo-workflows-server │ │
│ └─────────────────┘ │ │ │ (ClusterIP :2746) │ │
│ │ │ └───────────┬────────────┘ │
│ ┌─────────────────┐ │ │ │ │
│ │ PostgreSQL :5432 │◄───┼───────┤ ┌───────────▼────────────┐ │
│ │ (argo DB) │ │ 外部DB│ │ argo-workflows-ctrl │ │
│ └─────────────────┘ │ │ └────────────────────────┘ │
└─────────────────────────┘ │ │
│ namespace: traefik │
│ ┌────────────────────────┐ │
│ │ traefik (NodePort) │ │
│ │ 80→31112 443→31248 │ │
│ └───────────┬────────────┘ │
│ │ │
│ Ingress: argo118.local │
│ → argo-workflows-server │
└──────────────────────────────┘
- Kubernetes:sealos 部署的单节点集群 v1.33.6,containerd 运行时
- Harbor(102):私有镜像仓库,我把 Argo 和 Traefik 的镜像先推到这儿,集群再从 102 拉
- PostgreSQL(102):Argo 用它存工作流历史,102 上刚好有个 PG 容器在跑,直接复用
- Traefik:Ingress Controller,负责把 http 请求转到 Argo Server
- Argo Workflows:CNCF 工作流引擎,Helm Chart 部署
二、镜像准备
Argo 需要 4 个镜像,我先在能拉 quay.io 的机器上做 docker tag + push 到 102:
bash
# 1. CLI 镜像(argo server 用)
docker tag quay.io/argoproj/argocli:latest \
192.168.56.102/library/quay.io/argoproj/argocli:latest
docker push 192.168.56.102/library/quay.io/argoproj/argocli:latest
# 2. Executor 镜像(工作流 Pod 用)
docker tag quay.io/argoproj/argoexec:latest \
192.168.56.102/library/quay.io/argoproj/argoexec:latest
docker push 192.168.56.102/library/quay.io/argoproj/argoexec:latest
# 3. Controller 镜像
docker tag quay.io/argoproj/workflow-controller:latest \
192.168.56.102/library/quay.io/argoproj/workflow-controller:latest
docker push 192.168.56.102/library/quay.io/argoproj/workflow-controller:latest
# 4. kubectl 镜像(CRD upgrade job 用)
docker pull registry.k8s.io/kubectl:latest
docker tag registry.k8s.io/kubectl:latest \
192.168.56.102/library/registry.k8s.io/kubectl:latest
docker push 192.168.56.102/library/registry.k8s.io/kubectl:latest
tag 的命名我保留了原始路径(quay.io/argoproj/),这样一眼就能看出镜像来源,后面升级也方便找。
三、数据库准备
102 上已经跑着一个 PostgreSQL 容器(pgvector 镜像),直接给它建个 argo 库就行:
bash
docker run --name pgvector-pg18 \
-e POSTGRES_PASSWORD=123456 \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=argo \
-p 5432:5432 \
--restart=always \
-d pgvector/pgvector:pg18-trixie
在 k8s 侧建个 Secret 存数据库凭据:
bash
kubectl create namespace argo
kubectl create secret generic argo-postgres-config \
-n argo \
--from-literal=username=postgres \
--from-literal=password=123456
四、安装 Argo Workflows
4.1 下载 Chart
bash
wget https://github.com/argoproj/argo-helm/releases/download/argo-workflows-1.0.14/argo-workflows-1.0.14.tgz
tar xzf argo-workflows-1.0.14.tgz
解压后是个 argo-workflows/ 目录,用本地路径安装,不依赖 Helm repo。
4.2 手动装 CRD
一开始让 Helm 自己的 CRD install job 跑,但发现那个 Job 偶尔不稳。后来注意到解压出来的 chart 目录里 files/crds/minimal/ 就有现成的 YAML,一行命令直接 apply 更方便:
bash
cd argo-workflows/files/crds/minimal/
kubectl apply --server-side -f .
里面 8 个 CRD:Workflow、CronWorkflow、WorkflowTemplate、ClusterWorkflowTemplate 等。
4.3 Values 配置
yaml
# argo-workflows-values.yaml
usePostgresql: false # 不用 Helm 内建的 PG
persistence:
archive: true # 启用工作流归档
postgresql:
host: 192.168.56.102 # 连 102 上的 PG
port: 5432
database: argo
tableName: argo_workflows
userNameSecret:
name: argo-postgres-config
key: username
passwordSecret:
name: argo-postgres-config
key: password
crds:
install: false # 前面手动 apply 过了,关掉 Helm 的 CRD Job
upgradeJob:
image:
repository: 192.168.56.102/library/registry.k8s.io/kubectl
controller:
image:
registry: 192.168.56.102/library/quay.io
tag: latest
executor:
image:
registry: 192.168.56.102/library/quay.io
tag: latest
server:
image:
registry: 192.168.56.102/library/quay.io
tag: latest
所有 image registry 都指向 102 的 Harbor。
4.4 安装
bash
helm install argo-workflows ./argo-workflows \
--namespace argo \
--values argo-workflows-values.yaml
4.5 验证
bash
kubectl get pods -n argo
# NAME READY STATUS RESTARTS AGE
# argo-workflows-server-6b97579665-tnm9n 1/1 Running 0 3m
# argo-workflows-workflow-controller-7798db698f-9bkmq 1/1 Running 0 3m
五、Traefik 接入:一个 LoadBalancer 引发的排查
有了 Argo,还需要从浏览器访问它的 Web UI。选 Traefik 做 Ingress Controller。
5.1 初始配置
yaml
# traefik-values.yaml
service:
type: NodePort # 裸金属环境,用 NodePort 暴露
image:
registry: 192.168.56.102/library
repository: traefik
tag: v3.6.7
bash
helm install traefik traefik/traefik \
--namespace traefik \
--values traefik-values.yaml
5.2 不对劲
检查 Service 状态:
bash
kubectl get svc -n traefik
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# traefik LoadBalancer 10.99.241.28 <pending> 80:31112/TCP,443:31248/TCP 2m
TYPE 显示 LoadBalancer,EXTERNAL-IP 永远是 <pending>。 可我 values 里明明写的 NodePort。
5.3 排查过程
第一反应------是不是缺 MetalLB?不对,values 本身就指定了 NodePort,没有 LoadBalancer 控制器不应该影响 Service type 的判断。
用 helm template 渲染一下看看 chart 到底生成了什么:
bash
helm template traefik traefik/traefik \
--namespace traefik \
--values traefik-values.yaml | grep -A10 "kind: Service"
输出:
yaml
kind: Service
# ...
spec:
type: LoadBalancer # ← 还是 LoadBalancer!
values 文件没有生效。 说明 key 路径不对。
5.4 定位根因
直接看 chart 的默认 values:
bash
helm show values traefik/traefik | grep -B15 "type: LoadBalancer"
关键片段:
yaml
service:
enabled: true
spec: # ← 注意这一层
type: LoadBalancer
Traefik chart 40.2.0 的 service type 配置路径是 service.spec.type,不是 service.type。 老版本 chart(34.x 之前)用的是 service.type,新版本把 Service 相关配置收敛到了 service.spec 下。
5.5 修复
diff
service:
- type: NodePort
+ spec:
+ type: NodePort
image:
registry: 192.168.56.102/library
bash
helm upgrade traefik traefik/traefik \
-n traefik \
--values traefik-values.yaml
再检查:
bash
kubectl get svc -n traefik
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# traefik NodePort 10.99.241.28 <none> 80:31112/TCP,443:31248/TCP 10s
5.6 配置 Ingress
Traefik 就绪后,创建 Ingress 把外部请求转到 Argo Server:
yaml
# argo-workflows-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argo-workflows-ingress
namespace: argo
annotations:
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/router.entrypoints: "web"
spec:
rules:
- host: argo118.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argo-workflows-server
port:
number: 2746
bash
kubectl apply -f argo-workflows-ingress.yaml
本机 /etc/hosts 加上:
192.168.56.118 argo118.local
浏览器访问 http://argo118.local:31112,看到 Argo UI。
5.7 获取登录 Token
Argo Server 默认开启了 server 认证模式,打开 UI 后会先看到一个登录页要求填 Bearer Token。Token 就在集群里,一行命令取出来:
bash
TOKEN="Bearer $(kubectl get secret argo-workflows-server-token \
-n argo -o jsonpath='{.data.token}' | base64 --decode)"
echo $TOKEN
把输出的 Token 粘贴到登录页的输入框,点 Login 就进去了。这个 Secret 是 Helm 部署时自动为 argo-workflows-server ServiceAccount 创建的,不需要手动建。
六、Demo 验证
下面用
argo submit提交工作流,这个命令走的是 kubeconfig 直连 kube-apiserver,不需要上面那个 Token。
装个 Argo CLI:
bash
curl -sLO https://github.com/argoproj/argo-workflows/releases/latest/download/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
sudo mv argo-linux-amd64 /usr/local/bin/argo
提一个 hello-world 工作流试试:
bash
argo submit -n argo --watch - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-world-
spec:
entrypoint: main
templates:
- name: main
container:
image: alpine:latest
command: [echo]
args: ["Hello Argo Workflows!"]
EOF
整个链路:argo submit → controller 调度 → Pod 执行 → echo 输出 → PG 归档 → UI 可查。打开 Argo UI 能看到执行历史和日志,说明全链路通了。
七、踩坑记录 & 下一步
7.1 Ingress YAML 写错了
回看实际部署的 Ingress,host 和 http 被写成了两个独立的 rule:
yaml
spec:
rules:
- host: argo118.local # rule 1: 只有 host,无 http
- http: # rule 2: 只有 http,无 host(匹配所有)
paths:
- ...
应该是合并成一个 rule 才对。虽然目前能访问(第二个 rule 匹配所有 host 兜底了),但后面得改回来。
7.2 serversscheme: https 可能多余
Argo Server 默认在 2746 跑的是 HTTP。我的 Ingress 里加了 traefik.ingress.kubernetes.io/service.serversscheme: "https",这会让 Traefik 尝试用 HTTPS 连后端。目前没出问题,但如果遇到 502 就先排查这个 annotation。
7.3 接下来可以试试
- 配一下 Argo UI 的登录认证(目前裸奔)
- 把镜像 tag 从
latest固定到具体版本号 - 用 Traefik 原生的
IngressRouteCRD 替代标准 Ingress - 写个带步骤依赖的 DAG 工作流跑跑看
操作记录于 2026-06-07 | sealos k8s v1.33.6 + Harbor v2.14.2 + Traefik v3.6.7