由于公司的CI/CD系统特别垃圾,还不如一些开源货靠谱,而我这边又有一些构建项目部署到AWS的需要,所以就在之前部署的kubeflow上动起了脑筋,它里面有集成了argo,也许可以把它用起来。

argo是个基于kubernetes的项目,包含多个组件,其中argo-workflows就是工作流引擎,有点类似airflow,它可以编排各种容器,控制工作流的执行。

kubeflow内部使用了argo-workflow作为pipeline底层的任务编排组件,换句话说,你运行的每个pipeline任务,底层都是一个workflow在跑,由argo-workflows做控制。

但是在目前的版本中,kubeflow默认并没有把argo-server等组件装上,只部署了它需要的workflow-controller。并且由于采用manifests默认安装的kubeflow的argo-workflows是采用cluster(可以理解为集群全局)模式安装,所以我们直接再用namespaced方式再部署一套argo也不太行得通,会受到kubeflow里面那个的影响。

于是我研究了一下如何直接把kubeflow内部集成的这个argo-workflows抠出来用,就形成了本文,邪门歪道,仅供参考。

准备

通过研究kubeflow pipeline下面的third_party/argo目录和argo-workflows的manifests,我们可以发现以下几点:

  1. kubeflow仅使用了argo-workflows的workflow-controller组件,并未部署argo-server等
  2. workflow-controller默认采取了cluster模式部署
  3. kubeflow使用的workflow-controller部署方式基本继承自argo-workflows本体提供的方式,对镜像也没有什么破坏性的修改

基于以上几点,我们就可以尝试直接部署argo相关的组件,来补全argo相关的功能,以便可以独立使用argo。我这里只部署argo-workflows(argo-server)和argo-events。

Argo Workflows: argo-server

这个其实相当于argo-workflows的管理界面前后端,这里如果不嫌麻烦的话,可以去argo-workflows官方的manifests下面把argo-server组件相关的部署到kubeflow namespace下。而我的做法更简单粗暴一些,在部署的时候直接改了kubeflow pipelines里面的配置,在kustomization.yaml里面,把被剔除的对上游的依赖

- ../upstream/manifests/base/argo-server
- ../upstream/manifests/base/cluster-install/argo-server-rbac

重新加回来就可以了。然后像往常一样部署整个pipeline组件,argo-server就应该一起被装上去了。

装完之后,可能还需要一些设置,我这里是直接通过istio把argo-server的界面暴露出去的,你也可以参考argo官方的ingress配置,使用nginx之类的来做这个暴露。(其实这个比用istio方便一点,不需要处理访问权限相关的问题)

对argo-server部署上面做相应的修改,以便能正确暴露,使用下面的命令进行yaml编辑:

kubectl edit deployment -n kubeflow argo-server

下面是我主要修改的部分节选,主要改了几个:

  • args里面加了--x-frame-options=因为我是通过kubeflow的istio统一暴露出去的,所以它的页面是通过iframe展示的,需要配置这个参数为空来允许这样访问。
  • args里面--secure=false是因为我是采用HTTP来访问该服务,如果你用HTTPS可以不改。
  • BASE_HREF环境变量配置成/argo/还是因为通过istio的子路由进行访问,需要加这个前缀
  • readinessProbe的httpGet scheme字段从HTTPS改为HTTP,因为我的服务用secure=false已经是HTTP服务了,这样改才能让健康检查通过。
    spec:
      containers:
      - args:
        - server
        - --x-frame-options=
        - --secure=false
        env:
        - name: BASE_HREF
          value: /argo/
        image: quay.io/argoproj/argocli:latest
        imagePullPolicy: Always
        name: argo-server
        ports:
        - containerPort: 2746
          name: web
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /
            port: 2746
            scheme: HTTP

然后给istio加个virtualservice ,把下面这坨东西apply了,注意其把/argo/rewrite成/,这样才能访问到静态资源,我也懒得研究为啥it works,又不是不能用。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  namespace: kubeflow
  name: argo
spec:
  gateways:
  - kubeflow/kubeflow-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /argo/
    rewrite:
      uri: /
    route:
    - destination:
        host:  argo-server.kubeflow.svc.cluster.local
        port:
          number: 2746
    timeout: 300s

还需要给argo-server加个AuthorizationPolicy,不然会有RBAC的报错

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: argo-server
  namespace: kubeflow
spec:
  action: ALLOW
  rules:
  - {}
  selector:
    matchLabels:
      app: argo-server

也许这时候访问/argo/还是不行,那么需要改argo-server的service配置, 以便能让istio能正确认识该用哪个协议访问下游

kubectl edit service -n kubeflow argo-server

把服务的name从web改成http-web
这时候应该就能在/argo/路径访问到argo的管理界面了。

由于它默认是用client模式进行身份认证,所以你可以exec一下生成token的命令,来获取token以便登录进去。

Argo Events

如果需要一些外部的服务(比如GitHub和GitLab的webhook)触发workflow,可以用argo-events,这个是argo的另外一个组件,可以单独部署。跟着argo-events的官方文档使用cluster模式安装即可。

给argo-events配置各种权限:

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: argo-events
  name: operate-workflow-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: operate-workflow-role
  namespace: argo-events
rules:
  - apiGroups:
      - argoproj.io
    verbs:
      - "*"
    resources:
      - workflows
      - clusterworkflowtemplates
      - workflowtemplates
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: operate-workflow-role-binding
  namespace: argo-events
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: operate-workflow-role
subjects:
  - kind: ServiceAccount
    name: operate-workflow-sa
    namespace: argo-events

使用

通过上述的折腾,应该是基本能用了,但还有一些限制要知道。对于kubeflow的用户namespace,因为上面有istio-injection=enabled的label存在,所以所有在里面启动的pod都会默认被注入一个istio sidecar劫持流量,但在我们argo workflow运行时,通过sidecar通信会有问题,因此如果需要在用户namespace下运行workflow时,需要在workflow配置中显式设置

metadata:
  annotations:
    sidecar.istio.io/inject: 'false'

取消掉istio sidecar,以便容器间能够正常通信。

另外,还需要配置serviceAccountName: default-editor,这个应该是kubeflow在用户namespace默认生成的serviceaccount,如果需要用ns下的某些资源的话,配这个基本没啥问题。

由于上述不便,可能在单独的namespace下运行argo workflows和events的pod更好一些。

参考: