CI-CD

Argo Workflows - (5) Retry, 재귀 호출

Operation CWAL 2021. 7. 12. 22:38

Retry Strategy

실제 서비스 운영시, DB 연결시 Timeout 등의 문제로 프로세스를 재시작해야 하는 경우가 발생할 수 있다. Argo Workflows에서도 Task에서 에러 발생시, task를 다시 생성하여 재시도하는 기능을 제공한다. 다음은 일부러 Python 스크립트 에러를 발생시켜 지정된 횟수만큼 retry를 하는 워크플로우 예시이다.

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: wf-retrystrategy-
spec:
  entrypoint: dag-template
  arguments:
    parameters:
    - name: messageA
      value: A

  templates:
  - name: dag-template
    inputs:
      parameters:
      - name: messageA

    dag:
      tasks:
      - name: Task1
        arguments:
          parameters: [{name: text, value: "{{inputs.parameters.messageA}}" }]
        template: task-decision
      - name: TaskA
        template: task-A
        depends: Task1.Succeeded
        when: "{{tasks.Task1.outputs.result}} == A"

      
  - name: task-decision
    inputs:
      parameters:
      - name: text
    script:
      image: python:3.8-slim
      command: [python]
      source: |
        p = "{{inputs.parameters.text}}"
        print(p)

  - name: task-A
    retryStrategy:
      limit: 5
      retryPolicy: "Always"
      backoff:
        duration: "1"
        factor: 2
        maxDuration: 1m
    script:
      image: python:3.8-slim
      command: [python]
      source: |
        print("Task A was executed.)
  • task-A
    • script.source 필드의 내용을 보면, 일부러 "를 제거하여 Syntax 에러가 발생하도록 설정하였다.
  • retryStrategy
    • limit: 재시도 최대 횟수(number)
    • retryPolicy: 실패, 에러 또는 모든 경우에 대해 retry를 수행할지 선택하며, "Always"/"OnFailure"/"OnError"/"OnTrasientError" 옵션이 존재(string)
    • backOff
      • duration: 최초 retry 전까지 대기하는 시간, 기본은 초 단위이지만 2m, 4h 등으로 분, 시, 일 변경 가능(string)
      • factor: Backoff 발생시, 증가하는 duration의 배수를 의미하며 duration=1,factor=2인 경우, 1->2->4->8->... 와 같이 늘어남(number)
      • maxDuration: Backoff로 대기할 수 있는 최대 시간(string), limit이 아무리 커도 대기시간이 maxDuration을 초과하면 workflow는 종료됨

 

위 Workflow 실행시 아래와 같은 결과를 얻을 수 있다. TaskA의 재시도 5번이 모두 실패하였으며, 60초가 넘어가면서 Workflow도 종료되었다.

 

재귀 호출

프로그래밍을 하다보면, 재귀 함수를 통해 문제를 해결한 경험이 한번쯤 있을 것이다. Argo Workflows의 DAG나 Steps 등의 Template Invocator 역시 하나의 Template이기 때문에, 자기 자신을 호출하도록 구현할 수 있다. 우선 다음 예시를 보자. 

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: wf-recursion-
spec:
  entrypoint: dag-template
  templates:
  - name: dag-template
    dag:
      tasks:
      - name: roll-dice
        template: task-roll-dice
      - name: six
        template: task-six
        depends: roll-dice.Succeeded
        when: "{{tasks.roll-dice.outputs.result}} == 6"
      - name: not-six
        template: dag-template
        depends: roll-dice.Succeeded
        when: "{{tasks.roll-dice.outputs.result}} != 6"

  - name: task-roll-dice
    script:
      image: python:3.8-slim
      command: [python]
      source: |
        import random
        number = random.randint(1,6)
        print(number)
  
  - name: task-six
    script:
      image: python:3.8-slim
      command: [python]
      source: |
        print("Hurray, it's 6!")

Python 코드가 약간 거슬리지만, 내용 자체는 어렵지 않다. 주사위를 굴려서 6이 나오면 메시지를 출력하고 그렇지 않으면 다시 주사위를 굴리는 동작을 워크플로우로 구현하였다. 내부적으로는 6이 나올 때까지 계속해서 'task-roll-dice' 템플릿이 호출될 것이다. 운이 좋으면 한번에 끝날 수도 있지만, 그렇지 않다면 아래 결과처럼 계속해서 Task가 생성될 수 있다.