Wodom! のデータパイプライン、 Kubeflow Pipelines の CI/CD

こんにちは、JDSCの松井です。

今回はデータ基盤ソリューション Wodom! での Kubeflow Pipelines を用いたデータパイプラインについて、特に CI/CD の構成をメインに書いてみたいと思います。

Kubeflow Pipelines 上のデータパイプライン運用で直面した課題

Wodom! では、 Kubeflow Pipelines を用いてデータ処理のパイプラインを運用しています。

初期検証の段階では、マルチテナントの構成を手軽に実現しつつ、汎用的なパイプラインの運用が可能そうだと考えていましたが、実際に運用してみると、パイプラインのデプロイに関して次のような課題があることがわかってきました。

  1. イメージの更新は imagePullPolicy を Always にすることで上書きによってアップデートすることができるが、パフォーマンスに問題があるほか、できればイメージタグに紐づくイメージはイミュータブルに固定したい

  2. Kubeflow Pipelines の制限で、パイプラインの構成を記述した マニフェスト(Argo の Emissary Executor をバックエンドとしているため、実体は Argo の Workflow リソース) が変更されると、新しいパイプラインのバージョンをリリースしなければならない

  3. パイプラインのマニフェストが変更されているにもかかわらず、リリースを忘れると、イメージとマニフェスト内の齟齬が生まれ、バグに繋がる

  4. Kubeflow Pipelines の Recurring Run (定期実行)の仕様で、一度登録した Recurring Run のパイプラインのマニフェストは、一つのバージョンに固定されてしまう

  5. Recurring Run によって起動された Run (一つの実行単位)は、 Clone (複製)すると RUN_ID_PLACEHOLDER (RunのIDを置き換えるプレースホルダ)が更新されないため、 RUN_ID_PLACEHOLDER に依存した処理がエラーとなる

解決策

Wodom! では、現状の解決策として 1, 2, 3 に関してはパイプラインのデプロイワークフローの自動化と統一、 4 に関しては Recurring Run を再登録するバッチ処理の実装、 5 に関しては新しく Run を作成するという対処療法をとっています。
以下でそれぞれについて詳しく書いていきます。

パイプラインのデプロイワークフローの自動化と統一

1, 2, 3 の問題は、

  • パイプラインのバージョンを更新しなくても、ある時点で bug fix などの変更が行われれば、スムーズに変更を反映でき、それ以降のパイプラインの実行ではデプロイ古いパイプラインは起動しないようにしたい

  • あるイメージタグがどの git commit に紐づくかはイミュータブルに固定しておきたい

という 2 つの両立しづらいモチベーションによって発生しています。
そこで、

  • バージョン指定でのイメージタグと、あるイメージの紐付きは、開発中の変更の激しい状況では変更されうる

  • ただしイメージタグと git commit の紐付きは固定

  • 最初にあるバージョンをリリースしたら、 git tag を打つことで、あるパイプラインのバージョンのスタート地点が分かるようにする

という妥協点を見つけ、その上で上記の手続きを自動化しました。

また、

  • あるバージョンをリリースするときにそのバージョンがすでにリリースされていないか

  • イメージタグの紐付けを更新する際に、パイプラインのマニフェストに変更があり、互換性の問題が発生しうる状況ではないか

については、自動的にバリデーションを行うことで、ミスの防止を図りました。

Recurring Run を再登録するバッチ処理の実装

Wodom!では次の図のように、 gRPC を用いてアプリケーションサーバと Kubeflow Pipelines のサーバ間で通信を行っています。

Recurring Run の登録も、この通信経由で行っているので、Recurring Run のパイプラインのバージョンを上げるには同じ経路を用いることができます。

そこで、アプリケーション側のDBの情報と、単体の Recurring Run の登録に用いているロジックを再利用し、 Recurring Run のパイプラインバージョンをまとめて更新するためのバッチ処理を実装しました。

これにより、パイプライン側に更新があり、アプリケーション側ですでに作成していた Recurring Run のパイプラインバージョンを更新したい時は、このバッチ処理を用いることで、任意のタイミングでバージョンの更新が可能になりました。

また、アプリケーションのロジックを再利用することで、アプリケーション側でパイプラインの呼び方に変更があった場合にも対応が可能です。

新しく Run を作成するという対処療法

Recurring Run によって起動された Run のマニフェストは、 Recurring Run 設定時にマニフェストを一部変換する処理が挟まっているため、5 に書いたようなプレースホルダの処理に問題が発生します。

そのため、 Recurring Run を経由しないことで、対症療法的ではありますが、正しくパイプラインを起動することができます。

少々面倒ではありますが、現状はこちらの解決策で問題は回避できるようになりました。

今後の課題

また、「Kubeflow Pipelines 上のデータパイプライン運用で直面した課題」で挙げた、 5 のプレースホルダの問題は、仕様というよりはバグのように思えるので、改善できないか考えていきたいです。

すでに Recurring Run の現状の課題は大きな Issue となっており、 Pull Request も上がりつつあるようなので、コントリビューションする場合はコミュニティの様子を見つつ進めることになりそうです。

解決策の二つ目に挙げた「Recurring Run を再登録するバッチ処理の実装」については、アプリケーション側、パイプライン側、それぞれの修正に対応できる分、注意すべき互換性の問題が複雑になりつつあります。

Recurring Run で常に最新のパイプラインを使用するオプションがあっても良い気はしますが、それではマニフェストに記載済みのパイプラインの引数などに変更があった場合に、互換性の問題が生じると考えられます。

この問題は宣言的な処理の記述をどう更新していくかという本質的に難しい問題だと思うので、今後も整理を進めていきたいです。

おわりに

Kubeflow Pipelines は OSS として Github でホストされており、自由に開発に参加することができます。自分も先日 Istio 関連の Issue 解決のため Pull Request を提出し、マージすることができました。

Slack などのコミュニティ運営もされているので、気軽に参加してみるのも良いかもしれません。

コミュニティのカレンダーも Kubernetes などでコード管理されておりこれだけでも調べてみると面白そうです。

今回お話しした内容とは離れますが、今後は Kubeflow Pipelines ならではのデータバリデーション機能や機械学習関連機能の活用もやっていきたいと思っています。

最後まで読んでいただきありがとうございました!

この記事が気に入ったらサポートをしてみませんか?