Microsoft developer platform documentation update解説:SynapseML CIコンテナ化の変更点と確認すべき対応

Microsoft developer platform documentation update: ci: containerize CI pipeline with pre-built Docker imageは、SynapseMLのCIパイプラインを事前ビルド済みDockerイメージ中心に再構成する変更です。結論から言うと、SynapseMLの利用コードやAPIを直接変える更新ではありません。対応が必要なのは、SynapseMLの開発・検証・ビルド・リリースに関わる開発者、Azure Pipelinesの管理者、CI/CD基盤を保守しているチームです。

この変更の狙いは、CI実行のたびにJDK、Conda、SBT、Spark、テストデータなどを準備するのではなく、あらかじめ整備したDockerイメージ内でビルドやテストを実行することです。PR上の説明では、CIの実行時間が約66分から約27分へ短縮され、ウォールクロック時間で59%削減、総計算時間で33.5%削減したとされています。(GitHub)

目次

Microsoft developer platform documentation updateの要点

今回のMicrosoft developer platform documentation updateで押さえるべきポイントは、単なる「Docker化」ではありません。CIの実行環境、依存関係、キャッシュ、テストタイムアウト、Azure Container Registryへの接続方式まで含めて、パイプライン全体の再現性と実行効率を上げる変更です。

PR #2529は、SynapseMLのCIパイプライン全体を事前ビルド済みDockerイメージで動かすことを目的としています。新規ファイルとしてtools/docker/ci/Dockerfileとtemplates/free_disk.ymlが追加され、pipeline.yaml、build.sbt、TestBase.scalaなども変更対象になっています。(GitHub)

確認項目変更内容実務上の意味
CI実行環境Ubuntu 22.04ベースのCI用Dockerイメージを使用ホストVM依存を減らし、CI結果の再現性を高める
依存関係JDK 8、Miniconda、SBT、Spark 3.5、テストデータなどを事前準備ジョブごとのセットアップ時間を削減
イメージ管理ACR上のmmlsparkmcr.azurecr.io/synapseml/ciを利用レジストリ接続・認証設定の確認が必要
キャッシュ依存ファイルの内容ハッシュに基づきCIイメージを再利用依存関係が変わらない場合、イメージ再構築を避けられる
テストUnitTestsのスコープ付きコンパイル、10分のper-testタイムアウトなどを導入長時間停止するテストや不要な全体コンパイルを抑制
ディスク対策free_disk.ymlでホスト側ディスクを整理Microsoft-hosted agentの容量不足を避けやすくする

何が変わったのか

もっとも大きな変更は、CIジョブの実行環境を「都度セットアップするホストVM」から「必要な依存関係を先に詰めたDockerコンテナ」へ寄せている点です。Azure Pipelinesでは、コンテナージョブを使うことで、ジョブに必要なOS、ツール、依存関係のバージョンをより細かく制御できます。Microsoft Learnでも、コンテナーは特定環境でジョブを実行するための要素を提供し、ホスト上の既定環境よりもタスク実行コンテキストを制御しやすいと説明されています。(Microsoft Learn)

今回のPRでは、CI用DockerfileにJDK 8、Conda環境、Azure CLI、Spark、Node.js、SBT、Coursierキャッシュ、テストデータなどが組み込まれています。Dockerfile上ではUbuntu 22.04をベースに、Temurin JDK 8、Spark 3.5.0、SBT 1.10.11、Minicondaの固定バージョンなどが使われています。(GitHub)

CIイメージを先に作るBuildCIImageジョブが追加された

pipeline.yamlにはBuildCIImageジョブが追加されています。このジョブは、environment.yml、project/plugins.sbt、project/build.properties、build.sbt、sonatype.sbt、tools/docker/ci/Dockerfileなどの内容からハッシュを作り、同じ内容のイメージが既に存在すれば再利用し、なければビルドしてACRへpushする構成です。(GitHub)

これにより、依存関係が変わっていないにもかかわらず毎回重い環境構築をやり直す、という無駄を減らせます。PR説明では、キャッシュヒット時のBuildCIImageは約30秒で済むとされています。(GitHub)

多くのテスト・ビルド・publishジョブがcontainer: ciで動く

pipeline.yamlでは、Style、Publish、DatabricksE2E、PythonTests、RTests、WebsiteSamplesTests、UnitTestsなどのジョブにcontainer: ciが設定されています。これにより、ジョブ内の処理はホストVMに直接依存するのではなく、CI用コンテナ内で実行されます。(GitHub)

Azure PipelinesのYAMLスキーマでは、resources.containers.containerでコンテナーイメージ、レジストリ、endpoint、環境変数、ボリューム、起動オプションなどを定義できます。今回のようにプライベートレジストリ上のイメージを参照する場合は、サービスエンドポイントやサービス接続の設定確認が重要です。(Microsoft Learn)

テストデータの再ダウンロードを避ける仕組みが入った

build.sbtでは、テストデータ取得時にDATASET_CACHE配下のキャッシュを確認し、存在すればそこからコピーする処理が追加されています。存在しない場合のみURLからダウンロードする流れです。(GitHub)

これは小さく見えて、CI時間には効きやすい変更です。並列ジョブが多いリポジトリでは、同じテストデータを各ジョブが毎回取得するだけで、ネットワーク待ち、外部ストレージの一時的な失敗、ジョブ時間のばらつきが増えます。キャッシュ済みデータを使えば、テスト開始までの時間を短くし、失敗原因をテストそのものに寄せやすくなります。

UnitTestsはPROJECT変数でコンパイル対象を絞る

UnitTestsのmatrixにはPACKAGEに加えてPROJECTが設定され、各ジョブが必要なモジュールだけをコンパイルする方向に変更されています。たとえば、LightGBM系はPROJECT: "lightgbm"、OpenCV系はPROJECT: "opencv"、Cognitive Services系はPROJECT: "cognitive"のように分けられています。(GitHub)

これは、CI高速化で特に重要です。テスト対象が一部なのに毎回リポジトリ全体をコンパイルしていると、並列化しても全ジョブで同じ準備処理が重複します。PROJECT単位で絞ることで、各ジョブの準備時間を減らせます。

テストのハング対策として10分のper-testタイムアウトが入った

TestBase.scalaでは、TestBaseがTimeLimitsを継承し、各テストに10分のグローバルタイムアウトを設定する処理が追加されています。必要なサブクラスではtestTimeoutInSecondsを上書きできる設計です。(GitHub)

CIで厄介なのは、失敗するテストよりも「終わらないテスト」です。失敗なら原因調査に進めますが、ハングするとエージェント時間を消費し、後続ジョブの待ち時間も増えます。今回の変更は、テスト失敗を隠すためではなく、異常に長いテストを早期に検出するための運用改善と考えるべきです。

誰が対応すべきか

SynapseMLを単にライブラリとして利用しているだけなら、通常はアプリケーションコードの修正は不要です。注意すべきなのは、SynapseML本体をビルドする人、forkして独自CIを回している人、Azure Pipelinesで類似構成を使っている人です。

立場対応の必要性確認すべきこと
SynapseMLをアプリで利用する開発者低API変更ではないため、通常は依存バージョンやリリースノートの確認で十分
SynapseMLのforkを保守する開発者高pipeline.yaml、Dockerfile、ACR参照、テストタイムアウトを自リポジトリへ取り込むか判断
Azure Pipelines管理者高ACRサービス接続、Workload Identity Federation、Dockerレジストリ権限を確認
CI/CD基盤担当者高コンテナージョブ対応、ディスク容量、キャッシュ戦略、イメージ更新手順を確認
テスト担当者中10分タイムアウトで落ちる長時間テストがないか確認
セキュリティ担当者中事前ビルドイメージの脆弱性スキャン、権限、レジストリ公開範囲を確認

影響範囲はCI/CDが中心

今回のMicrosoft developer platform documentation updateは、SynapseMLのユーザー向け機能追加というより、開発基盤の改善です。影響範囲を見誤ると、不要なアプリ改修に時間を使ったり、逆にCI基盤の確認を後回しにしたりしがちです。

直接影響を受けるもの

直接影響を受けるのは、主に以下です。

  • Azure PipelinesのYAML定義
  • CI用Dockerfile
  • ACR上のCIイメージ
  • テストデータのキャッシュ
  • SBTの実行オプション
  • UnitTests、PythonTests、RTestsなどのジョブ時間
  • テストタイムアウトの扱い
  • ホストVMのディスク整理処理

PR説明では、UnitTestsの平均が18.0分から13.1分、PythonTestsの平均が28.4分から19.6分へ短縮したとされています。全体としては、壁時計時間が約66.3分から26.5〜27.5分へ短縮されたという測定結果が示されています。(GitHub)

直接影響を受けにくいもの

一方で、次のような領域は直接の変更対象ではありません。

  • SynapseMLの公開API
  • 既存アプリケーションの推論・学習ロジック
  • 利用者側のSparkジョブ実行環境
  • SynapseMLをpipやMavenから利用するだけのワークロード

ただし、ライブラリ本体の開発やfork運用をしている場合は別です。CIが変わると、テストの失敗条件、依存関係の解決方法、ビルド時間、キャッシュの扱いが変わるため、開発フローに影響します。

移行・設定確認で見るべきポイント

同じようなCIコンテナ化を自社プロジェクトに取り入れる場合、単にcontainer: ciを付けるだけでは不十分です。レジストリ認証、Dockerfileの保守、キャッシュ境界、ディスク容量、テスト設計までまとめて確認する必要があります。

ACRサービス接続と認証方式を確認する

今回のPRでは、CIイメージがmmlsparkmcr.azurecr.io/synapseml/ciに公開され、Azure Pipelines側ではACRエンドポイントとWorkload Identity Federation認証を使う構成が説明されています。(GitHub)

Azure Pipelinesのサービス接続ドキュメントでは、Azure Container Registryへの接続方式としてService Principal、Managed Identity、Workload Identity federationが挙げられています。特にWorkload Identity Federationを使う場合、シークレットの長期保存やローテーション負荷を減らせる一方で、接続名、利用許可、対象レジストリ、サブスクリプション設定の確認が必要です。(Microsoft Learn)

確認すべき項目は次の通りです。

確認箇所確認内容失敗しやすいポイント
サービス接続名YAMLのendpointやDockerタスクの接続名と一致しているか表示名変更後にYAML側が古いまま
ACR権限pull、push、manifest inspect、retagに必要な権限があるかpullだけ許可され、pushで失敗する
利用許可パイプラインがサービス接続を使えるよう承認されているか初回実行時に権限承認で止まる
認証方式Workload Identity Federationを使うのか、Service Principalを使うのかforkや別プロジェクトで同じ接続が使えない
イメージタグci-latestと内容ハッシュタグの扱いを理解しているかlatestだけを見て、実際の内容差分を追えない

Dockerfileの「便利さ」と「固定化」のバランスを見る

CI用Dockerfileには、JDK、Conda、Spark、Node.js、SBT、Pythonパッケージ、Rライブラリ、テストデータなどが入ります。便利ですが、依存関係を詰め込みすぎるとイメージが大きくなり、脆弱性対応や更新確認が重くなります。

今回のDockerfileでは、MinicondaやSBTのバージョンを固定し、libssl1.1の取得時にSHA256検証を行い、権限を755にするなど、再現性と安全性に配慮した実装が含まれています。(GitHub)

自社CIに応用する場合は、次の判断基準が役立ちます。

事前にDockerイメージへ入れるべきものジョブ内で都度実行してもよいもの
毎回同じバージョンで必要なJDK、SDK、CLIブランチごとに変わるアプリのビルド成果物
ダウンロードに時間がかかるが変更頻度が低い依存関係秘密情報を含む設定ファイル
テスト全体で共通利用する静的データ実行ごとに生成すべき一時データ
SBTやMavenなどの依存解決キャッシュ本番デプロイ先に依存する設定

ポイントは、「変わりにくいものはイメージへ、変わるものはジョブへ」です。ここを間違えると、毎回イメージを作り直すことになり、コンテナ化したのに速くならないという結果になります。

ディスク容量の余裕を確認する

コンテナージョブでも、ホスト側のディスク容量問題は消えません。Dockerイメージのpull、ビルドコンテキスト、レイヤー、テスト成果物、キャッシュが同じエージェント上に積み上がるためです。

今回追加されたfree_disk.ymlでは、Android SDK、Google Cloud SDK、dotnet、GHCなど、SynapseMLのCIで不要と考えられるホスト側ディレクトリを削除し、df -h /で空き容量を確認する簡潔な処理が入っています。(GitHub)

Microsoft-hosted agentを使う場合、エージェントの初期環境には多くのツールが入っています。便利な反面、大きなDockerイメージを扱うCIでは空き容量不足の原因になります。CIコンテナ化後に突然no space left on deviceが出る場合は、依存関係ではなくディスク容量が原因のことがあります。

テストタイムアウトの影響を確認する

10分のper-testタイムアウトは、CIの無限待ちを防ぐうえで有効です。ただし、既に長時間かかることが分かっているE2Eテスト、外部API待ちのテスト、大規模データ処理テストでは、正常でもタイムアウトする可能性があります。

移行時は、次の順番で確認すると安全です。

手順確認内容
既存CIのテスト時間を取得10分を超える単体テスト、E2Eテスト、外部サービス依存テストを洗い出す
長時間テストを分類遅い理由がデータ量、外部API、リトライ、Spark初期化のどれかを確認する
タイムアウトを上書き本当に必要なテストだけtestTimeoutInSecondsを上書きする
不要な待ちを削る固定sleep、過剰なリトライ、外部サービス待ちを見直す
CIログを確認タイムアウトで落ちたのか、テスト失敗で落ちたのかを区別する

単純にタイムアウトを長くするだけでは、CI高速化の効果が薄れます。長くすべきテストと、短く失敗させるべきテストを分けることが重要です。

注意すべき不一致: FabricE2Eの扱いは実ファイルで確認する

PR本文の説明では、FabricE2EはFabric SDKやsempyパッケージがCIコンテナに含まれないため、意図的にホストVM上で実行すると記載されています。(GitHub)

一方で、現在参照できるブランチ上のpipeline.yamlでは、FabricE2Eジョブにもcontainer: ciが設定されており、コメントでもFabricE2EがCIコンテナ内で動く旨が記載されています。(GitHub)

このような差分がある場合、PR本文だけで判断しないほうが安全です。最終的に取り込む際は、次の順で確認してください。

確認対象見るべき内容
最新のpipeline.yamlFabricE2Eにcontainer: ciがあるか
最新コミットPR本文と実装が一致しているか
実行ログFabricE2EがホストVMで動いたのか、コンテナ内で動いたのか
必要パッケージFabric SDK、sempy、認証情報がCIコンテナで利用可能か
失敗時のログ依存パッケージ不足なのか、Fabric APIや認証の問題なのか

ドキュメント更新やPR説明は、レビュー過程で実装とずれることがあります。CI基盤の変更では、必ず「説明」「YAML」「実行ログ」の3点を照合しましょう。

自社CIに応用する場合の進め方

SynapseMLのように依存関係が多いプロジェクトでは、CIコンテナ化の効果が出やすいです。ただし、最初から全ジョブをコンテナ化すると、失敗時の切り分けが難しくなります。移行は段階的に行うのが現実的です。

まずは遅いジョブと重複セットアップを洗い出す

最初にやるべきことは、Dockerfileを書くことではありません。CIログから、どの処理に時間がかかっているかを確認します。

見るべき指標は次の通りです。

  • 環境構築にかかる時間
  • 依存関係ダウンロードの時間
  • テスト実行時間
  • コンパイル時間
  • キャッシュヒット率
  • 同じ処理が複数ジョブで重複していないか
  • 外部サービスへの待ち時間
  • エージェントのディスク不足発生回数

SynapseMLのPRでは、SBT依存関係の事前取得、Sparkの事前ダウンロード、テストデータのキャッシュ、Conda環境の事前構築などが高速化の主な要素になっています。つまり、効果が出るのは「毎回同じものを準備している部分」です。

小さく始めるならStyleやUnitTestから始める

最初にコンテナ化しやすいのは、外部サービス依存が少ないStyleチェックやUnitTestです。これらは失敗原因が比較的分かりやすく、コンテナ化による環境差分の検出にも向いています。

反対に、Databricks E2E、Fabric E2E、外部API連携、GPU依存のジョブは後回しにしたほうが安全です。外部サービスの状態、認証、ネットワーク、SDK差分が絡むため、コンテナ化の問題なのか、サービス側の問題なのか切り分けにくいからです。

イメージ更新のトリガーを明確にする

CI用イメージは、一度作って終わりではありません。依存ファイルやDockerfileが変わったら更新し、変わらない場合は再利用する仕組みが必要です。

今回のPRでは、依存ファイル群のSHA256ハッシュからci-xxxxxxxxxxxxのようなタグを作り、既存イメージがあれば再タグ付けし、なければビルドする流れになっています。(GitHub)

自社CIでも、以下のようなファイルをハッシュ対象にすると管理しやすくなります。

技術スタックハッシュ対象の例
Node.jspackage-lock.json、pnpm-lock.yaml、Dockerfile
Pythonrequirements.txt、pyproject.toml、poetry.lock、Dockerfile
Java/Scalabuild.sbt、pom.xml、gradle.lockfile、Dockerfile
.NET.csproj、packages.lock.json、Dockerfile
共通ベースイメージ、環境定義、CIスクリプト、ツールバージョン

latestだけに依存すると、「昨日と同じ名前なのに中身が違う」という状態になり、CIの再現性が落ちます。内容ハッシュタグとlatestを併用し、ログから実際に使ったイメージを追えるようにするのが理想です。

失敗しやすいポイント

CIコンテナ化は効果が大きい一方で、つまずきどころも明確です。特に以下の問題は、移行時によく発生します。

失敗パターン原因対策
イメージpullに失敗するACRサービス接続の権限不足、endpoint名の不一致パイプラインからサービス接続を明示的に承認し、pull権限を確認する
Dockerビルドが遅いキャッシュ境界が悪く、毎回重いレイヤーが無効化される変更頻度の低い依存関係を上位レイヤーに分ける
コンテナ内でCLIが見つからないホストVMに入っていたツールに依存していたDockerfileに必要CLIを明記する
テストが急に落ちるタイムアウト、タイムゾーン、権限、ファイルパス差分失敗ログを環境差分とテスト失敗に分けて読む
ディスク容量不足になるイメージ、キャッシュ、成果物がホスト上に蓄積不要ツール削除、Docker prune、成果物サイズ削減を入れる
セキュリティレビューで止まるイメージに多数の依存関係が入り、脆弱性対応範囲が広がるSBOM、脆弱性スキャン、更新手順をCI設計に含める
forkで動かないMicrosoft側のACRやサービス接続に依存している自組織のACRへpushし、接続名と権限を置き換える

特にfork運用では、mmlsparkmcr.azurecr.ioやSynapseML MCRのようなMicrosoft側のレジストリ・サービス接続をそのまま使えない可能性があります。取り込む場合は、自組織のACR、Docker Hub、GitHub Container Registryなどに置き換える前提で設計してください。

パフォーマンス改善をどう評価するか

PR上の測定では、壁時計時間が約66分から約27分へ短縮され、総計算時間も約1228 agent-minutesから約817 agent-minutesへ減っています。これは大きな改善ですが、自社CIで同じ削減率が出るとは限りません。(GitHub)

評価するときは、単に「CIが速くなったか」ではなく、次の4つを分けて見ます。

指標見る理由
Wall-clock time開発者がPR結果を待つ時間に直結する
Agent-minutesCIコストやエージェント消費量に直結する
Flaky failure rate高速化しても不安定ならレビュー効率が落ちる
Cache hit rate事前ビルドイメージ戦略が機能しているか分かる

たとえば、Wall-clock timeが短くなってもAgent-minutesが増えている場合、並列化で待ち時間は減ったものの総コストは増えた可能性があります。逆にAgent-minutesが減っていても、キャッシュミス時だけ極端に遅いなら、依存関係更新時の運用を見直す必要があります。

今すぐ確認すべきチェックリスト

SynapseMLのforkや同様のAzure Pipelines構成を運用している場合は、以下を確認してください。

優先度確認内容
高pipeline.yamlにresources.containersとcontainer: ciが入る影響を確認する
高ACRサービス接続名、権限、Workload Identity Federation設定を確認する
高CI用Dockerfileのベースイメージ、JDK、Conda、SBT、Sparkのバージョンを確認する
高forkではMicrosoft側ACRを使えるか、自組織レジストリへ置き換えるか判断する
中10分per-testタイムアウトで落ちるテストがないか確認する
中テストデータや依存関係キャッシュが正しく使われるか確認する
中Microsoft-hosted agentのディスク容量不足対策を入れる
中CIイメージの脆弱性スキャンと更新手順を決める
低.dockerignoreや.gitignoreの変更が自社運用に合うか確認する

まとめ: API変更ではなく、CI基盤の再現性と速度を上げる更新

今回のMicrosoft developer platform documentation update: ci: containerize CI pipeline with pre-built Docker imageは、SynapseMLの利用者向けAPI変更ではなく、CI/CD基盤の改善です。対応の中心は、Azure Pipelines、Docker、ACR、サービス接続、テスト運用にあります。

実務で最初にやるべきことは、pipeline.yamlとCI実行ログの確認です。特に、ACRへの接続、container: ciの適用範囲、FabricE2Eの実行場所、10分タイムアウト、ディスク容量、キャッシュヒット率を見てください。PR説明と実ファイルに差分が見える箇所もあるため、最終判断は最新のYAMLと実行結果で行うのが安全です。

自社プロジェクトへ応用する場合は、いきなり全ジョブをコンテナ化するのではなく、StyleチェックやUnitTestから始め、依存関係の事前ビルド、内容ハッシュによるイメージ再利用、レジストリ認証、ディスク整理を順番に固めていきましょう。CIの高速化は、単なる待ち時間短縮ではなく、レビュー速度、開発者体験、計算コスト、テストの信頼性をまとめて改善する施策です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次