こんにちは。メルコインでソフトウェアエンジニアをしている@goroです。
この記事は、Merpay & Mercoin Advent Calendar 2024 の記事です。
本記事は自分の所属するチームが管理するマイクロサービスにおいて、ワークフローエンジンであるArgo Workflowsを導入し複数のバッチの制御を行ったので、その際に得た知見を共有します。
Argo Workflowsとは
Argo Workflowsは、Kubernetes上で並列ジョブをオーケストレーションするためのオープンソースのコンテナネイティブなワークフローエンジンです。Argo WorkflowsはKubernetesのCRD(Custom Resource Definition)として実装されています。CRDはKubernetes APIを拡張して独自のリソースを定義するものです。
ArgoWorkflowを利用することで、各ワークフローをkubernetesのマニフェストで定義することができます。ワークフローはDAGとしてモデル化されているので、タスク間の複雑な依存関係を表現することができます。
また、ワークフローの各ステップはKubernetesのPodとして実行されます。そのため、ユーザーがArgo Workflowsに詳しくなくても、Kubernetesについての知識があれば比較的容易に構築できます。また通常のKubernetesのPodで特定のタスクを実行できるなら、それをワークフローステップでも同様に実行できるのです。
私たちの開発するシステムはバッチが多く、その依存関係が複雑で、Kubernetes CronJobでこれらのバッチを管理するのが難しくなってきました。そこでワークフローエンジンとしてArgo Workflowsを導入し各バッチを管理するようになりました。
Argo Workflowsのアーキテクチャ
次にArgo Workflowsの内部アーキテクチャについて簡単に説明します。
Argo WorkflowsはWorkflowなどのCRDとWorkflow Controller、Argo Serverという2つのDeploymentで構成されます。Workflow Controllerはリコンサイルを行い、Argo ServerはAPIを提供します。なお、Controllerは単独で使用することもできます。
Workflow Controllerと Argo Server は両方とも Argo Workflows namespaceで実行されます。ワークフローとそこから生成される Pod は別のnamespaceで実行されることが一般的です。
それぞれの役割は以下のようなものになっています
Workflow Controllerの役割
Workflow ControllerはKubernetesのCRDを監視します。これにより、新しいワークフローが作成されたり、既存のワークフローが更新されたりしたときに適切に処理します。
ユーザーが定義したワークフローの各ステップをKubernetes上のポッドとしてスケジュールします。これには依存関係の解決や並列実行の管理も含まれます。
各タスクの実行状態をリアルタイムで更新し、ワークフロー全体の進行状況を把握します。成功、失敗、中断などの各状態を管理します。
タスクが失敗した場合の再試行ロジックや、失敗の際の回復手順を実行します。これにより、信頼性の高いワークフロー実行が可能になります。
Argo Serverの役割
ユーザーが操作できるAPIサーバーを提供します。
ワークフローの管理、ログのアクセス、ワークフローのステータス確認のためのユーザーインターフェースとAPIエンドポイントを提供します。
ユーザーとKubernetesのバックエンドをつなぐ役割を果たし、ワークフローの管理と可視化を容易にします。
Argo WorkflowsでWorkflowが実行されるまでの流れは以下のようなものになります。
- Userがワークフローをkubectl applyなどを利用しSubmitします。
- Argo Serverがワークフローのカスタムリソースを作成します。
- Workflow Controllerがそのカスタムリソースを検出します。
- Workflow Controllerは、Kubernetes APIを用いてポッドを作成し、ステータスを監視します。このプロセスは、ワークフローが完了するまでループします。Podの実行終了後は次のステップのPodを作成します。
Argo WorkflowsでWorkflowを定義する
今回、自分たちのサービスでArgo Workflowsを利用する上でWorkflowのカスタムリソースは利用していません。WorkflowTemplateというカスタムリソースでWorkflowを定義し、それをCronWorkflowというスケジュール実行したいワークフローを定義するリソースから呼び出す形でWorkflowを実行しています。
それぞれのリソースについては以下に簡単にまとめています。
WorkflowTemplateは、再利用可能なテンプレートとして複数のWorkflowやCronWorkflowで使用できるため、今回はWorkflowTemplateを利用しました。記載方法に関してはkindが異なるだけでWorkflowとWorflowTemplateは同じ内容になるため、WorkflowをWorflowTemplateにリネームするだけで簡単に再利用できるリソースを作成することができます。
Workflow
Workflowは、複数のタスクやジョブを定義し、それらを一貫したシーケンスや依存関係に基づいて実行するためのフレームワークやプロセスです。具体的には、下記のような要素を含みます。
タスクの定義: 具体的な作業内容(例えば、データ処理やモデルのトレーニングなど)
依存関係: あるタスクが他のタスクに依存している場合、その順序を定義。
実行環境: タスクが実行される場所(クラウド、オンプレミスなど)。
実行管理: タスクのスケジューリング、リトライ、エラーハンドリングなど。
例:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-world-
spec:
entrypoint: whalesay
templates:
- name: whalesay
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello world"]
WorkflowTemplate
WorkflowTemplateは、再利用可能なテンプレートとして複数のWorkflowで使用できる、Workflowの定義を含んでいます。具体的には、特定のタスクやシーケンスを標準化して、一貫した方法で繰り返し使用することができます。workflowTemplateRefでWorkflowやCronWorkflowから参照され利用されます。TemplateにはClusterTemplateWorkflowsという全てのnamescpaceで再利用するためのTemplateも存在します。
例:
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: hello-world-template
spec:
templates:
- name: whalesay-template
container:
image: docker/whalesay:latest
command: [cowsay]
args: ["hello from template"]
CronWorkflow
CronWorkflowは、Cronジョブのようにスケジュールに基づいて定期的に実行されるワークフローです。具体的には、特定の時間や間隔で自動的に実行されるように設定が可能です。
例:
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: hello-world-cron
spec:
schedule: "0 0 * * *" # 毎日午前0時に実行
workflowSpec:
workflowTemplateRef:
name: hello-world-template # 先ほど定義したWorkflowTemplateの名前を指定
またワークフローの依存関係を定義する方法は以下のStepsとDAGの2つの方法があります。
STEPS
STEPSでは一連のステップでタスクを定義することができます。テンプレートの構造は「リストのリスト」となっています。外側のリストは順次実行され、内側のリストは並行して実行されます。
以下の例ではA実行後にB-1とB-2がパラレルに実行されます。
- name: step-sample
steps:
- - name: A
template: test
- - name: B-1
template: test
- name: B-2
template: test
DAG
DAGは、タスクを依存関係のグラフとして定義することができます。DAGでは、すべてのタスクをリストし、特定のタスクが開始される前に完了しなければならない他のタスクを設定します。依存関係のないタスクは即座に実行されます。
以下の例の場合はA->B->C->Dの順で実行されます。
- name: dag-sample
dag:
tasks:
- name: A
template: test
- name: B
dependencies: [A]
template: test
- name: C
dependencies: [B]
template: test
- name: D
dependencies: [C]
template: test
これらのCRDと依存関係の定義を利用することで、以下のような順番にタスクを実行するWorkflowを作成することができました。
こちらは実際のArgo Workflows Webコンソール上のスクリーンショットを掲載しています。WebコンソールではこちらのようなWorkflowの実行詳細で各Workflowの実行状況をリアルタイムで確認できます。また、コンソール上から簡単にリトライの実行やログの確認を行うこともできます。さらには定義されたWorkflowTemplateやCronWorkflowの一覧も確認することができ、それらを利用してWorkflowの実行をコンソール上からワンクリックで行うことが可能です。
今回Argo Workflowsを利用して感じたメリット
最後に今回Argo Workflowsの導入により、感じたいくつかのメリットを紹介します。
まず第一に、Argo WorkflowsがKubernetesネイティブなワークフローエンジンであり、各タスクをKubernetesのPodとして実行するため、他のKubernetesリソースと同様に、YAML形式のマニフェストを使ってワークフローを記述できる点が非常に便利でした。これにより開発者は既存のKubernetes知識を活用して比較的簡単にワークフローを構築できました。
さらに、ワークフローはコンソール上で直感的に可視化されるため、それぞれのタスクがどのように接続され、実行されているかを一目で把握できます。タスクの失敗時には容易にリトライが可能で、完了したタスクのログもすぐにアクセスできるため、迅速なトラブルシューティングが実現します。
また、このようなArgo Worksflowsのメリットは開発者以外の方にも大きな恩恵をもたらしました。例えば、QAチームはKubernetesの複雑なジョブ管理の詳細を知らなくても、kubctlコマンドなどを利用せずコンソール上からタスクを実行できるようになり、QAの効率化にも良い影響が発生しました。
まとめ
今回は、Argo Workflowsを利用して複数バッチの制御を行った事例について書きました。今後Argo Workflowsを導入される方の参考に少しでもなれば幸いです。次の記事は kobaryoさんです。引き続きお楽しみください。