<-- mermaid -->

Kubernetes ネイティブなワークフローエンジン Argo Workflows のご紹介

こんにちは。Product Team の竹原です。

先日、社内用の小さな Web アプリを追加開発する機会がありまして、その際に CI/CD パイプラインの作成も行いました。
CI/CD パイプライン構築のために Argo Workflows を使ってみたところ非常に感触がよかったので、感想などを記載しておこうと思います。

※ 今回は概要の紹介程度となります。実際に作成したワークフローはまた次回ご紹介します。

Argo Workflows とは

Argo Workflows はタイトルで書いたとおりワークフローエンジンです。
エンジニアの身近なところで言えば近いのは Jenkins とか GitHub Actions とかでしょうか。
公式サイト では以下のように紹介されています。

Argo Workflows is an open source container-native workflow engine for orchestrating parallel jobs on Kubernetes.

Argo Workflows は、Kubernetes 上で並列ジョブをオーケストレーションするための、オープンソースのコンテナネイティブなワークフローエンジンです。

ここで重要なのは、

  • Kubernetes 上で動作する
  • コンテナネイティブ

というところだと思っています。

ワークフローを YAML で書いて Argo Workflows に与えると、GUI 上からそのワークフローを実行することができます。

公式のワークフローサンプル

https://argoproj.github.io/argo-workflows/walk-through/steps/
↑ ワークフロー上で3回 Hello world する公式サンプルです。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: steps-
spec:
  entrypoint: hello-hello-hello

  # This spec contains two templates: hello-hello-hello and whalesay
  templates:
  - name: hello-hello-hello
    # Instead of just running a container
    # This template has a sequence of steps
    steps:
    - - name: hello1            # hello1 is run before the following steps
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello1"
    - - name: hello2a           # double dash => run after previous step
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello2a"
      - name: hello2b           # single dash => run in parallel with previous step
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello2b"

  # This is the same template as from the previous example
  - name: whalesay
    inputs:
      parameters:
      - name: message
    container:
      image: docker/whalesay
      command: [cowsay]
      args: ["{{inputs.parameters.message}}"]

spec.entrypointhello-hello-hello なので、spec.templates.namehello-hello-hello であるワークフローがこのワークフローで実行されるもの、となります。
このワークフローを GUI から実行すると以下のようになります。

ワークフローを実行したときの GUI

ワークフロー内のステップ hello2ahello2b が縦に積まれているのは、並列実行されているからです。

spec:
  templates:
    steps:
    - - name: hello1
        (中略)
    - - name: hello2a
        (中略)
      - name: hello2b

↑ 一部抜粋ですが、並列処理は上記のように書きます。
これは hello1 が実行完了したら hello2ahello2b を並列実行する、という書き方です。
ハイフン2つ - - の step に続いてハイフン1つ - の step を書くと、それらが並列実行されます。
Jenkins に Groovy ファイルを与えて動かすパイプラインとかだと並列処理時は parallel { } で括る必要があったりして、行が増えたりインデントが増えたり面倒ですが、Argo Workflows ではシンプルに並列処理を書けます。

上記のサンプルだけでは「コンテナを動かせるのは分かったけど、例えばシェルスクリプト書く場合はどうすれば?」という疑問があると思いますので、Git のチェックアウトだけをするステップの例を示します。

spec:
  templates:
    - name: checkout
      inputs:
        parameters:
          - name: repo
      script:
        image: alpine/git
        command: [sh]
        source: |
         ls /src || mkdir /src
         cd /src
         git clone {{inputs.parameters.repo}}

こんな感じでシェルスクリプトを書けます。この例だと「認証情報はどう渡せば?」「volume に保存するには?」という追加の疑問があるかと思いますがそれはまた次回に...

Argo Workflow ならではの便利さとか工夫とか

私は Argo Workflows を CI/CD パイプラインの基盤として使用しました。
そのなかでの便利さや工夫などをご紹介します。

利用するツールや動作環境のバージョンをコントロールしやすい

当然といえば当然なのですが、Argo Workflows ではワークフローが 100% コンテナ上で動作するので、javanodemvnjqskaffold といった各種ツール・動作環境のバージョン指定はすべて Dockerfile 上で行えます。

共通サーバ上に Jenkins を立てていたりすると、「他のプロジェクトで使ってる mvn のバージョンが古すぎて、うちのプロジェクトだと使えないけど、最新の mvnPATH に含めちゃうと他プロジェクトが壊れる」といった事象があったりなかったりすると思います。
Argo Workflows ではそのような「バージョン差を生んでしまい、他が壊れる」といったことは気にしなくて良いんです。

Kubernetes のサービス名で名前解決ができるので、E2E テストがやりやすい

今回私が追加開発したアプリケーション (CI/CD の対象) は、フロントエンド (Vite 製) が1つ、バックエンド (Flask 製) が1つ、という構造でした。
そのどちらにも E2E テストが存在し、その E2E テストを流すためには、テスト対象のアプリケーションと、テスト対象から通信が走る先のモック (Wiremock) を起動し、テスト対象からモックに通信ができる必要があります。

E2E 実行環境に直接フロントエンドとバックエンドをデプロイしているなら、http://localhost:16000 など localhost に向けた通信で事足りるのですが、
私達は「E2E の実行環境はできるだけ本番環境と同じにする」という考えで Kubernetes 上にテスト対象をすべてデプロイしています。(もちろん本番環境のアプリはすべて Kubernetes 上で動いてます)

そのため、私達の E2E 実行環境はいつも以下のように構成されます。

いつもの E2E 実行環境

しかし Argo Workflows のワークフローはそれ自体が Kubernetes 上で動くので、以下のようになります。

Argo Workflows を使う際の E2E 実行環境

覚えづらかったり変わったりする可能性のある Node IP ではなく、Kubernetes の名前解決の仕組みを使ってテスト対象にアクセスできるので非常に便利です。

並列処理をシンプルに書ける

先に紹介したように、ハイフン2つ - - の記法により並列処理をシンプルに書くことができます。
しかし並列処理には更に別の記法もあり、以下の YAML は

    - - name: hello2a           # double dash => run after previous step
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello2a"
      - name: hello2b           # single dash => run in parallel with previous step
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello2b"

以下のように書いても同様の挙動になります。

    - - name: hello2a           # double dash => run after previous step
        template: whalesay
        arguments:
          parameters:
          - name: message
            value: "{{item.message}}"
        withItems:
          - { message: "hello2a" }
          - { message: "hello2B" }

withItems は、引数の個数分だけそのステップを並列実行してくれます。そのため、上記例の2つは全く同じ動きをします。
つまり処理をテンプレート化しておけば、「複数のリポジトリの並列チェックアウト」「Skaffold を使ってフロントエンドとバックエンドをビルド」といった似たような処理の繰り返しは簡単に書けます。

ステップをテンプレート化できる(Workflow Templates)

上で紹介した hello-hello-hello の例では1ファイル内にすべてを記述してありましたが、ファイル分割することもできます。
例えば以下の YAML で1ファイル作っておくと、

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: say
spec:
  templates:
  - name: whalesay
    inputs:
      parameters:
      - name: message
    container:
      image: docker/whalesay
      command: [cowsay]
      args: ["{{inputs.parameters.message}}"]

上記を他ファイルから以下のように参照できます。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: steps-
spec:
  entrypoint: hello-hello-hello
  templates:
  - name: hello-hello-hello
    steps:
    - - name: hello1
        templateRef:
          name: say
          template: whalesay
        arguments:
          parameters:
          - name: message
            value: "hello1"

Cluster Workflow Templates と Workflow Templates

上記ではステップ whalesay をテンプレート化しました。
これは kind: WorkflowTemplate として定義しましたので、Workflow Template という括りのテンプレートになります。
この他に kind: ClusterWorkflowTemplate として定義することができ、これは Cluster Workflow Template になります。

Workflow Template として定義すると、Kubernetes 内の同一 namespace からしかそのテンプレートを参照できませんが、
Cluster Workflow Template として定義すると、別の namespace からもそのテンプレートを参照することができます。

そのため、Git の clone や commit -> push、Skaffold を使ったアプリのビルド、といった他のワークフローにも転用できそうなテンプレートは Cluster Workflow Template にしておくと良さそうです。

おわりに

「Jenkins と大差なくない?」と思いながら使い始めた Argo Workflows でしたが、新しめのツールだけあって細かいところで便利さが発揮されていて、かなり使い心地がよかったです。

次回がもしあれば、実践編ということでワークフローの具体的な YAML についてご紹介しようと思います。

Page top