🤮

【第2話】40台バッチサーバーで17億ファイルを移したらメモリリーク祭りになった話

に公開

前話で「Rails 2.3+MogileFS+物理20台」から
Azure AKS+ActiveStorageへ移行することを決めた。

今回はその17億ファイル・170TBの本体データ移行の記録。
結論から言うと、40台のバッチサーバーが毎日落ちまくった

この記事は以下のシリーズの一部です
シリーズ一覧を見る


移行後の構成図(目指した理想)

綺麗にまとまった……はずだった。

実際の移行方式(2020年当時の現実解)

項目 内容
バッチサーバー 社内IaaS 40台(Dockerインストール済み)
並列処理 ID範囲で40分割(1台あたり約4,250万レコード担当)
DB接続 ActiveRecordを2系統(旧MySQL+新MySQL)
進捗管理 ローカルYAMLに1000レコードごとに保存
監視 cron + freeコマンド(1分ごと)
最大の問題 ActiveStorage 5.2 + azure-storage-blob(プレビュー版)のメモリリーク

メモリリークの推移(実測例)

経過時間 メモリ使用量 結果
0分 2GB -
1時間 8GB -
3時間 15GB OOM Killer発動 → 落ちる

GC.start を入れても全く効果なし。
当時、正式版のazure-storage-blob gemは存在せず、プレビュー版しか使えなかった。

最終的に採用した「原始的生存戦略」

# 擬似コード
Signal.trap(:INT) { graceful_shutdown }

loop do
  process_1000_records
  save_progress_to_yaml   # ← タヒんでもここから再開

  if memory_usage > 70%
    `docker kill #{container_id}`   # 自分で自分を殺す
  end
end
# 各サーバーに仕込んだcron(1分ごと)
* * * * * /usr/local/bin/memory_watcher.sh

# memory_watcher.sh
USED=$(free | awk '/Mem:/ {printf("%.0f", $3/$2 * 100)}')
if [ "$USED" -gt 70 ]; then
  docker kill image_migration_container
fi

40台が毎日120〜150回自殺しながら、約1ヶ月で170TBを移行完了
→ ExpressRoute 3Gbpsで流し、Azure通信費約300万円

なぜK8s CronJobにしなかったか

問題点 結論
Azure AKS 大規模クラスター作成で月額数百万円追加 コスト無理
社内K8s 当時社内にK8sなし 不可能
PersistentVolume 進捗管理でPVが爆タヒ 運用地獄
40台IaaS+Docker 原始的だが確実、タヒんでも即再開可能 採用

2020年の制約下では、これが唯一の現実解だった。

次回、第3話:「本番リリースで3回連続失敗した詳細」


170TB・17億ファイルの画像移行で3回失敗した2020年の全記録
第2話 完

Discussion