kubernetes

argo-workflow

misankim 2023. 3. 10. 00:44

argo-workflow


argo 프로젝트의 workflow 관리 도구



# argo workflow 배포

퀵스타트

최신 릴리즈 및 배포 커맨드
-> 릴리즈마다 매니페스트 경로가 다르니 릴리즈 페이지에서 배포 커맨드 확인

kubectl create namespace argo

kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.3.10/install.yaml


# 인증 우회를 위해 인증 모드를 server 로 수정

kubectl patch deployment \
  argo-server \
  --namespace argo \
  --type='json' \
  -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": [
  "server",
  "--auth-mode=server"
]}]'


# 특정 네임스페이스 범위로 argo workflow 배포



# argo cli 설치
-> 릴리즈마다 매니페스트 경로가 다르니 릴리즈 페이지에서 배포 커맨드 확인

curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.3.10/argo-darwin-amd64.gz

gunzip argo-darwin-amd64.gz

chmod +x argo-darwin-amd64

mv sudo ./argo-darwin-amd64 /usr/local/bin/argo

argo version


# 접속

kubectl -n argo port-forward deployment/argo-server 2746:2746

https://localhost:2746/


# 샘플 워크플로우 생성

argo submit -n argo --watch https://raw.githubusercontent.com/argoproj/argo-workflows/master/examples/hello-world.yaml

워크플로우 리스트 확인
argo list -n argo

가장 최근 실행된 워크플로우 확인
argo get -n argo @latest

가장 최근 실행된 워크플로우 로그 확인
argo logs -n argo @latest


# argo workflow 인증

## 토큰 인증



kubectl create sa jenkins

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: jenkins.service-account-token
  annotations:
    kubernetes.io/service-account.name: jenkins
type: kubernetes.io/service-account-token
EOF

ARGO_TOKEN="Bearer $(kubectl get secret jenkins.service-account-token -o=jsonpath='{.data.token}' | base64 --decode)"
echo $ARGO_TOKEN

## 권한 부여
-> argo workflow 배포 시 함께 배포되는 ClusterRole(argo-aggregate-to-admin, argo-aggregate-to-edit, argo-aggregate-to-view) 이 있기 때문에 ClusterRoleBinding 혹은 RoleBinding 만 생성해주면 됨

### 클러스터 레벨 권한 부여

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argo-binding-jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-aggregate-to-view
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: argo
EOF

### 네임스페이스 레벨 권한 부여

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: argo-binding-jenkins
  namespace: argo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-aggregate-to-view
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: argo
EOF

## sso 설정
-> argocd 와 동일하게 sso 인증을 제공함


workflow-controller-configmap 샘플

### google oauth2 사용자 인증 정보 설정

유형 - 웹 애플리케이션
승인된 자바스크립트 원본 - https://localhost:2746
승인된 리디렉션 URI - https://localhost:2746/oauth2/callback

### configmap 수정

kubectl edit cm workflow-controller-configmap

data:
  sso: |
    issuer: https://accounts.google.com
    sessionExpiry: 240h
    clientId:
      name: google-oauth2
      key: client-id-key
    clientSecret:
      name: google-oauth2
      key: client-secret-key
    redirectUrl: https://localhost:2746/oauth2/callback
    scopes:
     - email
    rbac:
      enabled: true
    insecureSkipVerify: true

### google oauth2 인증을 위한 클라이언트 아이디/시크릿 생성

echo -n '0000000000000-cifctdaeua2z1a8bf4udrc8hkediadzn.apps.googleusercontent.com' > client-id-key

echo -n '0000000000000s3TNdn0zEdYvIf-PfWjq7z' > client-secret-key

kubectl create secret -n argo generic google-oauth2 \
  --from-file=./client-id-key \
  --from-file=./client-secret-key

### argo-server 인증 방식을 sso 로 변경

kubectl patch deployment \
  argo-server \
  --namespace argo \
  --type='json' \
  -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": [
  "server",
  "--auth-mode=sso"
]}]'

configmap 수정하여 재적용 시
kubectl rollout restart deploy argo-server

(참고) sso 로그인 시 보여지는 항목

Issuer: argo-server
Subject: 126442351131619240018
Groups: -
Email: my-email@gmail.com
Email Verified:
Service Account: -

### 클러스터 레벨의 rbac 기본값 설정
-> rbac 의 경우 rbac-rule-precedence 값을 통해 우선 순위를 설정 가능하며, rbac-rule-precedence 값이 높을 수록 우선 순위가 높고, 0의 경우 적용되는 것이 하나도 없는 경우에 적용되는 기본 값

vim argo-sso-default.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default-user
  namespace: argo
  annotations:
    workflows.argoproj.io/rbac-rule: "true"
    workflows.argoproj.io/rbac-rule-precedence: "0"
secrets:
- name: default-user.service-account-token
---
apiVersion: v1
kind: Secret
metadata:
  name: default-user.service-account-token
  namespace: argo
  annotations:
    kubernetes.io/service-account.name: default-user
type: kubernetes.io/service-account-token

 
기본 권한을 읽기 전용으로 주고 싶다면

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argo-binding-default-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-aggregate-to-view
subjects:
- kind: ServiceAccount
  name: default-user
  namespace: argo

 
### 클러스터 레벨 rbac 을 위한 설정
-> 특정 사용자에게 클러스터 전체 어드민 권한 부여

vim argo-sso-cluster.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: argo
  annotations:
    workflows.argoproj.io/rbac-rule: "email in ['my-email@gmail.com']"
    workflows.argoproj.io/rbac-rule-precedence: "1"
secrets:
- name: admin-user.service-account-token
---
apiVersion: v1
kind: Secret
metadata:
  name: admin-user.service-account-token
  namespace: argo
  annotations:
    kubernetes.io/service-account.name: admin-user
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argo-binding-admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-aggregate-to-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: argo


### 네임스페이스 레벨 rbac 을 위한 설정
-> 네임스페이스 레벨로 rbac 적용을 위해서는 argo-server deployment 에 SSO_DELEGATE_RBAC_TO_NAMESPACE=true 환경변수 추가 필요

kubectl edit deploy argo-server

spec:
...
  template:
      labels:
        app: argo-server
    spec:
      containers:
      - args:
        - server
        - --auth-mode=sso
        env:
        - name: SSO_DELEGATE_RBAC_TO_NAMESPACE
          value: "true"
        image: quay.io/argoproj/argocli:v3.3.10
        imagePullPolicy: IfNotPresent
        name: argo-server
...

네임스페이스 레벨의 admin 권한을 부여할 네임스페이스가 default 인 경우 아래처럼 작성

vim argo-sso-ns.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ns-admin-user
  namespace: default
  annotations:
    workflows.argoproj.io/rbac-rule: "email in ['my-email@gmail.com']"
    workflows.argoproj.io/rbac-rule-precedence: "1001"
secrets:
- name: ns-admin-user.service-account-token
---
apiVersion: v1
kind: Secret
metadata:
  name: ns-admin-user.service-account-token
  namespace: default
  annotations:
    kubernetes.io/service-account.name: ns-admin-user
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: argo-binding-ns-admin-user
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-aggregate-to-admin
subjects:
- kind: ServiceAccount
  name: ns-admin-user
  namespace: default


## argocd dex 인증
-> arogcd 의 인증 서버인 dex 를 인증 수단으로 사용(okta 인증 등에 사용된다고 함)



# workflow pod 보안
-> 일반 사용자로 실행되도록 설정
-> 실행되는 스크립트 내용에 따라 에러가 발생할 수 있음


...
  workflowSpec:
    entrypoint: curl
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
...


# k8s cronjob 을 cronworkflow 로 마이그레이션
-> 파드가 사라져도 workflow 는 남아있기 때문에 실행된 크론의 성공/실패 여부를 확인하기 용이함
-> 웹 ui 를 통해 배치성 작업을 관리할 수 있어 편리함
-> 파드는 로그를 확인하는 역할만 있기 때문에 클러스터에 컨테이너의 로그를 전송하는 로깅 설정(gcp logging 등)이 되어 있다면 남길 필요는 없음

workflow 정의에 사용되는 필드 참조

## cronjob 및 cronworkflow 에 사용되는 configmap 매니페스트

vim configmap.yaml
 
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-curl
data:
  check.sh: |
    #!/bin/sh
    apk update && apk add curl

    echo '####################################'
    echo 'url check'
    echo '####################################'

    for url in `cat /tmp/list.txt`
      do echo $url
      curl -s -o /dev/null -w "%{http_code}" $url
      echo
      echo
    done

    exit 0
  list.txt: |
    https://www.google.com/
    https://google.com/
    https://www.naver.com/
    https://www.daum.net/


## 기존 cronjob 매니페스트

vim cronjob.yaml

apiVersion: batch/v1
kind: CronJob
metadata:
  name: curl
spec:
  schedule: "* * * * *"
  successfulJobsHistoryLimit: 5
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      template:
        metadata:
          annotations:
            proxy.istio.io/config: |
              holdApplicationUntilProxyStarts: true
        spec:
          containers:
          - name: curl
            image: alpine:latest
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - /check.sh
            volumeMounts:
            - name: curl
              mountPath: /check.sh
              subPath: check.sh
            - name: curl
              mountPath: /tmp/list.txt
              subPath: list.txt
          volumes:
          - name: curl
            configMap:
              name: cm-curl
          restartPolicy: Never


## cronworkflow 매니페스트

vim cronworkflow.yaml
 
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: curl
spec:
  schedule: '* * * * *'
  successfulJobsHistoryLimit: 30 # wf not pod
  failedJobsHistoryLimit: 30 # wf not pod
  workflowMetadata:
    annotations:
      proxy.istio.io/config: |
        holdApplicationUntilProxyStarts: true
  workflowSpec:
    entrypoint: curl
    ttlStrategy: # time to delete wf
      secondsAfterSuccess: 3600
      secondsAfterFailure: 3600
    podGC: # pod delete policy
      strategy: OnPodCompletion # success or fail 
      # strategy: OnPodSuccess # only success
    templates:
    - name: curl
      retryStrategy:
        limit: "2" # try total 3 times
      metadata: {}
      inputs: {}
      outputs: {}
      container:
        name: main
        image: alpine:latest
        command:
        - /bin/sh
        - /check.sh
        resources: {}
        volumeMounts:
        - name: curl
          mountPath: /check.sh
          subPath: check.sh
        - name: curl
          mountPath: /tmp/list.txt
          subPath: list.txt
      volumes:
      - name: curl
        configMap:
          name: cm-curl


## cronworkflow 배포
-> cwf, wf 모두 네임스페이스 레벨의 리소스
-> cwf 를 삭제하면 wf, pod 도 함께 삭제됨
kubectl apply -f configmap.yaml

kubectl apply -f cronworkflow.yaml

kubectl get cwf

kubectl get wf