二足早くArgoのCronWorkflowを触ってみる

**注意** この記事はWIPのPRに基づいて書かれているためリリース版と大きく仕様が異なる可能性があります。(2019/12/1現在)

## はじめに
ArgoはKubernetes上でジョブを制御することを目的としたコンテナネイティブなワークフローエンジンです。ジョブの並列制御やリトライ設定ができるのはもちろんのこと、必要な時だけコンテナが立ち上げるのでリソースを専有するなく効率的に実行することができます。Argoにはその他にも様々な機能がありますが今回は割愛します。

筆者が担当しているプロダクトでは当初、他のワークフローエンジンを導入しましたがKubernetesとの相性が悪かったためArgoに乗り換えました。しかし、いざ運用しようとすると所々に辛い部分がありました。その辛みの一つがスケジュール実行機能が無いことです。

このスケジュール実行機能がArgoのネイティブとしてサポートされそうです。2019/12/1現在、[WIPのPR](https://github.com/argoproj/argo/pull/1758)の状態ですが待ちきれずローカルでビルドして触ってみました。

## Argoのスケジュール実行の現状
Digdagなどのワークフローエンジンではスケジュール実行をサポートしていますが、Argoは単体でこれをサポートしていません。Argoをスケジュール実行させる方法は主に２つ紹介されています。

### KubernetesのCronJob
KubernetesのCronJob内で`argo submit`コマンドを実行することでキックします。詳しくは[こちらの記事](https://qiita.com/chez-shanpu/items/f175aafc5c995b1f63b3#workflow%E3%81%AE%E5%AE%9A%E6%9C%9F%E5%AE%9F%E8%A1%8C)で紹介されています。


### Argo EventsのCalendar
Argo EventsはArgo Projectの一つであり、さまざまイベントソースをきっかけにArgoをキックすることができます。その中にCalendarというイベントソースがありCron形式でスケジュール実行を定義することができます。筆者のプロダクトではCloud Pub/Subをきっかけにトリガーしたいワークフローもあったためこちらの方法を取りました。しかし、最初にGatewayやSensorといった特有の概念を理解する必要があり、さらに複数のワークフローを扱う際にSensor定義が複雑なので使いこなすのに時間が必要でした。

いずれにしても、WorkflowをConfigMapに保存したりYAMLにインラインで定義する必要があり単にスケジュール実行したいという欲求に対してひと手間もふた手間も必要な状態です。

## コミュニティでの議論
[こちらのIssue](https://github.com/argoproj/argo/issues/1229)でCron形式でのWorkflowの実行について議論されています。前項で示したように、やろうと思えばできなくはないものの複雑な設定が必要であり、やはりワークフローエンジンとしてスケジュール実行は基本でありネイティブでサポートすべきだとの声があがりました。仕様はKubernetesのJobとCronJobの関係と同様に現状のCustom Resource Definition(CDR)であるWorkflowとは別にCronWorkflowを新たに定義することが提案されました。そして現在、simster7さんが[こちらのPR](https://github.com/argoproj/argo/pull/1758)で実装してくださっています。この機能は2020年1月のv2.5に含まれることが予定されているので議論開始からおおよそ1年でリリースされることになります。

## CronWorkflowの仕様
KubernetesのCronJobのすべてのオプションをサポートし、既存のWorkflowを簡単にCronWorkflowに変換できるようにするとの指針が掲げられています。

具体的には下記のオプションが追加される予定とのことです。

- schedule  
通常のcronフォーマット形式の他にKubernetesのCronJobに用いられているライブラリと同じものが使われているので`@hourly`といった指定もできます
- startingDeadlineSeconds
-  concurrencyPolicy
- suspend
-  successfulJobsHistoryLimit / failedJobsHistoryLimit

## 実際に触ってみた
PRに示されている例を参考に実装途中のブランチをローカルでビルドしてGKEにデプロイし実際にCronWorkflowを動かしてみました。

ローカルでのビルドとデプロイは[CONTRIBUTING.md](https://github.com/argoproj/argo/blob/master/CONTRIBUTING.md)に従えばわりと簡単にできます(最後の`helm install`がなぜかできなかったため[インストール用のmanifest](https://github.com/argoproj/argo/blob/master/manifests/install.yaml)のworkflow-controllerのimageを自前ビルドしたものを指定してデプロイしました)。今回はargoコマンドに新たなコマンドが追加されているのでCLIのビルドも必要です。

PR上で示されてるサンプルを実行してみます。

```yaml:test-cron.yaml
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: test-cron-wf
spec:
  schedule: "* * * * *"
  concurrencyPolicy: "Replace"
  startingDeadlineSeconds: 0
  workflowSpec:
    entrypoint: whalesay
    templates:
    - name: whalesay
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["date"]
```

`argo submit`ではなくK8sのCronJobと同様に`argo cron create`コマンドを使います。

```console:作成
% ./dist/argo cron create test-cron.yaml
Name:                          test-cron-wf
Namespace:                     default
Created:                       Sun Dec 01 13:30:01 +0900 (now)
Schedule:                      * * * * *
Suspended:                     false
StartingDeadlineSeconds:       0}
ConcurrencyPolicy:             Replace
```

一覧では前回実行時間も確認できます。

```console:一覧
% ./dist/argo cron list
NAME           AGE   LAST RUN   SCHEDULE    SUSPENDED
test-cron-wf   1m    3s         * * * * *   false
```

現状のArgo UIではそれぞれが単体で実行されたように表示されます。
<img width="1020" alt="argo-ui.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86712/8a30cd34-fca2-2330-d316-eb5a7c87d100.png">

Workflowと同様にPodとして認識されています。
<img width="656" alt="gke.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86712/b0a4a986-ad43-144a-f877-c77a604eb151.png">

現時点で`successfulJobsHistoryLimit`と`failedJobsHistoryLimit`は未実装のため試せていませんが、[PodGCのPR](https://github.com/argoproj/argo/pull/1234)で将来的に欲しい機能とされてた直近のいくつかのPodを残す機能が実質的に別の形で実装されるということになりそうです。ちなみにCronWorkflowとPodGCは併用ができました。

##今後の期待(個人の意見)
さらに期待するとするならばArgo UIの改善です。**CronWorkflow単位での表示**と**UIからの手動実行**が実装されれば他のワークフローエンジンと遜色なく使えるのではないかと思います。今までは実行の度にWorkflow定義をアップロードする形式でしたがCronWorkflowではWorkflow定義をArgoが保持しておく形になるので、これらを実現することが可能になるのではないかと予想しています。

## おわりに
現在、実装途中のArgoのCronWorkflowについてIssueやPRをもとに仕様や実際に使った様子を紹介しました。CronWorkflowの登場により実際のプロダクトへの導入や運用が簡単になるのではないかと感じています。

### 余談
Argoを筆者のプロダクトに導入した際は実際の運用に必要な機能が不足していることを認識しつつ「K8sでワークフローエンジンを使うならArgo一択なのできっと必要な機能は後からついてくるだろう」と考え正直先走った意思決定をしました。なので、このような機能がArgoに実装されていくことは非常にありがたいです。simster7さんをはじめArgoプロジェクトのコントリビュータの方々には頭があがりません。この場を借りて感謝申し上げます。