データサイエンスとDevOpsの融合:Jupyter、Git、Kubernetesを用いたMLOps
Kubeflow チームは、DAG を使用しない独立したコントローラーと GitOps を組み合わせた MLOps パイプラインを構築し、Issue ラベリングの自動化を通じて運用負荷を大幅に削減する実践的なアプローチを共有している。
キーポイント
GitOps と reconciler による CI/CD の再定義
従来の DAG ベースのワークフローではなく、各工程(開発、トレーニング、デプロイ)を独立したコントローラーで管理し、宣言的な状態記述によって実際の状態を一致させるアプローチを採用している。
機械学習を活用した Issue 自動ラベリングの実装
GitHub の Issue 増加に対応するため、Google AutoML を活用して Kubeflow 固有のラベル予測モデルを開発し、平均精度 72%、再現率 50% を達成して運用トイルを削減した。
継続的なモデル再トレーニングとデプロイの重要性
新しいデータ流入に対してモデルの効果を維持するため、定期的にモデルを再トレーニングし、CI/CD パイプラインを通じて自動的にデプロイする仕組みの構築が不可欠であると強調している。
Jupyter, Git, Kubernetes の統合活用
モデル開発には Jupyter Notebook を、継続的インテグレーション・デリバリーには GitOps を、基盤インフラには Kubernetes とマネージドクラウドサービスをそれぞれ最適に組み合わせて使用している。
リコンサイラーによるシステムの回復力
リコンサイラーは現在の状態と望ましい状態を比較し、差分に応じてアクションを実行することで、システムが乱された場合でも最終的に所定の状態に戻ることを保証する。
明示的な DAG 不要な宣言的パイプライン
複数の独立したコントローラーを組み合わせることで、明示的な依存関係グラフ(DAG)を作成せずにデータフローを実現でき、各チームが独自のツールを選定できる。
Trainer と Deployer の自律的制御
Trainer はモデルの鮮度を確認して必要に応じて再学習し、Deployer はデプロイ済みモデルと学習済みモデルの整合性を保つために自動でデプロイを行う。
影響分析・編集コメントを表示
影響分析
この記事は、MLOps の実装において従来の複雑なワークフロー管理から脱却し、GitOps とコントローラーパターンを組み合わせる現代的なアーキテクチャを提示しています。特に、運用上の課題解決に ML を活用し、それを CI/CD で自動化する事例は、データサイエンティストと DevOps エンジニアの協業における具体的な指針となり、業界全体の MLOps 成熟度を高める重要な示唆を与えています。
編集コメント
DAG に縛られない柔軟な MLOps アーキテクチャと、運用課題自体を ML で解決する逆転の発想が非常に興味深いです。実務で即座に適用可能なフレームワークとして参考になる内容です。
背景 レコンシラーによるレジリエントシステムの構築
DAGは存在しない
GitOps:プルリクエストによる運用
統合:レコンシラー + GitOps = MLのためのCI/CD 差分の計算
アクチュエーション モデルトレーニング
モデルデプロイ
制御ループ
独自のCI/CDパイプラインを構築する
次のステップ 系譜追跡
参考文献
Kubeflowは、Kubernetes上での機械学習のデプロイと管理を容易にする急成長中のオープンソースプロジェクトです。
Kubeflowの爆発的な人気により、GitHubイシューが大量に流入し、トリアージして適切な専門家に割り振る必要が生じました。下のグラフは、過去1年間に新規に作成されたイシューの数を示しています:

図1: Kubeflowイシューの数
この流入に対応するため、機械学習を使用してイシューに自動的にラベルを付ける「Issue Label Bot」というGitHubアプリへの投資を開始しました。最初のモデルは、GitHub上の人気のあるパブリックリポジトリのコレクションを使用してトレーニングされ、一般的なラベルのみを予測しました。その後、Google AutoMLを使用してKubeflow固有のモデルをトレーニングし始めました。新しいモデルは、平均精度72%、平均再現率50%でKubeflow固有のラベルを予測できるようになりました。これにより、Kubeflowメンテナのイシュー管理に関連する負担が大幅に軽減されました。以下の表は、ホールドアウトセットに対するKubeflow固有ラベルの評価指標を含んでいます。以下の精度と再現率は、私たちのニーズに合わせて調整した予測しきい値に対応しています。
area-centraldashboard
area-components
area-sdk-dsl-compiler
表1: 様々なKubeflowラベルの評価指標
新しいイシューが到着する速度を考えると、モデルを定期的に再トレーニングすることが優先事項となりました。この新しいデータを活用するためにモデルを継続的に再トレーニングしデプロイすることは、モデルの有効性を維持するために重要であると考えています。
私たちのCI/CDソリューションを図2に示します。MLワークフローのステップ(前処理、トレーニング、検証、デプロイなど)を接続するために、有向非巡回グラフ(DAG)を明示的に作成することはありません。代わりに、一連の独立したコントローラーを使用します。各コントローラーは、望ましい世界の状態を宣言的に記述し、実際の世界の状態をそれに一致させるために必要なアクションを実行します。この独立性により、各ステップに最も適したツールを自由に使用することが容易になります。具体的には以下を使用しています:
モデル開発にはJupyterノートブック。
継続的インテグレーションとデプロイにはGitOps。
基盤となるインフラストラクチャにはKubernetesとマネージドクラウドサービス。

図2: 私たちのCI/CDの方法を示しています。現在のパイプラインは、独立して動作する2つのコントローラーで構成されています。トレーナー(左側)は、どのモデルが存在すべきか、つまり私たちのモデルが「新鮮である」とはどういうことかを記述することで設定します。トレーナーは定期的に、トレーニング済みモデルのセットが十分に新鮮かどうかをチェックし、そうでない場合は新しいモデルをトレーニングします。同様に、デプロイヤー(右側)を設定して、デプロイされたモデルがトレーニング済みモデルのセットと同期している状態を定義します。正しいモデルがデプロイされていない場合は、新しいモデルをデプロイします。
モデルトレーニングとデプロイの詳細については、以下の「アクチュエーション」セクションを参照してください。
レコンシラーによるレジリエントシステムの構築
レコンシラーは、レジリエントなシステムを構築する上で非常に有用であることが証明されている制御パターンです。レコンシルパターンは、Kubernetesの動作の中心にあります。図3は、レコンシラーがどのように機能するかを示しています。レコンシラーは、まず世界の状態(例:現在デプロイされているモデル)を観察することで機能します。次に、レコンシラーはこれを望ましい世界の状態と比較して差分を計算します(例:ラベル「version=20200724」のモデルがデプロイされるべきだが、現在デプロイされているモデルはラベル「version=20200700」を持っている)。その後、レコンシラーは世界を望ましい状態に導くために必要なアクションを実行します(例:デプロイされたモデルを変更するためのプルリクエストを作成する)。

図3. 私たちのデプロイヤーによって適用されるレコンシラーパターンの図解。
レコンシラーは、システムがどのように乱されても最終的に望ましい状態に戻るという高い確信度を提供するため、レジリエントなシステムを構築する上で非常に有用であることが証明されています。
DAGは存在しない
コントローラーの宣言的な性質は、DAGを明示的に作成することなく、データが一連のコントローラーを通過できることを意味します。DAGの代わりに、一連のデータ処理ステップは、以下の図4に示すように、一連の望ましい状態として表現することができます:

図4: DAGを明示的にエンコードすることなく、独立したコントローラーからパイプラインがどのように生じるかを示しています。ここには2つの完全に独立したコントローラーがあります。最初のコントローラーは、すべての要素aiに対して要素biが存在すべきことを保証します。2番目のコントローラーは、すべての要素biに対して要素ciが存在すべきことを保証します。
このレコンシラーベースのパラダイムは、多くの従来のDAGベースのワークフローに比べて以下の利点を提供します:
障害に対する回復力:システムは継続的に望ましい状態を達成し維持しようとします。
エンジニアリングチームの自律性の向上:各チームは、自分たちのニーズに合ったツールとインフラストラクチャを自由に選択できます。レコンシラーフレームワークは、コントローラー間の結合を最小限に抑えながら、表現力豊かなワークフローを記述できるようにします。
実戦でテストされたパターンとツール:このレコンシラーベースのフレームワークは、何か新しいものを発明するものではありません。Kubernetesには、コントローラーを簡単に構築できるようにすることを目的とした豊富なツールエコシステムがあります。Kubernetesの人気は、このパターンとサポートツールに精通した大規模で成長中のコミュニティが存在することを意味します。
GitOps:プルリクエストによる運用
GitOps(図5)は、インフラストラクチャを管理するためのパターンです。GitOpsの核となる考え方は、ソース管理(Gitである必要はありません)が、インフラストラクチャを記述する設定ファイルの信頼できる情報源であるべきだということです。コントローラーはソース管理を監視し、設定の変更に応じて自動的にインフラストラクチャを更新できます。これは、変更を加える(または変更を元に戻す)ためには、単にプルリクエストを開けばよいことを意味します。

図5: Label Botに新しいモデルをプッシュするには、使用したいAuto MLモデルのIDを格納している設定マップを更新するPRを作成します。PRがマージされると、Anthos Config Management(ACM)が自動的にそれらの変更をGKEクラスターにロールアウトします。その結果、それ以降の予測は新しいモデルを使用して行われます。(画像提供:Weaveworks)
統合:レコンシラー + GitOps = MLのためのCI/CD
背景を説明したので、レコンシラーとGitOpsのパターンを組み合わせてMLのためのCI/CDをどのように構築したかについて詳しく見ていきましょう。
解決する必要があった問題は3つありました:
望ましい状態と実際の状態の差分をどのように計算するか?
実際の状態を望ましい状態に一致させるために必要な変更をどのように実行するか?
1と2を継続的に実行する制御ループをどのように構築するか?
差分の計算
差分を計算するために、私たちは単に必要なことを正確に行うラムダを記述します。したがって、この場合、2つのラムダを記述しました:
最初のラムダは、最新モデルの経過時間に基づいて再トレーニングが必要かどうかを判断します。
2番目のラムダは、最新のトレーニング済みモデルと、ソース管理にチェックインされた設定マップにリストされているモデルを比較することによって、モデルを更新する必要があるかどうかを判断します。
これらのラムダを単純なWebサーバーでラップし、Kubernetesにデプロイします。このアプローチを選択した理由の1つは、Kubernetesのgit-syncに依存して、リポジトリをポッドボリュームにミラーリングさせたかったからです。これにより、すべてのGit管理はgit-syncを実行するサイドカーによって処理されるため、ラムダは非常にシンプルになります。
必要な変更を適用するために、私たちはTektonを使用して
原文を表示
Background Building Resilient Systems With Reconcilers
There is no DAG
GitOps: Operation By Pull Request
Putting It Together: Reconciler + GitOps = CI/CD for ML Computing Diffs
Actuation Model Training
Model Deployment
The Control Loop
Build Your Own CI/CD pipelines
What’s Next Lineage Tracking
Further Reading
Kubeflow is a fast-growing open source project that makes it easy to deploy and manage machine learning on Kubernetes.
Due to Kubeflow’s explosive popularity, we receive a large influx of GitHub issues that must be triaged and routed to the appropriate subject matter expert. The below chart illustrates the number of new issues opened for the past year:

Figure 1: Number of Kubeflow Issues
To keep up with this influx, we started investing in a Github App called Issue Label Bot that used machine learning to auto label issues. Our first model was trained using a collection of popular public repositories on GitHub and only predicted generic labels. Subsequently, we started using Google AutoML to train a Kubeflow specific model. The new model was able to predict Kubeflow specific labels with average precision of 72% and average recall of 50%. This significantly reduced the toil associated with issue management for Kubeflow maintainers. The table below contains evaluation metrics for Kubeflow specific labels on a holdout set. The precision and recall below coincide with prediction thresholds that we calibrated to suit our needs.
area-centraldashboard
area-components
area-sdk-dsl-compiler
Table 1: Evaluation metrics for various Kubeflow labels.
Given the rate at which new issues are arriving, retraining our model periodically became a priority. We believe continuously retraining and deploying our model to leverage this new data is critical to maintaining the efficacy of our models.
Our CI/CD solution is illustrated in Figure 2. We don’t explicitly create a directed acyclic graph (DAG) to connect the steps in an ML workflow (e.g. preprocessing, training, validation, deployment, etc…). Rather, we use a set of independent controllers. Each controller declaratively describes the desired state of the world and takes actions necessary to make the actual state of the world match. This independence makes it easy for us to use whatever tools make the most sense for each step. More specifically we use
Jupyter notebooks for developing models.
GitOps for continuous integration and deployment.
Kubernetes and managed cloud services for underlying infrastructure.

Figure 2: illustrates how we do CI/CD. Our pipeline today consists of two independently operating controllers. We configure the Trainer (left hand side) by describing what models we want to exist; i.e. what it means for our models to be “fresh”. The Trainer periodically checks whether the set of trained models are sufficiently fresh and if not trains a new model. We likewise configure the Deployer (right hand side) to define what it means for the deployed model to be in sync with the set of trained models. If the correct model is not deployed it will deploy a new model.
For more details on model training and deployment refer to the Actuation section below.
Building Resilient Systems With Reconcilers
A reconciler is a control pattern that has proven to be immensely useful for building resilient systems. The reconcile pattern is at the heart of how Kubernetes works. Figure 3 illustrates how a reconciler works. A reconciler works by first observing the state of the world; e.g. what model is currently deployed. The reconciler then compares this against the desired state of the world and computes the diff; e.g the model with label “version=20200724” should be deployed, but the model currently deployed has label “version=20200700”. The reconciler then takes the action necessary to drive the world to the desired state; e.g. open a pull request to change the deployed model.

Figure 3. Illustration of the reconciler pattern as applied by our deployer.
Reconcilers have proven immensely useful for building resilient systems because a well implemented reconciler provides a high degree of confidence that no matter how a system is perturbed it will eventually return to the desired state.
There is no DAG
The declarative nature of controllers means data can flow through a series of controllers without needing to explicitly create a DAG. In lieu of a DAG, a series of data processing steps can instead be expressed as a set of desired states, as illustrated in Figure 4 below:

Figure 4: illustrates how pipelines can emerge from independent controllers without explicitly encoding a DAG. Here we have two completely independent controllers. The first controller ensures that for every element ai there should be an element bi. The second controller ensures that for every element bi there should be an element ci.
This reconciler-based paradigm offers the following benefits over many traditional DAG-based workflows:
Resilience against failures: the system continuously seeks to achieve and maintain the desired state.
Increased autonomy of engineering teams: each team is free to choose the tools and infrastructure that suit their needs. The reconciler framework only requires a minimal amount of coupling between controllers while still allowing one to write expressive workflows.
Battle tested patterns and tools: This reconciler based framework does not invent something new. Kubernetes has a rich ecosystem of tools that aim to make it easy to build controllers. The popularity of Kubernetes means there is a large and growing community familiar with this pattern and supporting tools.
GitOps: Operation By Pull Request
GitOps, Figure 5, is a pattern for managing infrastructure. The core idea of GitOps is that source control (doesn’t have to be git) should be the source of truth for configuration files describing your infrastructure. Controllers can then monitor source control and automatically update your infrastructure as your config changes. This means to make a change (or undo a change) you just open a pull request.

Figure 5: To push a new model for Label Bot we create a PR updating the config map storing the id of the Auto ML model we want to use. When the PR is merged, Anthos Config Management(ACM) automatically rolls out those changes to our GKE cluster. As a result, subsequent predictions are made using the new model. (Image courtesy of Weaveworks)
Putting It Together: Reconciler + GitOps = CI/CD for ML
With that background out of the way, let’s dive into how we built CI/CD for ML by combining the Reconciler and GitOps patterns.
There were three problems we needed to solve:
How do we compute the diff between the desired and actual state of the world?
How do we affect the changes needed to make the actual state match the desired state?
How do we build a control loop to continuously run 1 & 2?
Computing Diffs
To compute the diffs we just write lambdas that do exactly what we want. So in this case we wrote two lambdas:
The first lambda determines whether we need to retrain based on the age of the most recent model.
The second lambda determines whether the model needs to be updated by comparing the most recently trained model to the model listed in a config map checked into source control.
We wrap these lambdas in a simple web server and deploy on Kubernetes. One reason we chose this approach is because we wanted to rely on Kubernetes’ git-sync to mirror our repository to a pod volume. This makes our lambdas super simple because all the git management is taken care of by a side-car running git-sync.
To apply the changes necessary, we use Tekton to glue together various CLIs that we use to perform the various steps.
To train our model we have a Tekton task that:
Runs our notebook using papermill.
Converts the notebook to html using nbconvert.
Uploads the .ipynb
This notebook fetches GitHub Issues data from BigQuery and generates CSV files on GCS suitable for import into Google AutoML. The notebook then launches an AutoML job to train a model.
We chose AutoML because we wanted to focus on building a complete end to end solution rather than iterating on the model. AutoML provides a competitive baseline that we may try to improve upon in the future.
To easily view the executed notebook we convert it to html and upload it to GCS which makes it easy to serve public, static content. This allows us to use notebooks to generate rich visualizations to evaluate our model.
Model Deployment
To deploy our model we have a Tekton task that:
Uses kpt to update our configmap with the desired value.
Runs git to push our changes to a branch.
Uses a wrapper around the GitHub CLI (gh) to create a PR.
The controller ensures there is only one Tekton pipeline running at a time. We configure our pipelines to always push to the same branch. This ensures we only ever open one PR to update the model because GitHub doesn’t allow multiple PRs to be created from the same branch.
Once the PR is merged Anthos Config Mesh automatically applies the Kubernetes manifests to our Kubernetes cluster.
We picked Tekton because the primary challenge we faced was sequentially running a series of CLIs in various containers. Tekton is perfect for this. Importantly, all the steps in a Tekton task run on the same pod which allows data to be shared between steps using a pod volume.
Furthermore, since Tekton resources are Kubernetes resources we can adopt the same GitOps pattern and tooling to update our pipeline definitions.
The Control Loop
Finally, we needed to build a control loop that would periodically invoke our lambdas and launch our Tekton pipelines as needed. We used kubebuilder to create a simple custom controller. Our controller’s reconcile loop will call our lambda to determines whether a sync is needed and if so with what parameters. If a sync is needed the controller fires off a Tekton pipeline to perform the actual update. An example of our custom resource is illustrated below:
apiVersion: automl.cloudai.kubeflow.org/v1alpha1 kind: ModelSync metadata: name: modelsync-sample namespace: label-bot-prod spec: failedPipelineRunsHistoryLimit: 10 needsSyncUrl: http://labelbot-diff.label-bot-prod/needsSync parameters: - needsSyncName: name pipelineName: automl-model pipelineRunTemplate: spec: params: - name: automl-model value: notavlidmodel - name: branchName value: auto-update - name: fork value: git@github.com:kubeflow/code-intelligence.git - name: forkName value: fork pipelineRef: name: update-model-pr resources: - name: repo resourceSpec: params: - name: url value: https://github.com/kubeflow/code-intelligence.git - name: revision value: master type: git serviceAccountName: auto-update successfulPipelineRunsHistoryLimit: 10
The custom resource specifies the endpoint, needsSyncUrl, for the lambda that computes whether a sync is needed and a Tekton PipelineRun, pipelineRunTemplate, describing the pipeline run to create when a sync is needed. The controller takes care of the details; e.g. ensuring only 1 pipeline per resource is running at a time, garbage collecting old runs, etc… All of the heavy lifting is taken care of for us by Kubernetes and kubebuilder.
Note, for historical reasons the kind, ModelSync, and apiVersion automl.cloudai.kubeflow.org are not reflective of what the controller actually does. We plan on fixing this in the future.
Build Your Own CI/CD pipelines
Our code base is a long way from being polished, easily reusable tooling. Nonetheless it is all public and could be a useful starting point for trying to build your own pipelines.
Here are some pointers to get you s
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み