こちらはあくあたん工房GWアドベントカレンダー5月5日の記事です.
Workflow Engine とは
Job1があるサービスからデータを取得する,Job2がJob1で取得したデータをもとに利用可能なかたち例えばある数式に基づいた評価スコアに変換する,Job3がJob2で算出されたスコアでランキングを生成する...といった一連のJobの流れがあるとします. このとき,当然ながらJob2はJob1が終わった後に実行しないと意味がないですし,同様にJob3はJob2が終わった後に実行しないといけません.このようなJobの依存関係の解決方法として,例えばKubernetesではCronJobのスケジュールをうまく設定することが考えられます.しかし,この方法では一つ一つのJobの実行時間を把握しておかなければいけません.
またJobの数が数十個単位やそれ以上になったとき,そこに新たなJobを追加しようとするのは,もはや職人芸の域と言えるでしょう.
このような問題を解決してくれるのがWorkflow Engineです.
一連のJobの流れをWorkflowといい,Workflow EngineはこのようなWorkflowの実行を管理したり,それぞれのJobのログをとってくれたり等を担ってくれます(なお,Workflow Engineとして持つべき機能は厳密には定義されていないためどのような機能を提供しているかは各ツールによります).
Workflow Engineの一覧は,こちらにまとめられているため目を通してみると良いかもしれません.
Argo とは
Argo1はArgoprojに所属するツールの一つで,KubernetesをプラットフォームとするWorkflow Engineです.
カスタムリソースとしてWorkflow
リソースを定義し,マニフェストファイルをapply
することで各Workflowを実行します.
Argoの生い立ちについては公式ブログにまとめられています.
Argoを触ってみる
環境
- minikube v1.0.0
- argo v2.2.1
インストール
公式ドキュメントを参考に進めます.
argoコマンド
Argoにおけるworkflow
リソースの管理はkubectl
をラップしたargo
コマンドを使います.
# Mac
$ brew install argoproj/tap/argo
# Linux
$ curl -sSL -o /usr/local/bin/argo https://github.com/argoproj/argo/releases/download/v2.2.1/argo-linux-amd64
$ chmod +x /usr/local/bin/argo
シェルを再読込してargo
と入力し,コマンドの説明が表示されればインストール成功です.
CRD/Workflow
CRDとしてworkflow
を登録し,必要なコントローラ等をデプロイします.
$ kubectl create ns argo
$ kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo/v2.2.1/manifests/install.yaml
kubectl api-resources
で確認してみます.workflow
リソースの略称はwf
のようです.
$ kubectl api-resources | grep argo
workflows wf argoproj.io true Workflow
実行してみる
公式ドキュメント内のexampleを試してみようと思います.
Steps
"-"の数で実行順序を定義します.
マニフェストファイルを見てみましょう.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: steps- # サフィックスとして5文字の英数字が割り当てられるため,判別しやすいようにうしろに"-"をつけている
spec:
entrypoint: hello-hello-hello # このentorypointに記述されているtemplateが実行されます
# spec内では"hello-hello-hello"と"whalesay"の二つのtemplateが定義されています.
templates:
- name: hello-hello-hello
steps: # Stepsで実行することを明記
- - name: hello1 # hello1が最初に実行されるstepです.
template: whalesay # このstepに到達すると,template.whalesayが実行されます
arguments:
parameters:
- name: message
value: "hello1"
- - name: hello2a # "- -" => 前のstepが実行し終わると実行開始されます.
template: whalesay
arguments:
parameters:
- name: message
value: "hello2a"
- name: hello2b # "-" => 前のstepと並列して実行されます.
template: whalesay
arguments:
parameters:
- name: message
value: "hello2b"
- name: whalesay
inputs:
parameters:
- name: message
container:
image: docker/whalesay
command: [cowsay]
args: ["{{inputs.parameters.message}}"]
workflowを定義する部分と,各step内のJobTemplateが同列に記述されているのは面白いですね.
実行する際はargo submit
を使います.
$ argo submit steps.yml --watch # watchオプションをつけると実行状況の監視ができる
Name: steps-rndzz
Namespace: argo
ServiceAccount: default
Status: Succeeded
Created: Fri May 03 16:20:37 +0900 (18 seconds ago)
Started: Fri May 03 16:20:37 +0900 (18 seconds ago)
Finished: Fri May 03 16:20:55 +0900 (now)
Duration: 18 seconds
STEP PODNAME DURATION MESSAGE
✔ shanpu-steps-rndzz
├---✔ hello1 steps-rndzz-2539174266 7s
└-·-✔ hello2a steps-rndzz-1532723075 7s
└-✔ hello2b steps-rndzz-1549500694 10s
DAG
DAG2ではdependencies
に依存関係にあるJobを明示的に指定します.
マニフェストファイルを見てみましょう.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-diamond-
spec:
entrypoint: diamond
templates:
- name: echo
inputs:
parameters:
- name: message
container:
image: alpine:3.7
command: [echo, "{{inputs.parameters.message}}"]
- name: diamond
dag:
tasks:
- name: A
template: echo
arguments:
parameters: [{name: message, value: A}]
- name: B
dependencies: [A] # Aの実行終了後Bが実行される
template: echo
arguments:
parameters: [{name: message, value: B}]
- name: C
dependencies: [A] # Aの実行終了後Cが実行される
template: echo
arguments:
parameters: [{name: message, value: C}]
- name: D
dependencies: [B, C] # B,Cの実行終了後Dが実行される
template: echo
arguments:
parameters: [{name: message, value: D}]
DAGもSteps同様,argo submit
で実行します.
$ argo submit dag.yml --watch
Name: dag-diamond-5dkj2
Namespace: argo
ServiceAccount: default
Status: Succeeded
Created: Fri May 03 16:36:28 +0900 (17 seconds ago)
Started: Fri May 03 16:36:28 +0900 (17 seconds ago)
Finished: Fri May 03 16:36:45 +0900 (now)
Duration: 17 seconds
STEP PODNAME DURATION MESSAGE
✔ dag-diamond-5dkj2
├-✔ A dag-diamond-5dkj2-322730072 9s
├-✔ B dag-diamond-5dkj2-373062929 3s
├-✔ C dag-diamond-5dkj2-356285310 3s
└-✔ D dag-diamond-5dkj2-406618167 2s
実行したWorkflowの確認
argoコマンドを使うことでsubmitされたworkflowの確認ができます.
-
argo list
: submitされたworkflowの一覧を表示
$ argo list
NAME STATUS AGE DURATION
dag-diamond-5dkj2 Succeeded 4m 17s
steps-rndzz Succeeded 20m 18s
-
argo get [workflow_name]
: submitされたworkflowの詳細を表示
$ argo get dag-diamond-5dkj2 2019/05/03 16:40
Name: dag-diamond-5dkj2
Namespace: argo
ServiceAccount: default
Status: Succeeded
Created: Fri May 03 16:36:28 +0900 (4 minutes ago)
Started: Fri May 03 16:36:28 +0900 (4 minutes ago)
Finished: Fri May 03 16:36:45 +0900 (4 minutes ago)
Duration: 17 seconds
STEP PODNAME DURATION MESSAGE
✔ dag-diamond-5dkj2
├-✔ A dag-diamond-5dkj2-322730072 9s
├-✔ B dag-diamond-5dkj2-373062929 3s
├-✔ C dag-diamond-5dkj2-356285310 3s
└-✔ D dag-diamond-5dkj2-406618167 2s
-
argo logs [job_name]
: workflow内の任意のJobの実行ログを表示
$ argo logs dag-diamond-5dkj2-322730072
A
なお各コマンドやオプションについては--help
で確認することができます(当たり前だけど忘れがち).
$ argo --help
argo is the command line interface to Argo
Usage:
argo [flags]
argo [command]
Available Commands:
...
UI
Argoではコマンドを用いた方法以外にもUIで各workflowの確認ができます(ただし何故かLogだけ何も表示されない...).
$ kubectl -n argo port-forward deployment/argo-ui 8001:8001
# コマンド実行後,ブラウザでlocalhost:8001にアクセスする
Workflow Controlerのスコープ範囲
デフォルトではどのnamespaceからsubmitしてもworkflowは実行されます.
しかし,ConfigMapにスコープ範囲を記述することでWorkflow Controllerの関心の範囲を限定することができます(sample).
例えばnamespace/argo
に限定する場合は次のようなConfigMapを適用します.
apiVersion: v1
kind: ConfigMap
metadata:
name: workflow-controller-configmap
namespace: argo
data:
config: |
namespace: argo
$ kubectl apply -f scope-config.yml
# configmapの内容を反映させるため一度controllerを削除する
# pods/workflow-controllerはdeploymentリソース配下のpodなので,しばらくすると自動的に復活する
$ kubectl delete -n pod workflow-controller-xxxxx
Workflowの定期実行
ネイティブでは時間による定期実行をサポートしていないようです.
考えられる方法としては
- argo-eventsのCalendarゲートウェイを使う
- CronJobリソースを使う
が挙げられます.
今回は今現在argo-eventsがpre-release状態であること,単に時間による定期実行をしたいだけであることから,後者のCronJobを用いた方法を試してみたいと思います.
具体的な方法としてはCronJobでworkflowを起動するJobを定義&適切な権限を持ったサービスアカウントを割り当てることで実現させます.
WorkflowのマニフェストはConfigMapに記述し,それをマウントする方法をとりました.
サービスアカウントについてはargo submit
したときにデフォルトでserviceaccounts/default
が使われるので,これにclusterrole/admin
をbindさせます(この方法は公式ドキュメントの3.Configure the service account to run workflowsに記述されています).
マニフェストファイルは以下のようになります.
apiVersion: v1
kind: ConfigMap
metadata:
name: manifest-cm
namespace: argo
data:
wf-manifest: |
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-diamond-
namespace: argo
spec:
entrypoint: diamond
templates:
- name: echo
inputs:
parameters:
- name: message
container:
image: alpine:3.7
command: [echo, "{{inputs.parameters.message}}"]
- name: diamond
dag:
tasks:
- name: A
template: echo
arguments:
parameters: [{name: message, value: A}]
- name: B
dependencies: [A]
template: echo
arguments:
parameters: [{name: message, value: B}]
- name: C
dependencies: [A]
template: echo
arguments:
parameters: [{name: message, value: C}]
- name: D
dependencies: [B, C]
template: echo
arguments:
parameters: [{name: message, value: D}]
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cron-wf
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: exec-workflow
image: argoproj/argocli:latest
command: ["argo","submit"]
args: ["/argo-wf/wf-manifest"]
volumeMounts:
- name: wf-manifest
mountPath: /argo-wf/
volumes:
- name: wf-manifest
configMap:
name: manifest-cm
失敗したときの通知
ネイティブでの通知機能はありませんが,workflowの終了ステータスを取得できるのでこれを用いることで失敗または成功の通知が可能です.
exampleコードは公式リポジトリにあります(こちら).
onExit
にメインで実行したいtemplateの後に実行したいtemplate名を記述します.
onExit
に記述したtemplate内ではwhen
を用いてworkflow.status
に応じて呼び出すJobを変更します.
このとき呼び出されるJobのimageに"Slackへの通知"や"メール通知"を行うものを用いることで通知機能が実現できます.
なお,Workflow内で使える内部変数の一覧はこちらにまとめられています.
困ったときは...
ドキュメントはこちら
exampleコードはこちら
issueはこちら
困ったときはこれらを覗いてみるとヒントがあるかもしれません.
まとめ
ArgoはWorkflowの定義にKubernetes同様のマニフェストファイルを適用するため,普段からKubernetesを使っている人にとって導入はスムーズに行えるのではないかと思います.
特に既存のJobTemplateをほぼそのまま使えるのはいいですね.
今回はver2.2.1を使いましたが,記事執筆時点でver2.3.0がpre-releaseとなっていました.
バグの修正だけでなく,様々な機能が追加されており,導入を検討している方は見てみると良いかもしれません.
またver2.4-Milestoneでは,"argo-eventsと統合した新しいAPIサーバ" との記述があり,argo-events
とどのように発展していくのか注目したいところです.
apiバージョンはまだalphaと成長途中のArgoですが,完成度は高く,個人的に今のところKubernetes上で動かすWorkflow EngineとしてはArgo一択のような気がしています.
この記事を読んでArgoに興味が湧いた方は,一度GWの空いた時間に触ってみてはいかがでしょうか(といってもGWは今日を含めてあと2日...).