首页 > 其他分享 >ArgoWorkflow教程(八)---基于 LifecycleHook 实现流水线通知提醒

ArgoWorkflow教程(八)---基于 LifecycleHook 实现流水线通知提醒

时间:2024-10-29 13:20:53浏览次数:2  
标签:status LifecycleHook name workflow --- exit template ArgoWorkflow http

argoworkflow-8-workflow-notify.png

本篇介绍一下 ArgoWorkflow 中的 ExitHandler 和 LifecycleHook 功能,可以根据流水线每一步的不同状态,执行不同操作,一般用于发送通知。

1. 概述

本篇介绍一下 ArgoWorkflow 中的 ExitHandler 和 LifecycleHook 功能,可以根据流水线每一步的不同状态,执行不同操作,一般用于发送通知。

比如当某个步骤,或者某个 Workflow 执行失败时,发送邮件通知。

在 ArgoWorkflow 不同版本中中有两种实现方式:

  • 1)v2.7 版本开始提供了 exit handler 功能,可以指定一个在流水线运行完成后执行的模板。同时这个模板中还可以使用 when 字段来做条件配置,以实现比根据当前流水线运行结果来执行不同流程。
    • 已废弃,v3.3 版本后不推荐使用
  • 2)v.3.3 版本新增 LifecycleHook,exit handler 功能则不推荐使用了,LifecycleHook 提供了更细粒度以及更多功能,exit handler 可以看做是一个简单的 LifecycleHook。

2. ExitHandler

虽然官方已经不推荐使用该功能了,但是还是简单介绍一下。

ArgoWorkflow 提供了 spec.onExit 字段,可以指定一个 template,当 workflow 执行后(不论成功或者失败)就会运行 onExit 指定的 template。

类似于 Tekton 中的 finally 字段

同时这个 template 中可以使用 when 字段来做条件配置。比如根据当前流水线运行结果来执行不同流程。

比如下面这个 Demo,完整 Workflow 内容如下:

# An exit handler is a template reference that executes at the end of the workflow
# irrespective of the success, failure, or error of the primary workflow. To specify
# an exit handler, reference the name of a template in 'spec.onExit'.
# Some common use cases of exit handlers are:
# - sending notifications of workflow status (e.g. e-mail/slack)
# - posting the pass/fail status to a webhook result (e.g. github build result)
# - cleaning up workflow artifacts
# - resubmitting or submitting another workflow
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: exit-handlers-
spec:
  entrypoint: intentional-fail
  onExit: exit-handler
  templates:
    # primary workflow template
    - name: intentional-fail
      container:
        image: alpine:latest
        command: [sh, -c]
        args: ["echo intentional failure; exit 1"]

    # exit handler related templates
    # After the completion of the entrypoint template, the status of the
    # workflow is made available in the global variable {{workflow.status}}.
    # {{workflow.status}} will be one of: Succeeded, Failed, Error
    - name: exit-handler
      steps:
        - - name: notify
            template: send-email
          - name: celebrate
            template: celebrate
            when: "{{workflow.status}} == Succeeded"
          - name: cry
            template: cry
            when: "{{workflow.status}} != Succeeded"
    - name: send-email
      container:
        image: alpine:latest
        command: [sh, -c]
        # Tip: {{workflow.failures}} is a JSON list. If you're using bash to read it, we recommend using jq to manipulate
        # it. For example:
        #
        # echo "{{workflow.failures}}" | jq -r '.[] | "Failed Step: \(.displayName)\tMessage: \(.message)"'
        #
        # Will print a list of all the failed steps and their messages. For more info look up the jq docs.
        # Note: jq is not installed by default on the "alpine:latest" image, however it can be installed with "apk add jq"
        args: ["echo send e-mail: {{workflow.name}} {{workflow.status}} {{workflow.duration}}. Failed steps {{workflow.failures}}"]
    - name: celebrate
      container:
        image: alpine:latest
        command: [sh, -c]
        args: ["echo hooray!"]
    - name: cry
      container:
        image: alpine:latest
        command: [sh, -c]
        args: ["echo boohoo!"]

首先是通过 spec.onExit 字段配置了一个 template

spec:
  entrypoint: intentional-fail
  onExit: exit-handler

这个 template 内容如下:

    - name: exit-handler
      steps:
        - - name: notify
            template: send-email
          - name: celebrate
            template: celebrate
            when: "{{workflow.status}} == Succeeded"
          - name: cry
            template: cry
            when: "{{workflow.status}} != Succeeded"

内部包含 3 个步骤,每个步骤又是一个 template:

  • 1)发送邮件,无论成功或者失败
  • 2)若成功则执行 celebrate
  • 3)若失败则执行 cry

该 Workflow 不论执行结果如何,都会发送邮件,邮件内容包含了任务的执行信息,若是执行成功则会额外打印执行成功,若是执行失败则会打印执行失败。

为了简单,这里所有操作都使用 echo 命令进行模拟

由于在主 template 中最后执行的是 exit 1 命令,因此会判断为执行失败,会发送邮件并打印失败信息,Pod 列表如下:

[root@argo-1 lifecyclehook]# k get po
NAME                                              READY   STATUS      RESTARTS        AGE
exit-handlers-44ltf                               0/2     Error       0               2m45s
exit-handlers-44ltf-cry-1621717811                0/2     Completed   0               2m15s
exit-handlers-44ltf-send-email-2605424148         0/2     Completed   0               2m15s

各个 Pod 日志

[root@argo-1 lifecyclehook]# k logs -f exit-handlers-44ltf-cry-1621717811
boohoo!
time="2024-05-25T11:34:39.472Z" level=info msg="sub-process exited" argo=true error="<nil>"
[root@argo-1 lifecyclehook]# k logs -f exit-handlers-44ltf-send-email-2605424148
send e-mail: exit-handlers-44ltf Failed 30.435347. Failed steps [{"displayName":"exit-handlers-44ltf","message":"Error (exit code 1)","templateName":"intentional-fail","phase":"Failed","podName":"exit-handlers-44ltf","finishedAt":"2024-05-25T11:34:16Z"}]
time="2024-05-25T11:34:44.424Z" level=info msg="sub-process exited" argo=true error="<nil>"
[root@argo-1 lifecyclehook]# k logs -f exit-handlers-44ltf
intentional failure
time="2024-05-25T11:34:15.856Z" level=info msg="sub-process exited" argo=true error="<nil>"
Error: exit status 1

至此,这个 exitHandler 功能就可以满足我们基本的通知需求了,比如将结果以邮件发出,或者对接外部系统 Webhook,更加复杂的需求也可以实现。

不过存在一个问题,就是 exitHandler 是 Workflow 级别的,只能整个 Workflow 执行完成才会执行 exitHandler。

如果想要更细粒度的,比如 template 级别则做不到,v3.3 中提供的 LifecycleHook 则实现了更加细粒度的通知。

3. LifecycleHook

LifecycleHook 可以看做是一个比较灵活的 exit hander,官方描述如下:

Put differently, an exit handler is like a workflow-level LifecycleHook with an expression of workflow.status == "Succeeded" or workflow.status == "Failed" or workflow.status == "Error".

LifecycleHook 有两种级别:

  • Workflow 级别
  • template 级别

Workflow 级别

Workflow 级别的 LifecycleHook 和 exitHandler 基本类似。

下面就是一个 Workflow 级别的 LifecycleHook Demo,完整 Workflow 内容如下:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: lifecycle-hook-
spec:
  entrypoint: main
  hooks:
    exit: # Exit handler
      template: http
    running:
      expression: workflow.status == "Running"
      template: http
  templates:
    - name: main
      steps:
      - - name: step1
          template: heads
    
    - name: heads
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was heads\""]
    
    - name: http
      http:
        # url: http://dummy.restapiexample.com/api/v1/employees
        url: "https://raw.githubusercontent.com/argoproj/argo-workflows/4e450e250168e6b4d51a126b784e90b11a0162bc/pkg/apis/workflow/v1alpha1/generated.swagger.json"

首先是配置 hook

spec:
  entrypoint: main
  hooks:
    exit: # Exit handler
      template: http
    running:
      expression: workflow.status == "Running"
      template: http

可以看到,原有的 onExit 被 hooks 字段替代了,同时 hooks 字段支持指定多个 hook,每个 hook 中可以通过 expression 设置不同的条件,只有满足条件时才会执行。

这里的 template 则是一个内置的 http 类型的 template

    - name: http
      http:
        # url: http://dummy.restapiexample.com/api/v1/employees
        url: "https://raw.githubusercontent.com/argoproj/argo-workflows/4e450e250168e6b4d51a126b784e90b11a0162bc/pkg/apis/workflow/v1alpha1/generated.swagger.json"

该 Workflow 的主 template 比较简单,就是使用 echo 命令打印一句话,因此会执行成功,那么 hooks 中的两个 hooks 都会执行。

两个 hook 对应的都是同一个 template,因此会执行两遍。

template 级别

template 级别的 hooks 则是提供了更细粒度的配置,比如可能用户比较关心 Workflow 中某一个步骤的状态,可以单独为该 template 设置 hook。

下面是一个template 级别的 hooks demo,Workflow 完整内容如下:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: lifecycle-hook-tmpl-level-
spec:
  entrypoint: main
  templates:
    - name: main
      steps:
        - - name: step-1
            hooks:
              running: # Name of hook does not matter
                # Expr will not support `-` on variable name. Variable should wrap with `[]`
                expression: steps["step-1"].status == "Running"
                template: http
              success:
                expression: steps["step-1"].status == "Succeeded"
                template: http
            template: echo
        - - name: step2
            hooks:
              running:
                expression: steps.step2.status == "Running"
                template: http
              success:
                expression: steps.step2.status == "Succeeded"
                template: http
            template: echo

    - name: echo
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was heads\""]

    - name: http
      http:
        # url: http://dummy.restapiexample.com/api/v1/employees
        url: "https://raw.githubusercontent.com/argoproj/argo-workflows/4e450e250168e6b4d51a126b784e90b11a0162bc/pkg/apis/workflow/v1alpha1/generated.swagger.json"

内容和 Workflow 级别的 Demo 差不多,只是 hooks 字段的位置不同

spec:
  entrypoint: main
  templates:
    - name: main
      steps:
        - - name: step-1
            hooks:
              # ...
            template: echo
        - - name: step2
            hooks:
						  # ...
            template: echo

在 spec.templates 中我们分别为不同的步骤配置了 hooks,相比与 exiHandler 则更加灵活。

如何替代 exitHandler

LifecycleHook 可以完美替代 Exit Handler,就是把 Hook 命名为 exit,虽然 hook 的命名无无关紧要,但是如果是 exit 则是会特殊处理。

官方原文如下:

You must not name a LifecycleHook exit or it becomes an exit handler; otherwise the hook name has no relevance.

这个 exit 直接是写死在代码里的,具体如下:

const (
    ExitLifecycleEvent = "exit"
)

func (lchs LifecycleHooks) GetExitHook() *LifecycleHook {
    hook, ok := lchs[ExitLifecycleEvent]
    if ok {
       return &hook
    }
    return nil
}

func (lchs LifecycleHooks) HasExitHook() bool {
    return lchs.GetExitHook() != nil
}

那么我们只需要将 LifecycleHook 命名为 exit 即可替代 exit handler,就像这样:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: lifecycle-hook-
spec:
  entrypoint: main
  hooks:
    exit: # if named exit, it'a an Exit handler
      template: http
  templates:
    - name: main
      steps:
      - - name: step1
          template: heads
    - name: http
      http:
        # url: http://dummy.restapiexample.com/api/v1/employees
        url: "https://raw.githubusercontent.com/argoproj/argo-workflows/4e450e250168e6b4d51a126b784e90b11a0162bc/pkg/apis/workflow/v1alpha1/generated.swagger.json"

4. 常见通知模板

通知一般支持 webhook、email、slack、微信通知等方式。

在 ArgoWorkflow 中则是准备对应的模板即可。

Webhook

这应该是最通用的一种方式,收到消息后具体做什么事情,可以灵活的在 webhook 服务调整。

对于 ArgoWorkflow 模板就是执行 curl 命令即可,因此只需要一个包含 curl 工具的容器

apiVersion: argoproj.io/v1alpha1
kind: ClusterWorkflowTemplate
metadata:
  name: step-notify-webhook
spec:
  templates:
    - name: webhook
      inputs:
        parameters:
          - name: POSITIONS # 指定什么时候运行,多个以逗号隔开,例如:Pending,Running,Succeeded,Failed,Error
            value: "Succeeded,Failed,Error"
          - name: WEBHOOK_ENDPOINT
          - name: CURL_VERSION
            default: "8.4.0"

      container:
        image: curlimages/curl:{{inputs.parameters.CURL_VERSION}}
        command: [sh, -cx]
        args: [
          "curl -X POST  -H \"Content-type: application/json\" -d '{
          \"message\": \"{{workflow.name}} {{workflow.status}}\",
          \"workflow\": {
                \"name\": \"{{workflow.name}}\",
                \"namespace\": \"{{workflow.namespace}}\",
                \"uid\": \"{{workflow.uid}}\",
                \"creationTimestamp\": \"{{workflow.creationTimestamp}}\",
                \"status\": \"{{workflow.status}}\"
              }
        }'
        {{inputs.parameters.WEBHOOK_ENDPOINT}}"
        ]

Email

对于邮件方式,这里简单提供一个使用 Python 发送邮件的 Demo。

# use golangcd-lint for lint
apiVersion: argoproj.io/v1alpha1
kind: ClusterWorkflowTemplate
metadata:
  name: step-notify-email
spec:
  templates:
    - name: email
      inputs:
        parameters:
          - name: POSITIONS # 指定什么时候运行,多个以逗号隔开,例如:Pending,Running,Succeeded,Failed,Error
            value: "Succeeded,Failed,Error"
          - name: CREDENTIALS_SECRET
          - name: TO # 收件人邮箱
          - name: PYTHON_VERSION
            default: "3.8-alpine"
      script:
        image: docker.io/library/python:{{inputs.parameters.PYTHON_VERSION}}
        command: [ python ]
        env:
          - name: TO
            value: '{{inputs.parameters.TO}}'
          - name: HOST
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: host
          - name: PORT
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: port
          - name: FROM
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: from
          - name: USERNAME
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: username
          - name: PASSWORD
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: password
          - name: TLS
            valueFrom:
              secretKeyRef:
                name: '{{inputs.parameters.CREDENTIALS_SECRET}}'
                key: tls
        source: |
          import smtplib
          import ssl
          import os
          from email.header import Header
          from email.mime.text import MIMEText

          smtp_server = os.getenv('HOST')
          port = os.getenv('PORT')
          sender_email = os.getenv('FROM')
          receiver_emails = os.getenv('TO')
          user = os.getenv('USERNAME')
          password = os.getenv('PASSWORD')
          tls = os.getenv('TLS')

          # 邮件正文,文本格式
          # 构建邮件消息
          workflow_info = f"""\
            "workflow": {{
              "name": "{{workflow.name}}",
              "namespace": "{{workflow.namespace}}",
              "uid": "{{workflow.uid}}",
              "creationTimestamp": "{{workflow.creationTimestamp}}",
              "status": "{{workflow.status}}"
            }}
          """
          msg = MIMEText(workflow_info, 'plain', 'utf-8')
          # 邮件头信息
          msg['From'] = Header(sender_email)  # 发送者
          msg['To'] = Header(receiver_emails)  # 接收者
          subject = '{{workflow.name}} {{workflow.status}}'
          msg['Subject'] = Header(subject, 'utf-8')  # 邮件主题
          if tls == 'True':
            context = ssl.create_default_context()
            server = smtplib.SMTP_SSL(smtp_server, port, context=context)
          else:
            server = smtplib.SMTP(smtp_server, port)

          if password != '':
            server.login(user, password)

          for receiver in [item for item in receiver_emails.split(' ') if item]:
            server.sendmail(sender_email, receiver, msg.as_string())

            server.quit()

【ArgoWorkflow 系列】持续更新中,搜索公众号【探索云原生】订阅,阅读更多文章。


5. 小结

本文主要分析了 Argo 中的通知触发机制,包括旧版的 exitHandler 以及新版的 LifecycleHook,并提供了几个简单的通知模板。

最后则是推荐使用更加灵活的 LifecycleHook。

标签:status,LifecycleHook,name,workflow,---,exit,template,ArgoWorkflow,http
From: https://www.cnblogs.com/KubeExplorer/p/18512868

相关文章

  • Vue基础-列表渲染v-for
    列表渲染v-for基本使用◼v-for的基本格式是"itemin数组":数组通常是来自data或者prop,也可以是其他方式;item是我们给每项元素起的一个别名,这个别名可以自定来定义;◼我们知道,在遍历一个数组的时候会经常需要拿到数组的索引:如果我们需要索引,可以使用格式:"item......
  • python知识点100篇系列(23)- 使用stylecloud生成词云
    使用stylecloud生成词云stylecloud是wordcloud优化版,相对来说操作更简单;一个很方便的功能是,可以使用FontAwesome提供的免费图标更改词云的形状;FontAwesome提供的免费图标库参考这里安装所需库主要是安装一下分词用到的jieba和生成词云使用的stylecloud安装方式......
  • #渗透测试#SRC漏洞挖掘# 信息收集-Shodan进阶之未授权访问
    免责声明本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章阅读。                            ......
  • 机票电子行程单如何批量查验?Java机票电子行程单查验接口示例-发票查验接口
    机票电子行程单来了,它方便了人们的出行。现如今,随着旅游、差旅市场的回暖与线上业务的蓬勃发展,机票电子行程单的需求量急剧攀升,如何高效且准确地查验这些电子行程单成为许多企业和财务部门关注的焦点。传统的人工查验流程耗时且易出错,尤其在行程单数量巨大的情况下,费时费力......
  • 东方博宜1266 - 求最大数
    问题描述问555555的约数中最大的三位数是多少?输入无。输出约数中最大的三位数。样例输入输出C++#include<iostream>usingnamespacestd;intmain(){intnum=555555;intlargestDivisor=0;for(inti=1;i<=num;i++){if(......
  • 自动化测试工具Ranorex Studio(十五)-自定义代码ACTION
    在Recorder提供的功能不能够满足的情况下,可以使用自定义代码。这里有一些例子,可以方便演示自定义代码Action。•   自定义验证•   访问测试用例相关的数据和参数•   扩展报告在项目视图窗口中,仔细看一个录制模块文件,你会看到有两个相关的代码文件。 图:录......
  • Pytorch学习--神经网络--非线性激活
    一、用法torch.nn.ReLU图像处理中的应用:在图像处理任务中,ReLU激活函数能够增强特征提取的能力,使网络更好地捕捉图像的细节和边缘。这是因为ReLU对大部分负数响应为零,能在一定程度上减少网络计算量,并对特征层起到稀疏化的效果,避免信息的过度平滑。torch.nn.Sigmoid......
  • HNU-操作系统实验lab6-2022级
    实验目的任务调度是操作系统的核心功能之一。UniProton实现的是一个单进程支持多线程的操作系统。在UniProton中,一个任务表示一个线程。UniProton中的任务为抢占式调度机制,而非时间片轮转调度方式。高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务挂起或......
  • 【Python原创毕设|课设】基于Python、机器学习的垃圾邮件分类与安全宣传网站-文末附下
    基于Python、机器学习的垃圾邮件分类与安全宣传网站-(获取方式访问文末官网)一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、数据库设计七、功能实现八、源码获取一、项目简介该该系统是一个基于Python的邮件分类和安全宣传网站,结合了机器学习和数......
  • MBI6671Q聚积升降压LED驱动芯片车规级认证AEC-Q100认证
    引言: MBI6671Q是由聚积(Macroblock)公司推出的一款多拓扑恒流控制器,专为驱动高亮度LED设计。它广泛应用于汽车照明等需要高效、可靠和灵活控制的领域,特别是在高功率LED照明场景中。主要特点:1.宽输入电压范围:MBI6671Q支持5.4V至65V的宽输入电压范围,使其能够适应多种电源环境......