Skip to content

確認問題|Professional ⑥ デバッグ・デプロイ(配点 10%)

教材 06-debugging-deploy.md の内容に基づく確認問題です(全10問)。 まず問題を解き、下の「解答・解説」で答え合わせをしてください。

問題

Q1. 遅いジョブを Spark UI で診断する。公式ガイドが示す切り分けの手順として正しいものはどれか。

  • A. Executors タブでメモリ使用量を確認 → SQL DAG を開く → タスク数を数える → ログを取得する
  • B. ①ジョブタイムライン(イベントタイムライン)で主要な問題を特定 → ②最長のステージを見る → ③スキューまたはスピルを探す(ステージ詳細ページ。まず確認すべきはスピル)→ ④最長ステージが入出力(I/O)依存かを判断 → ⑤低速ステージの他の原因を探す
  • C. ①最長ステージを開く → ②ジョブタイムラインに戻る → ③Executors タブでログを取得 → ④スキューを探す
  • D. Spark UI ではなく必ずシステムテーブルから分析を始め、Spark UI は最後の手段とする

Q2. ステージ詳細ページの Summary Metrics でスキューを判定する基準として、正しいものはどれか。

  • A. タスク実行時間の 最大(Max)が 75 パーセンタイルより 50% 以上長い場合、スキューの可能性が高い。健全なステージでは 75 パーセンタイルと Max がほぼ同じになる
  • B. 中央値(median)が最小値の 2 倍以上のとき
  • C. Spill (Memory) が Spill (Disk) を上回っているとき
  • D. タスク数がクラスターのコア数より多いとき

Q3. ステージの I/O(Input / Output / Shuffle Read / Shuffle Write)が小さいのに遅い。SQL DAG を開いて原因を特定するとき、教材が挙げる主な原因と対処として正しいものはどれか。

  • A. I/O が小さいステージは必ずドライバーのボトルネックなので、ドライバーを大きくすれば解決する
  • B. 小さいファイルの読み書きは問題にならないため、まずクラスターを水平スケールする
  • C. UDF が遅い場合は、UDF を実行するステージのタスク数がコア数より多くなるよう coalesce() する
  • D. ①多数の小さなファイルの読み書き(スキャン演算子で読み取りファイル数を確認。数万ファイルなら小さなファイル問題で、ファイルは 8 MB を下回らないようにする。→ OPTIMIZE・予測最適化・最適化された書き込み)、②低速 UDF(DAG に UDF ノードが出る。ネイティブ関数への書き換えが最善。不可なら UDF を実行するステージのタスク数がコア数より少ない場合に事前に repartition(num_cores) する)、③デカルト結合(結合条件のない総当たり)、④分解結合/explode() の爆発(入る行数より出る行数が明らかに多い)

Q4. ジョブが失敗し、Executor が削除されていた。Executor が削除される主な理由と調べ方として正しいものはどれか。

  • A. Executor の削除は常にエラーであり、自動スケールでは起こらない
  • B. 失敗した Executor の調査は Spark UI の Jobs タブから行い、コンピューティングのイベントログは参照しない
  • C. 主な理由は①自動スケール(Autoscaling)=エラーではなく想定内、②スポットインスタンス損失=クラウドプロバイダーが VM を回収、③Executor のメモリ不足(OOM)。調べ方は、まずコンピューティングの [イベントログ] でサイズ変更中やスポット損失などの説明があるかを確認し、情報がなければ Spark UI の [Executors] タブから該当 Executor のログを取得する
  • D. OOM が疑われる場合は、パーティション数を減らして 1 タスクあたりの処理量を増やすのが定石である

Q5. 構造化ストリーミングを本番運用する際の構成として、教材が推奨するものはどれか。

  • A. 汎用(All-Purpose)コンピュートで実行し、自動スケールを有効にして負荷変動に対応する
  • B. ジョブコンピュート(Jobs Compute) で実行し(汎用は使わない)、ジョブのスケジュールに Continuous(継続的)モードを使い、ストリーミング用コンピュートでは自動スケールを有効にしない(スケールダウンに制限があるため。拡張自動スケーリングが必要なら Lakeflow パイプラインを推奨)。displaycount など結果を返す不要コードはノートブックから削除する
  • C. サーバーレスコンピュートでは Trigger.ProcessingTime を使うのが推奨である
  • D. Continuous モードは構造化ストリーミングのトリガー間隔の設定であり、ジョブのスケジュール方式ではない

Q6. foreachBatch を使ったストリーミング書き込みの処理保証について、正しい説明はどれか。

  • A. foreachBatch などの操作は少なくとも 1 回(at-least-once)保証であり exactly-once ではないため、処理パイプラインを冪等(idempotent)に作る必要がある。exactly-once の基盤はチェックポイントで、Delta へは txnAppIdtxnVersionbatchId にバインド)による冪等書き込みで exactly-once 相当を実現できる
  • B. foreachBatch は exactly-once を保証するため、シンク側での冪等性は不要である
  • C. foreachBatch は連続処理モードでも動作するため、foreach を使う必要はない
  • D. チェックポイントを削除して再起動しても、同じ txnAppId を使い続けるのが正しい運用である

Q7. streamingQuery.awaitTermination() の使いどころについて、正しい説明はどれか。

  • A. Lakeflow ジョブでストリームを動かすときは必ず awaitTermination() を呼ぶ必要がある
  • B. awaitTermination() はスレッドをブロックしないため、どこで呼んでも副作用はない
  • C. awaitTermination() を呼ばないと、ジョブサービスがバックログメトリックを取得できなくなる
  • D. Lakeflow ジョブでは使わない(ジョブサービスが実行完了を自動的に防ぐため不要で、使うとセル完了をブロックしてジョブサービスの追跡=バックログメトリック・ジョブ通知を妨げる)。使うべき場面は、汎用コンピュート上の対話型ノートブック(セル実行を維持し状態・エラーを観察)、ローカル/開発環境(メインスレッド終了でプロセスが落ちるのを防ぐ)、ジョブ以外のコンテキストでドライバーへエラーを伝播させたいとき

Q8. 宣言型オートメーション バンドル(旧称 Databricks Asset Bundles)の構成と一意性について、正しい説明はどれか。

  • A. databricks.yml は 1 バンドルに複数配置でき、ターゲットごとに用意する必要がある
  • B. targetsdefault: true は複数のターゲットに設定できる
  • C. databricks.ymlルートに 1 つだけ置く必須のメイン構成ファイルで、追加の構成ファイルは include で参照する。トップレベルマッピングは bundle(必須、name 必須)/ run_as / include / scripts / sync / artifacts / variables / workspace / permissions / resources / targets など。default: true にできるターゲットは 1 つだけ。バンドルは「名前 × ターゲット × デプロイ元 ID」で一意に識別され、これらが同一だと別バンドルのデプロイが互いに干渉する
  • D. バンドルの一意性はバンドル名のみで決まるため、ターゲットが違えば同名バンドルでも干渉しない

Q9. バンドルのデプロイモード(mode: developmentmode: production)の違いとして、正しい説明はどれか。

  • A. development モードでは同時実行が無効化され、production モードでは有効化される
  • B. development モードは、ファイル/ノートブック以外のリソース名に [dev ${workspace.current_user.short_name}] プレフィックスを付けて dev タグを付与し、関連パイプラインを development: true にし、デプロイ済みリソースのスケジュール/トリガーを一時停止し、全ジョブで同時実行を有効化し、デプロイロックを無効化する。production モードは、パイプラインが development: false であることを検証し、ターゲット指定の Git ブランチ(git.branch)と現在のブランチが一致するかを検証し(--force で上書き可)、サービスプリンシパルを run_as に設定するのが推奨で、development と異なり --compute-id による既存クラスター上書きはできない
  • C. production モードでは --cluster-id による既存クラスター上書きが推奨されている
  • D. presetsmode より優先度が低く、個別リソース設定は presets に上書きされる

Q10. Databricks の CI/CD について、教材の推奨として正しいものはどれか。

  • A. Git フォルダー(旧 Repos)はジョブ構成やパイプライン構成もソース管理するため、複数環境へのクロスワークスペース展開に最適である
  • B. CI/CD の認証には個人用アクセストークン(PAT)を使うのが最も安全である
  • C. dev / staging / prod は同一ワークスペース内でカタログを分けて管理し、クラスターサイズやシークレットは構成ファイルにハードコードするのが望ましい
  • D. 宣言型オートメーション バンドルが推奨アプローチ。認証は ワークロード ID フェデレーション(OAuth トークンフェデレーション) が最も安全(Databricks シークレット不要)で、次点がサービスプリンシパル(本番モードでは run_as に設定)。dev / staging / prod は別ワークスペースに分離し、環境固有値(クラスターサイズ・シークレット)はハードコードせずパラメーター化する。Git フォルダーや「ジョブを含む Git」はコードファイルのみをソース管理しジョブ/パイプライン構成は含まれないため、複数環境展開にはバンドルが適切

解答・解説

Q1. 正解: B 公式の Spark UI ガイドはステップバイステップ形式で、①ジョブタイムライン(イベントタイムライン)で主要な問題を特定(失敗ジョブ/Executor は赤表示、1 分以上の実行ギャップ、1 つの長いジョブ、多数の小さなジョブ、どれにも当てはまらなければ Duration でソート)→ ②最長のステージを見る(Input / Output / Shuffle Read / Shuffle Write とタスク数を確認。タスクが 1 つだけ = 問題のサイン)→ ③スキューまたはスピルを探す(最初に確認すべきはスピル。統計が表示されなければそのステージにスピルはない)→ ④最長ステージが I/O 依存かを判断 → ⑤低速ステージの他の原因を探す、の順に進む。

Q2. 正解: A ステージ詳細ページの Summary Metrics でタスク所要時間の最小 / 25% / 中央値 / 75% / 最大を確認し、Max が 75 パーセンタイルより 50% 以上長い場合はスキューの可能性が高いと判断する。健全なステージでは 75 パーセンタイルと Max がほぼ同じになる。スキューは 1 つまたは少数のタスクだけが他より極端に長くかかる状態で、クラスター使用率が下がり全体が遅くなる。C のスピル指標や D のタスク数はスキューの判定基準ではない。

Q3. 正解: D I/O が小さいのに遅いステージは SQL DAG(ジョブページ上部の「関連付けられた SQL クエリ」)を開いて原因を特定する。各ノードの時間は全タスクの累積時間(クロック時間ではない)だが、コストと相関するので有用。主な原因は①多数の小さなファイルの読み書き(8 MB 未満を避ける。原因は列が多すぎる/高カーディナリティ列でのパーティション分割 → OPTIMIZE、予測最適化、最適化された書き込み、ファイルレイアウト見直し)、②低速 UDF(コメントアウトして影響を確認 → ネイティブ関数への書き換えが最善。不可なら UDF を実行するステージのタスク数がコア数より少ない場合に repartition(num_cores)。UDF は各タスクがパーティション全体をメモリに載せるためメモリ問題も起こしうるので再パーティションでタスクを小さくする)、③デカルト結合、④分解結合/explode() の爆発。C は coalesce() でタスク数を減らす方向なので誤り(正しくは repartition で増やす)。

Q4. 正解: C Executor が削除される主な理由は①自動スケール(エラーではなく想定内)、②スポットインスタンス損失(クラウドプロバイダーが VM を回収)、③Executor の OOM(A が誤り)。調べ方は、まずコンピューティングの [イベントログ] を確認してクラスターのサイズ変更中/スポット損失などの説明があるかを見る。イベントログに情報がなければ Spark UI の [Executors] タブから失敗した Executor のログを取得する(B が誤り)。OOM への一般的な発想は「パーティションを細かくしてタスクあたりのメモリ量を減らす」「より大きい/メモリの多いコンピュートを選ぶ」「スピルやスキューを解消してメモリ圧を下げる」「collect などドライバーに大量データを集約する処理を避ける」であり、D は方向が逆。

Q5. 正解: B Databricks は本番の構造化ストリーミングをスケジュールされた Lakeflow ジョブとして実行することを推奨し、常に①display / count など結果を返す不要コードをノートブックから削除、②汎用コンピュートを使わず必ずジョブコンピュートで実行(失敗・再試行時に新しい計算リソースがデプロイされる)、③ジョブのスケジュールに Continuous(継続的)モードを使う(※これはジョブのスケジューリング機能であり構造化ストリーミングのトリガー間隔ではない。D が誤り)、④ストリーミング用コンピュートで自動スケールを有効にしない(スケールダウンに制限があるため。拡張自動スケーリングが必要なら Lakeflow パイプラインを推奨)を構成する。A は①②④すべてに反する。サーバーレスでは Trigger.AvailableNow()Trigger.Once() のみサポートで AvailableNow() が推奨なので C も誤り。

Q6. 正解: AforeachBatch などの操作は少なくとも 1 回(at-least-once)保証であり exactly-once ではないため、処理パイプラインを冪等に作る必要がある(B が誤り)。exactly-once の基盤はチェックポイント(オフセット+コミット)で、Delta へは txnAppId(一意のアプリケーション ID)+ txnVersionbatchId にバインドする単調増加の数値)による冪等書き込みで exactly-once 相当を実現できる。foreachBatch連続処理モードでは動かない(マイクロバッチ実行に依存するため)ので、連続モードで書くなら foreach() を使う(C が誤り)。またチェックポイントを削除して新チェックポイントで再起動する場合は、新チェックポイントが batchId 0 から始まり Delta が (batchId, txnAppId) を一意キーとして既知バッチをスキップしてしまうため、別の txnAppId を指定する必要がある(D が誤り)。

Q7. 正解: DstreamingQuery.awaitTermination() / spark.streams.awaitAnyTermination() は現在のスレッドをブロックする(B が誤り)。Lakeflow ジョブでは使わない:ジョブサービスが実行完了を自動的に防ぐため不要で、使うとセル完了をブロックしてジョブサービスの追跡(バックログメトリック・ジョブ通知)を妨げる(A・C が誤り。C は因果が逆で、呼ぶことが妨げになる)。使うべき場面は、①汎用コンピュート上の対話型ノートブック(セル実行を維持し状態・エラーを観察)、②ローカル/開発環境(メインスレッド終了でプロセスが落ちるのを防ぐ)、③ジョブ以外のコンテキストでドライバーへエラーを伝播させたいとき(クエリ例外を再送出させる)。

Q8. 正解: C バンドルはルートに databricks.yml を 1 つだけ置き(A が誤り)、追加の YAML は include で参照する。トップレベルマッピングは bundle(必須、name 必須。databricks_cli_version / cluster_id / deployment / git も持てる)/ run_as / include / scripts / sync / artifacts / variables / workspace / permissions / resources / targetsdefault: true にできるターゲットは 1 つだけ(B が誤り)。バンドルは「名前 × ターゲット × デプロイ元 ID」で一意に識別され、これらが同一だと別バンドルのデプロイが互いに干渉する(D が誤り)。BUNDLE_ROOT 環境変数でルート外からもコマンド実行できる。

Q9. 正解: B development モードの主な動作は、ファイル/ノートブック以外のリソース名への [dev ${workspace.current_user.short_name}] プレフィックスと dev タグ付与、関連 Lakeflow パイプラインの development: true 化、bundle deploy --cluster-id <id> による既存クラスター上書き、デプロイ済みリソースのスケジュール/トリガーの一時停止(個別に schedule.pause_status: UNPAUSED で解除可)、全ジョブでの同時実行の有効化(個別に max_concurrent_runs: 1 で無効化可)、デプロイロックの無効化(反復高速化のため)。A は同時実行の有効/無効が逆。production モードは development: false の検証、Git ブランチ一致の検証(任意、--force で上書き可)、本番デプロイにはサービスプリンシパルを推奨(強制するには run_as に設定)で、development と異なり --compute-id / compute_id による既存クラスター上書きはできない(C が誤り)。presets の優先順位は「個別リソース > presets > mode」なので D も誤り。

Q10. 正解: D Databricks の CI/CD で使えるツールのうち宣言型オートメーション バンドルが推奨。認証は ワークロード ID フェデレーション(OAuth トークンフェデレーション) が最も安全(Databricks シークレット不要)で、CI/CD 用サービスプリンシパルも使う(本番モードのバンドルデプロイでは run_as にサービスプリンシパルを設定するのが推奨。B の PAT は最も安全とは言えない)。基本原則は「すべてをバージョン管理」「テストの自動化(pytest / databricks bundle validate)」「IaC の採用(環境固有値はハードコードせずパラメーター化)」「環境の分離=dev / staging / prod で別ワークスペース」「監視と自動ロールバック」「資産管理の統合」(C が誤り)。バンドルを使わない軽量オプション(Git フォルダー、ジョブを含む Git)はソース管理されるのがコードファイルのみで、ジョブ/パイプライン構成やダッシュボード発行構成は含まれないため、複数環境のクロスワークスペース展開には不向き(A が誤り)。