🤮
【第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