레이블이 argo-workflows인 게시물을 표시합니다. 모든 게시물 표시
레이블이 argo-workflows인 게시물을 표시합니다. 모든 게시물 표시

argo workflow onexit and hooks

# argo workflow 에서 워크플로우의 종료시 보통 Spec.OnExit 를 사용한다.
# 사용예시 https://github.com/argoproj/argo-workflows/blob/main/examples/exit-handlers.yaml
spec:
  entrypoint: intentional-fail
  onExit: exit-handler

# OnExit 핸들러는 workflow.status 로 성공,실패 여부를 판단한다.
# 참고로 steps 에서 - 에 개수에 따른 차이
# - - 는 이전 단계 이후에 순차적으로 실행
#   - 는 이전 단계와 동시에 실행(argo workflow 그래프로 보면 스텝(노드)들이 동시에 수행되는 것으로 표시된다.)
  - name: exit-handler
    steps:
    - - name: notify
        template: send-email
      - name: celebrate
        template: celebrate
        when: "{{workflow.status}} == Succeeded"

# 그런데 steps.OnExit 로 설정하면 workflow.Status 가 "Running" 로 나온다.
spec:
  template:
  steps:
  - - name: aaa
      onExit: exit-handler

# 주석을 자세히 보니
# spec.onExit 에서는 primary workflow 의 workflow.status(global변수) 가(success, failure, error) 인지를 알 수 있다.
# step.onExit 은 아직 워크플로우가 진행중이니 running 상태가 되는것으로 보인다.
# 아래 코드를 보면 step.OnExit 는 LifecycleHook 형태가 된다.

# 참고로 workflow.status 에 다음과 같은 phase 값이 올 수 있다.
(Unknown)
Pending
Running
Succeeded
Failed
Error

# hooks 는 workflow level, template 에서 사용할 수 있다.
# hooks.exit 는 onExit 와 같은 역할을 한다.
# 사용예시 
spec:
  entrypoint: main
  templates:
  - name: main
    steps:
      - - name: aaa
          hooks:
            running:
              expression: steps.aaa.status == "Running"
              template: http
            success:
              expression: steps.aaa.status == "Succeeded"
              template: http
            failed:
              expression: steps.aaa.status == "Failed"
              template: http
          template: echo

#####

# hook 은 현재 단계의 template 시작에 한번만 트리거돼 스텝별 시작/종료 상태를 파악하기 힘들다.
# 현재 단계에 시작/종료시 상태 파악을 위해 별도 template 을 앞뒤로 넣어준다.
specs:
  entrypoint: main
  templates:
  - name: main
    steps:
    - name: start-main
      template: update-status
      arguments:
        parameters:
        - name: task
          value: "start-main"
        - name: status
          value: "running"
  
    - name: main
      template: echo
        
    - name: finish-main
      template: update-status
      arguments:
        parameters:
        - name: task
          value: "finish-main"
        - name: status
          value: "{{steps.main.status}}"

# 상태 처리를 위해 호출될 템플릿
  - name: update-status
    inputs:
      parameters:
      - name: task
        value: ""
      - name: status
        value: ""
    steps:
    - - name: running-status
        when: "{{inputs.parameters.status}} == running"
        template: write-status
      - name: success-status
        when: "{{inputs.parameters.status}} == Succeeded"
        template: write-status
      - name: fail-status
        when: "{{inputs.parameters.status}} == Failed"
        template: write-status

#####

# 참고
# golang template 에서 사용한다면 다음같이 backtick 으로 감싸줘야 한다.
"{{ `{{inputs.parameters.status}}` }}"

# 팁
# vim 등에서 argo workflow yaml 에 대한 lint 가 없어 편집시 불편하다.
# workflow yaml 작성시 다음과 같이 hwatch(watch 대체 커맨드)와 argo lint 로 확인하면서 편집하자.
hwatch -n 1 argo lint workflow.yaml

argo-workflow parameter json escape

# argo-workflow parameter 로 json 형태를 전달 받았을떄
# single quotation 으로 감싸지 않으면 " escape 가 되지 않는다.

# test 
# argo worfklow version: 3.4.4
# workflow
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: ysoftman-test
  namespace: ysoftman-workflow
spec:
  serviceAccountName: workflow-template
  entrypoint: ysoftman
  templates:
    - name: ysoftman
      steps:
        - - name: ysoftman
            template: print-data
            arguments:
              parameters:
                - name: data1
                  value: "{\"a\":\"apple     lemon\"}"
                - name: data2
                  value: '{"b":"banana"}'

    - name: print-data
      inputs:
        parameters:
          - name: data1
          - name: data2
      script:
        image: "alpine"
        command: [sh]
        source: |
          echo '{{inputs.parameters.data1}}'
          echo '{{inputs.parameters.data2}}'
          echo '{{inputs.parameters}}'
          # invalid JSON 
          echo "{{inputs.parameters.data1}}"
          echo "{{inputs.parameters.data2}}"
          echo "{{inputs.parameters}}"
          # invalid JSON 
          echo {{inputs.parameters.data1}}
          echo {{inputs.parameters.data2}}
          echo {{inputs.parameters}}
          sleep 1000

# workflow pod log 결과
{"a":"apple     lemon"}
{"b":"banana"}
[{"name":"data1","value":"{\"a\":\"apple     lemon\"}"},{"name":"data2","value":"{\"b\":\"banana\"}"}]
{a:apple lemon}
{b:banana}
[{name:data1,value:{"a":"apple lemon"}},{name:data2,value:{"b":"banana"}}]
{a:apple     lemon}
{b:banana}
[{name:data1,value:{"a":"apple     lemon"}},{name:data2,value:{"b":"banana"}}]

#####

# data3 의 값이 json 이고 필드값으로 yaml, xml 등이 escaped 로 된 복잡한 파라메터인 경우 다음과 같이 처리해도 올바른 JSON 으로 출력되지 않는다.
echo '{{inputs.parameters.data3}}' # /argo/staging/script 에러
echo {{inputs.parameters.data3}} # " 가 빠진 invalid JSON
echo -e {{inputs.parameters.data3}} # "가 빠지고 \n 등에서 /argo/staging/script 에러

# jq 가 포함된 이지미를 사용해 script 에 다음과 같이 환경변수를 파싱해서 사용했다.
echo "$ARGO_TEMPLATE" | jq -r '.inputs.parameters[] | select(.name=="data3").value' | jq -r .workflow_yaml > /tmp/data.yaml

kaniko args

# k8s pod 환경에서 이미지 빌드를 위해 kaniko 를 사용한다.
# github pull, docker registry push 를 위해 다음 2가지를 준비한다.
# github > personal_access_token > repo 접근 권한체크해서 생성
kubectl create secret generic ysoftman-generic-secret \
  --from-literal=git-personal-access-token="abc123" \
  --namespace=ysoftman-test

# 이미지 푸시를 위새 docker secret 생성
kubectl create secret docker-registry ysoftman-secret \
  --docker-server=ysoftman \
  --docker-username=ysoftman \
  --docker-password=ysoftman123 \
  --namespace=ysoftman-test

# 이제 argo workflow 로 kaniko(executor) 로 실행하는데,
# dockerfile ARGS 에 전달하기 위해 --build-arg 옵션을 아래와 같이 사용했다.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: ysoftman-test
  namespace: ysoftman-test
spec:
  entrypoint: build-image-and-push
  serviceAccountName: workflow-template
  templates:
    - name: build-image-and-push
      inputs:
        parameters:
          - name: fruit
            value: "lemon"
      script:
        image: "rockylinux:latest"
        command: [bash]
        source: |
          curl -X GET "https://httpbin.org/get" -H "accept: application/json"
          echo "-----"
          echo $ysoftman1
          echo $ysoftman2
        env:
          - name: ysoftman1
            value: lemon
          - name: ysoftman2
            valueFrom:
              secretKeyRef:
                name: my-secret # name of an existing k8s secret
                key: mypassword # 'key' subcomponent of the secret
      container:
        name: kaniko
        image: "gcr.io/kaniko-project/executor:debug"
        env:
          - name: github_personal_access_token
            valueFrom:
              secretKeyRef:
                name: ysoftman-generic-secret
                key: git-personal-access-token
        command: [executor]
        args:
          - "--context=git://$(github_personal_access_token)@github.com/ysoftman/foobar.git#refs/heads/branch1"
          - "--context-sub-path=./aaa/bbb"
          - "--dockerfile=Dockerfile"
          - "--destination=ysoftman/foobar:test"
          - "--build-arg var1={{inputs.parameters.fruit}}"
        volumeMounts:
          - name: kaniko-secret
            mountPath: /kaniko/.docker/
      volumes:
        - name: kaniko-secret
          secret:
            secretname: ysoftman-secret
            items:
              - key: .dockerconfigjson

# 그런데 pod 로그에 다음과 같이 에러가 발생한다.
Error: unknown flag: --build-arg var1

# --build-arg 사용시 IFS(Internal Field Separator) 공백구분을 지원하지 않아 export IFS='' 를 설정하라고 한다.
# 위 와 같은 yaml 에서는 IFS 설정이 안되니 다음과 같이 구분하면 된다.
args:
  - "--build-arg"
  - "var1={{inputs.parameters.fruit}}"

# 그리고 container > args 에서 env 참조시 $(VAR_NAME) 를 사용해야 한다.
args:
 - "foobar=$(github_personal_access_token)"

argo-workflows usage

# ingress 가 없는 경우 k8s api 와 통신하는 프록시 서버 띄우기
kubectl proxy

# 프록시 서버로 접근
http://localhost:8001/api/v1/namespaces/{네임스페이스}/services/{서비스}/proxy

# argo-workflows 로그인
# 다음 명령으로 토큰값을 파악해서 입력한다.
echo -n "Bearer $(kubectl get secret argo-workflows -n argo-workflows -o=jsonpath='{.data.token}' | base64 --decode)" | pbcopy

#####

# argo cli 사용하기
# 로그인 설정
export ARGO_SERVER='argo-workflow.ysoftman.test:80'
export ARGO_HTTP1=true
export ARGO_SECURE=false
export ARGO_BASE_HREF=""
export ARGO_TOKEN="Bearer $(kubectl get secret argo-workflows -n argo-workflows -o=jsonpath='{.data.token}' | base64 --decode)"
export ARGO_NAMESPACE="argo-workflows"

# argo_workflow_hello_world.yaml 작성
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: hello-world
spec:
  entrypoint: whalesay
  templates:
    - name: whalesay
      container:
        image: docker/whalesay
        command: [cowsay]
        args: ["hello world"]
        resources:
          limits:
            memory: 32Mi
            cpu: 100m

# workflow 작업 수행(argo server 에서도 워크플로우가 생성된것을 확인할 수 있다.)
argo submit ./argo_workflow_hello_world.yaml

# workflow lis 로 workflow 이름 확인
argo list

# workflow log
argo logs hello-worldd9z5q --follow

# delete workflow
argo delete hello-worldd9z5q