Kubernetesネイティブなワークフローエンジンとは!FAANSでArgo Workflowsを導入した話

OGP

はじめに

こんにちは。ブランドソリューション開発本部 バックエンド部 SREの笹沢(@sasamuku)です。

ZOZOではショップスタッフの販売サポートツール「FAANS」を2022年8月に正式リリースしました。FAANSはアパレルのショップスタッフ様を支援する様々な機能を提供しています。例えば、ZOZOTOWN上で実店舗の在庫取り置きができる機能や、コーディネート投稿の機能などがあります。投稿されたコーディネートはZOZOTOWNやWEAR、Yahoo!ショッピングに連携が可能で、今後はブランド様のECサイトとも連携できる予定です。これによりお客様のコーディネート選びをサポートし購買体験をより充実したものにします。機能の詳細に関しましては下記プレスリリースをご覧ください。

corp.zozo.com

今回はFAANSで採用しているワークフローエンジン「Argo Workflows」について、その採用理由や構成例をご紹介します。ワークフローエンジンの採用を検討している方、Kubernetesネイティブなワークフローエンジンに興味をお持ちの方の参考になれば幸いです。

なお本稿は単体でもお読みいただけますが、過去の記事をご覧いただくとFAANSの技術変遷をよりご理解いただけます。

techblog.zozo.com techblog.zozo.com

目次

ワークフローエンジン選定の過程

本章では、Kubernetesネイティブなワークフローエンジンに対する考察を交えながら、Argo Workflowsの導入を決めた過程をご説明します。

ワークフローエンジン導入の背景

FAANSが提供する機能の1つに「成果確認」があります。これはショップスタッフ様が投稿したコーディネートに対する閲覧数や経由売上といった指標を成果として可視化するものです。この機能の裏側ではそれらのデータを集計するワークフローが必要となりますが、実装当時のFAANSにはワークフローを実行する基盤はまだありませんでした。

どのツールでワークフローを実行するか検討するために、まずFAANSに求められる最低限の要件を書き出しました。それが下記になります。

  • スケジュール実行
  • リトライ機構
  • タスク間依存関係の表現
  • Webコンソールでの手動実行やログの確認1

スケジュール実行やリトライ機構が備わっていることは大前提です。加えて、複雑なワークフローの構成にはタスク間の依存関係を表現できなければなりません。タスク間の依存関係とは、例えば「タスクAとBの完了を待ってからCを実行する」というものです。さらに開発者体験も重要です。ログの確認やワークフローの再実行はWebコンソールでできると便利です。

よって、これらの要件を満たすワークフローエンジンを選定する必要がありました。

代表的なワークフローエンジンの紹介

まずは、フラットな視点で代表的なワークフローエンジンを調査しました。スター数が1kを超えるワークフローエンジンをいくつか抜粋すると以下のようなものがあります2。()内は2022/10時点のスター数になります。

それぞれの特徴を簡単に見ていきます。

LuigiとApache AirflowはPythonでワークフローを記述します。これらは機械学習での利用が多いのですが、より一般的なユースケースにも利用可能です。Pythonによるツール独自の記法も含むため慣れていない場合は一定の学習が必要になります。

Tekton PipelinesとArgo WorkflowsはKubernetesネイティブなワークフローエンジンであり、Kubernetesマニフェストを使ってワークフローを記述します。Kubernetesの利用が前提となるため、運用基盤が整っていない場合は導入の障壁が高いです。

Digdagはdigファイルと呼ばれる独自ファイルにワークフローを定義します。記法はYAMLに近くシンプルな表現が可能です。ZOZOでは採用しているプロダクトが多いためノウハウは蓄積されています。

Kubernetesネイティブなワークフローエンジン

FAANSでは「Kubernetesネイティブなワークフローエンジン」を採用する方針にしました。FAANSはKubernetesを採用しており、エコシステムの1つとして迎え入れる基盤が既にありました。そして、後述の「Kubernetesネイティブであることの利点の大きさ」が決め手となりました。

Kubernetesネイティブとは

ここでは「Kubernetesネイティブ」を「Kubernetesで動かすことを前提に設計された」と定義しています。つまりコンテナでのタスク実行を前提としたワークフローエンジンと言えます。では、Kubernetesネイティブであると何がよいのでしょうか。次のような利点があると考えます。

  • ワークフロー定義をマニフェストで管理できる
  • ワークフローエンジンの移行性が高い
  • リソース利用効率が高い

それでは1点ずつ詳解していきます。

ワークフロー定義をマニフェストで管理できる

ワークフロー定義をマニフェストで管理できるということは、DeploymentやIngressといった既存のKubernetesリソースと同じ方法でワークフロー定義を扱えるということです。ここでの「ワークフロー定義」は「どのようなタスクをどのような順番で実行するかの決まりごと」という抽象的な意味合いで捉えてください。

次の図はワークフローがKubernetes上で実行されるまでの流れを示しています。

Flow until workflows are executed on Kubernetes

【1】のステップではワークフロー定義をマニフェストに落とし込みます。【2】のステップではマニフェストをkube-apiserver経由でクラスタに登録します。そして【3】のステップで実際にPod上で処理が実行されます。実際にはカスタムリソースの作成もありますがここでは省略しています。

このようにワークフロー定義をマニフェストとして管理できることで次のメリットがあります。

  • GitOpsのプラクティスを適用
  • Control loopによる制御
  • データストアとしてetcdを使用

一点ずつ補足していきます。

GitOpsとはGitを一元的なソースとしてKubernetesリソースを管理する手法です3。ワークフロー定義をマニフェストとして管理することでGitOpsのプラクティスを適用できます。これによりKubernetes上のワークフローが自動的にGitの状態と一致するようになります。

続いて、Control loopについてです。Control loopはKubernetesにおける更新ロジックです。現実状態をマニフェストに記載された理想状態へ収束させることができます。これを実現するのがControllerです。ApplyされたワークフローはControllerによって監視されます。そして、ControllerがControl loopを回すことでワークフローが常に理想状態と一致するように更新されます。

最後にデータストアについてです。ワークフロー定義をマニフェストで管理できるということは、これらの保存のためにデータストアを別途用意する必要がありません。なぜならマニフェストはetcdに保存されるからです。etcdはKubernetesクラスタ内部のコンポーネントで、クラスタに登録される全ての情報が保存されます。別途データベースを構成するとEOLや脆弱性対応に運用負荷がかかるため、このメリットは大きいと感じます。ただし、ログの永続化にはロギングサービスやデータベースが必要となります。あくまでワークフロー定義を保存する上で別途データストアは不要ということですのでご注意ください。

以上から、ワークフロー定義をマニフェストで管理することにより、Kubernetesの既存の仕組みをパワフルに活用したワークフローの運用が可能だと分かります。

ワークフローエンジンの移行性が高い

Kubernetesネイティブなワークフローエンジンではワークフロー内の各タスクはコンテナで実行されます。どのタスクをどのような順番で実行するかはマニフェストで記述されますが、タスク内でどのような処理を実行するのかという関心事はコンテナに寄せられます。これによりワークフローを利用するシーンにおいても、コンテナ一般のメリットである「可搬性」を享受できます。これには具体的に次のようなメリットがあります。

  • ローカルで簡単な動作検証ができる
  • ワークフローエンジンの移行性が高い

2点目は特に強力です。タスクを独自仕様や特定言語で定義する場合と比較し、コンテナでパッケージングされたタスクは再利用可能です。つまり、コンテナをサポートする他ワークフローエンジンへの移行が比較的容易です。加えて、規模縮小によりワークフローエンジンを廃止したとしてもKubernetes Job/Kubernetes CronJobで動作させることが可能です。

リソース利用効率が高い

Kubernetesネイティブなワークフローエンジンはリソース利用効率が高く、相性のよいマネージドサービスと組み合わせることでコストパフォーマンスを上げることができます。

ここでの「リソース利用効率」とは、アサインされたコンピューティングリソースに対して、実質的に利用されるリソースの割合を意味しています。ワークフローで必要となるリソースとアサインされるリソースに過不足がないほど、リソース利用効率の高い状態となります。ワークフローという処理形態では、必要なときに必要な分だけリソースを確保できればよいため、要求に応じてアサイン量を調整できることがより重要となります。

この点において、Kubernetesネイティブなワークフローエンジンは優秀です。なぜなら、タスクをPodとして切り出して実行できるからです。Kubernetesのマネージドサービスが提供する機能を利用すれば、Podの需要に合わせてNodeをスケールできます4。さらに、Pod単位でスケール可能なサービス5を利用すればさらに効率が高まります。このように、Kubernetesネイティブなワークフローエンジンをクラウド環境のマネージドサービスで動作させることにより、リソース利用効率ひいてはコストパフォーマンスを高めることができます。

Argo Workflowsの選定理由

以上から、選定候補として残ったのがKubernetesネイティブなワークフローエンジンであるTekton PipelinesとArgo Workflowsの2つでした。最終的にArgo Workflowsを選択しましたが、どちらもFAANSにおける最低限の要件は満たしていました。

その上でArgo Workflowsを選んだ理由は、既にArgo CDを運用しており親和性があったからです。具体的にはArgo CD Dexと連携可能であり認証のための設定を1から構築する手間が省けるという点です。

Argo Workflowsの構成

本章ではArgo Workflowsをどのような構成で利用しているかを解説していきます。Argo Workflowsの基本的な概念からFAANSでの構成例まで説明しています。

Argo Workflowsのコアコンセプト

まずはじめにArgo Workflowsのコアコセプトをご紹介します。主なカスタムリソースとしてWorkflowWorkflowTemplateCronWorkflowがあります。それぞれの概要は下表の通りです。

カスタムリソース 説明
Workflow 最も基本的なリソース。ワークフローの定義と実行ステータスを持つ。
WorkflowTemplate 頻繁に利用するワークフローをテンプレートとして定義するリソース。
CronWorkflow スケジュール実行したいワークフローを定義するリソース。

全体構成

全体観を掴んでいただくためにマニフェスト作成からワークフロー実行までの構成を下図に示します。なお、Kubernetes環境にはGKE Autopilotを利用しています。

Overall Argo Workflows architecture

ワークフロー実行の流れ

基本的な流れは前述の通りです。ワークフロー定義を落とし込んだマニフェストをKubernetesクラスタに登録し、Podでタスクが実行されます。ワークフローのデプロイにはArgo CDを用いています。Argo CDはGitOpsを実現するCI/CDツールです。Argo Workflowsにおけるワークフロー定義はマニフェストとして作成されるため、GitOpsのプラクティスを適用できます。これによりKubernetesの実状態をGitHubのソースへ自動的に追従させることができます。

マニフェストの分散管理

アプリケーションリポジトリを監視するArgo CD Applicationの存在に違和感を感じた方もいらっしゃるかと思います。FAANSでは従来、Kubernetesのマニフェストは全てインフラリポジトリで管理していました。しかし、開発チームが運用するワークフローに限り、それらのマニフェストをアプリケーションリポジトリに切り出して管理しています。これには開発サイドの裁量でワークフローを管理できるため開発効率がよいという利点があります。Twelve-Factor Appにあるコードベースの思想から逸脱しているようにも見えますが、現時点で大きな課題は生じておらずメリットの方が大きいと感じています。

Workflowの構成

FAANSでのWorkflowの構成例をご紹介します。主に利用するカスタムリソースはWorkflowTemplateCronWorkflowの2つです。WorkflowTemplateに再利用可能なワークフローをまとめておき、それをCronWorkflowから呼び出すという構成を基本としています。これによりマニフェストの責任範囲が明確になり運用管理がしやすくなります。

文章だけでは分かりにくいのでサンプルマニフェストを基に説明していきます。

下記のWorkflowTemplate公式サンプルをFAANSでの利用形態に近づけたものです。templatesフィールドは大きく2つのテンプレート種別に分かれています。便宜上、本稿では部品テンプレートと製品テンプレートと呼称します。この例では、whalesay-templateが部品テンプレート、mainmain-on-mondayが製品テンプレートになります。製品テンプレートは部品テンプレートを1以上利用して目的の処理を実現します。このような構成にすることでテンプレートをDRYに保ちます。

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: workflow-template
spec:
  templates:
  - name: whalesay-template # 部品テンプレート
    inputs:
      parameters:
      - name: message
    container:
      image: docker/whalesay
      command: [cowsay]
      args: ["{{inputs.parameters.message}}"]
  - name: main # 製品テンプレート
    dag:
      tasks:
      - name: inner-A
        template: whalesay-template # 部品テンプレートを使用
        arguments:
          parameters:
          - name: message
            value: hello world # パラメータ注入
      - name: inner-B
        dependencies: [inner-A]
        template: whalesay-template
        arguments:
          parameters:
          - name: message
            value: hello again
  - name: main-on-monday # 製品テンプレート
    dag:
      tasks:
      - name: inner-A
        template: whalesay-template
        arguments:
          parameters:
          - name: message
            value: hello monday

続いて、CronWorkflowのサンプルを見ていきます。こちらは上のWorkflowTemplateの製品テンプレートであるmain-on-mondayを呼び出し、月曜日のみ実行されるスケジュールを組んでいます。CronWorkflowには、どの製品テンプレートをいつ呼び出すのか、履歴をどのくらい保持するのかといった関心だけを詰め込むようにしています。

apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: cron-workflow-on-monday
spec:
  schedule: "0 6 * * 1" # 毎週月曜午前6時に実行
  timezone: "Asia/Tokyo"
  startingDeadlineSeconds: 0
  concurrencyPolicy: "Allow"
  successfulJobsHistoryLimit: 4
  failedJobsHistoryLimit: 4
  suspend: false
  workflowSpec:
    entrypoint: main
    templates:
      - name: main
        steps:
          - - name: run
              templateRef:
                name: workflow-template
                template: main-on-monday # 製品テンプレートを呼び出し

以上のような構成にすることで、WorkflowTemplateCronWorkflowの責務の違いが明確になり、開発や運用がしやすくなります。なお、ワークフローの内容や数量によって適切な構成は異なりますのでご留意ください。

Webコンソールの紹介

最後にArgo WorkflowsのWebコンソールをご紹介します。

ログイン画面

Webコンソールにアクセスするとログイン画面が表示され、一番左のログインボタンを押下するとSSOでログインできます。SSOを実現するためにArgo CD Dexとの連携およびAzure ADでのSAML認証を利用しています。また独自ドメインによるHTTPSでのアクセスをGoogleマネージドSSL証明書で可能にしています。

Argo Workflows login screen

Workflow一覧

実行されたWorkflowの一覧を確認できます。表示する件数6や保持期間7を、成功・失敗の場合に分けて柔軟に設定できます。

Argo Workflows Workflow list screen

Workflow詳細

一覧画面から項目を押下すると実行結果の詳細を確認できます。タスク間の依存関係と成否がグラフィカルに表示されます。

Argo Workflows successful Workflow details screen

失敗すると次のような表示になります。タスクを押下すると詳細が表示され、実行後のPodを保持していればMAIN LOGSよりログを確認できます。なお最後尾の成功しているタスクはSlack通知です。

Argo Workflows failed Workflow details screen

WorkflowTemplate一覧

クラスタに登録されたWorkflowTemplateの一覧を確認できます。

Argo Workflows WorkflowTemplate list screen

WorkflowTemplate詳細

一覧画面から任意の項目を押下すると詳細を確認できます。SUBMITボタンを押下することでWebコンソールからもWorkflowの実行が可能です。マニフェストの編集も可能であるため検証に役立ちます。

Argo Workflows WorkflowTemplate details screen

CronWorkflow一覧

クラスタに登録されたCronWorkflowの一覧を確認できます。WorkflowTemplateと異なりSCHEDULENEXT RUNが表示されています。

Argo Workflows CronWorkflow list screen

CronWorkflow詳細

一覧画面から任意の項目を押下すると詳細を確認できます。WorkflowTemplate同様に手動による実行や、マニフェストの編集が可能です。

Argo Workflows CronWorkflow details screen

まとめ

Kubernetesネイティブなワークフローエンジンの特徴とFAANSにおけるArgo Workflowsの構成をご紹介しました。ワークフロー定義をマニフェストで管理できる点は非常に強力ですし、移行性やリソース利用効率の高さも魅力的です。既にクラウド環境でKubernetesを運用しており、かつ、ワークフローエンジンの導入を検討されている方にとっては有力な候補となります。本稿が検討の一助となれば幸いです。

おわりに

最後に、ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

corp.zozo.com


  1. ここでの「ログの確認」とはPod内の永続化されていないログをWebコンソールから参照することを指します。

  2. 他にもKubeflow PipelinesPrefectなどあります。

  3. GitOpsの詳細はCircleCI社のブログが参考になりますのでご参照ください。

  4. 例えばGoogle Cloud Platformであればcluster autoscalernode auto-provisioningが該当します。

  5. Pod単位でスケール可能なサービスにはAmazon EKS on AWS FargateGKE Autopilotがあります。

  6. CronWorkflowではオプションを使ってWorkflowの保持件数を設定できます。

  7. ttlStrategyを利用してWorkflow毎に保持期間を設定できます。

カテゴリー