DataflowをPythonで使っているとbucket使用量が肥大化してお金が溶ける


要約

  • PythonでDataflowを実行すると、実行完了後に GCSの temp_location のファイルが消えない不具合がある
    • Python実装のみの不具合(Javaで実行したときは勝手に消えるらしい)
  • 毎日数十GBのBigQueryデータ投入タスクを行なっていると、すごい勢いでGCSの使用量が増え、多額の請求が発生する
  • 今のところ、GCS bucket lifecycleでデータを自動削除するくらいしか回避方法がない

発生した問題

GCPのDataflow(Python)を使って、毎日数十GBのデータをBigQueryに投入するジョブを走らせていたところ、GCSの請求金額が一日あたり$7/日ずつ増えている事に気が付きました。

GCS bucketを調査すると、Dataflowで使っているbucketの使用量が爆速で増えてました。

  • 220GB/日のペース

原因

原因は、Dataflow実行時に --temp_location で指定するフォルダ内に、生成された一時ファイルが永遠に残り続けていたためでした。

$ gsutil du -s -h gs://******-bucket/dataflow/tmp/** 
40.24 TiB    gs://******-bucket/dataflow/tmp/**

オプション名から、 temp_location フォルダに置かれる一時ファイルは、Dataflowジョブが終わった段階で勝手に消えるものと期待していましたが、実際には消えずに残っていました。

  • ドキュメントでは「ジョブが失敗すると、Dataflow は一時ファイルやステージング ファイルを自動的にクリーンアップしないことがあります。」などと書かれている箇所があり、ジョブ成功時には勝手に消えそうな感じがする。

temp_location フォルダのファイルが消えない件についてサポートに問い合わせたところ、「 Javaでは消えるがPythonではなぜか消えない。バグっぽい 」との回答を得たので、しばらくの間は temp_location のファイルを手動で消して、bucket使用量が肥大化しないようにしないといけなそうです。

bucketの肥大化を防ぐには、GCSのオブジェクトライフサイクルを使うと良いです。

Dataflowを使用しているチームの方は、一度 temp_location が肥大化していないか確認していただけると良いかと思います。

備考

Q: どうして数十GBのBigQueryへのデータ投入で220GB/日も一時ファイルが作られていたんですか?
A: Apache Beamの WriteToBigqueryの仕様のためです。
WriteToBigqueryは、BigQueryに書き込むデータを一旦無圧縮JSONとしてファイルに書き出すため、もとのCSVデータの10倍くらいのファイルを一時ファイルとして吐くようでした。

例:

元のデータが

c1,c2,c3,c4
1,"a","あ",true
2,"b","い",false

のようなテーブル(BeamのPCollection)のとき、WriteToBigqueryは

{"c1": 1, "c2": "a", "c3": "あ", "c4": true}
{"c1": 2, "c2": "b", "c3": "い", "c4": false}

のようなファイルを一時ファイルとして吐いており、カラム名が毎行付与されていく&無圧縮なのでファイルサイズがめっちゃでかくなってしまう。

  • しかも、扱っているテーブルはカラム数が100個くらいあるテーブルが多く、値はカラ(null)であることが多いため、その分Beam→jsonでデータサイズが増えていました。
  • 中間ファイルの形式は無圧縮JjsonかAvroかを選べるらしいのですが、どうせ一時ファイルだし何でもいいやと無指定でジョブを実行していたら、無圧縮jsonが永続的に保存され続ける状態になってしまっていました。