TT Lab
はじめる
学ぶ 学習パス コース

CAPA — Argoプロジェクト認定アソシエイト

成功したパイプラインの結果ファイルはどこへ消えた?

TT Labで続きを見る

一言でいうと

Workflowの成功、中間ファイルの片付け、最終結果の保持は、それぞれ別の契約です。3つの契約をそれぞれ確認してはじめて、CIの緑の表示を信じて、次の作業に進めます。

なぜ必要なのか

夜間のデータ処理は成功しました。ところが、朝に結果を読むチームがファイルを見つけられません。担当者は、リトライ回数を増やし、コンテナのメモリを上げます。これは役に立つでしょうか。データを生成する処理が失敗したのではなく、成功後の片付けのポリシーが最終結果まで消したのなら、より早く成功して、より確実にファイルを消すだけです。失敗した層から区別してはじめて、復旧の方向が合います。

反対の事故もあります。結果はきちんと作られたのに、一時ファイルがたまり続けます。Workflowの一覧にはSucceededが表示されるため、アラートが鳴りません。片付けを担当するServiceAccountがKubernetesの権限を失った場合なら、業務コンテナの終了コードとGCのエラーは、同時に互いに異なることがあります。このモジュールでは、実際のS3互換ストレージと実際のコンテナを使って、2つの事故を別々に再現します。

どう動くのか

パスがそのまま実行の境界である

生成側は、Pod内の/work/temp.txtに書きます。executorは、そのファイルをオブジェクトストアのkeyとしてアップロードします。消費側は、ストアのkeyから再びダウンロードします。ローカルのファイルパスとS3のkeyは、同じ概念ではありません。別々の2つのPodが、ローカルで同じパスを使っても安全なことがありますが、同じバケットの同一keyにアップロードすると、あとのアップロードが、先に書かれた結果を上書きすることがあります。

generateNameは、Kubernetesのオブジェクト名の衝突を減らします。それだけでは、S3のkeyが変わるわけではありません。出力keyに{{workflow.uid}}を入れると、今回の実行の識別子が、保存パスまでつながります。ファイルの内容にも、生成側のUIDを入れ、消費側が自分のUIDと比較すれば、単にファイルが存在するかどうかよりも、強い検査ができます。他人の正常なファイルを読んだ事故も発見できるからです。

このラボの2つの正常な実行は、それぞれruns/<실행 UID>/temp.txtとruns/<실행 UID>/final.jsonを使います(プレースホルダーは実行のUIDです)。衝突の実験は、同じbatchのsharedパスを、2つの実行が共有します。ほかの実験試行のsharedファイルまで消さないように、batch自体は、実験ヘルパーが毎回区別します。同じ試行の中でだけ、意図した衝突が発生します。

いつ消して何を残すか

Workflowレベルのspec.artifactGC.strategyは、片付けのデフォルトのポリシーです。OnWorkflowCompletionは完了を基準に、OnWorkflowDeletionはWorkflowの削除を基準に動作します。個別の出力artifactのartifactGC.strategy: Neverは、そのファイルを片付けの対象から除外します。中間成果物は消しつつ、最終結果は残すポリシーを表現できます。

ここでNeverは、バックアップではありません。ストレージがなくなったり、運用者がオブジェクトを削除したりすれば、ファイルはなくなります。ラボのストレージは、VM内の破棄可能なemptyDirなので、セッション終了後は維持されません。本番では、永続ボリューム・復旧可能なバックアップ・アクセス制御・TLS・保持期間を、別途設計する必要があります。

Workflowの成功を読むことと、ファイルの保持を確認することも違います。このラボは、実際に読んだバイトのUIDとハッシュを比較し、削除を判断するときは、認証済みの一覧照会と404 NoSuchKeyをあわせて確認します。接続の失敗や403を見て、「ファイルが消えた」と結論づけてはいけません。

Kubernetesの権限とストレージの権限は別である

GCのPodは、まずWorkflowArtifactGCTaskを見て、処理結果をstatusに記録する必要があります。このとき必要なKubernetesの権限は、該当リソースのlist/watchと、statusサブリソースのpatchです。そのあとのS3オブジェクトの削除には、ストレージのアカウントの権限が必要です。Kubernetes Roleを直したのに、S3でAccessDeniedなら、別の層を見ていることになります。2つの権限体系を混ぜないでください。

kubectl auth can-i patch workflowartifactgctasks --subresource=statusのように、サブリソースを明示して確認します。workflowartifactgctasks/statusという引数だけを渡すと、別の意味に解釈されて、誤ったnoを見ることがあります。コマンドが出した結果だけでなく、何を質問したのかも、証拠に含める必要があります。

このラボでわざと権限のないGCは、ArtifactGCErrorとforbiddenのログを残します。業務の状態は、依然としてSucceededのことがあります。エラーとfinalizerを強制的に取り除いて、緑色にするのは、復旧ではありません。元の失敗の証拠を保全して、最小のRoleを結び付けたあと、新しいWorkflow UIDで、片付けと保持が正常かを確認します。すでに失敗した元のGCが、自動で再処理されるという仮定はしません。

現場での姿

モデル学習では、大容量の中間チェックポイントを消しつつ、最終モデルと評価レポートは保持する必要があります。データ変換では、同じ時間帯に重なった再実行2つが、互いの入力を消さないようにする必要があります。規制対象のレポートなら、「作業が成功した」と「結果を再度読んで検証できる」を、別々の指標で管理するほうがよいです。失敗した業務、失敗した片付け、誤った保持設定の対応担当者が、同じとは限らない点も重要です。

LabHubの環境検証でも、サービスをReadyと確認したのに、正常なS3アップロードが500だったことがありました。小さなラボのボリューム上限を、初期メタデータが先に占有したのが原因でした。匿名リクエストが403だという事実だけを検査していたら、この障害を見逃したはずです。許可すべきリクエストが成功する対照群と、拒否すべきリクエストが失敗する反例を、一緒に置く理由です。

次のラボですること

まずストレージのアクセス境界を確認します。共有キーをUIDごとのキーに直して、2つの実行を比較したあと、最終保持の欠落とGCの権限失敗を、別々に分析します。最小権限で新しい実行を検証し、共有キーの衝突と、削除時のGCを観察します。最後に、同じSucceededが、別々の保存結果を持ちうることを、実際のUIDを入れて説明します。ヘルパーの完了メッセージは、実験の実行が終わったという意味であり、ラボの採点の合格とは同じではありません。

公式ドキュメント