top of page

【AWSデータ移行】あえて「手作業×スクリプト」のハイブリッドで挑む!構造の異なる稼働中システムへ安全に移行した2ヶ月の全記録

  • 4 日前
  • 読了時間: 11分

この記事でわかること


  • AWSクロスアカウント・別リージョン環境における、異種スキーマ間のデータ移行戦略

  • 稼働中システムのID衝突を防ぎ、準備2ヶ月・本番2日を完遂したリスク管理

  • 完全自動化ツールを見送り、「Excelマッピング×動的スクリプト」によるデータ再構築術 




   目次




   はじめに


私は現在、AWS上で稼働する、研究者向け材料計算プラットフォームを開発・運用するプロジェクトに参画しています。


旧システムから新システムへのメジャーアップデートを無事にリリースし、チームがようやく一息ついていた頃のことです。 


ある日、マネージャーから「旧システムのデータ移行」を提案されました。


確かに、2つのシステムを並行稼働させる必要はありません。コストや運用保守の観点からも、システムを1つに統合したほうがメリットが大きいです。旧システムに残っている貴重な計算結果ファイルは、新システムへ早急に移行すべきでした。 


以前、プライベートな検証環境で模擬的な移行をした経験もありました。神経を使うタスクではあるけれど、エンジニアとしてやりがいを感じる重要なミッションだと感じた私は、何の疑いもなく元気に返答しました。 


「お任せください!」


この後、待ち受けている絶望的な壁の存在も知らずに・・・



   データ移行の要件と障壁


移行の対象となるリソースは以下の2つです。


・Amazon Aurora(データベース) 

 →グループ情報、ユーザー情報、計算ジョブのメタデータ など


・Amazon EFS(ストレージ) 

 →計算結果ファイル



ここまでは何も問題ありません。Webシステムではごく一般的な構成です。


しかし、真の恐怖は対象リソースそのものではなく、これらを「どのような条件下で」移行しなければならないかという前提条件にありました。


1.移行先はすでに「本番稼働中」 

新システムはすでにリリースされており、新しいユーザーが日々計算ジョブを回しています。つまり、システムを止めて「まっさらな状態からDBを丸ごと上書き(リストア)」することは許されません。既存のデータと、移行するデータを同居させる必要があります。 


2.インフラが「別アカウント・別リージョン」

移行元と移行先でAWSアカウントもリージョンも異なるため、ネットワーク(VPC)が分断されています。ローカルや同一VPC内で気軽にデータを流し込める環境ではありませんでした。 


3.全件ではなく「一部ユーザーのみ」を抽出移行

 旧システムにいる全ユーザーを一括で移すのではなく、「特定のグループのデータだけ」のように、条件に合致するデータだけをピンポイントで抜き出す必要がありました 。


4.データ構造が全く違う(フラット構造から親子構造へ)

旧システムでは独立に保存されていた計算ジョブのデータが、新システムでは「プロジェクト > サブグループ > 計算ジョブ」のように、厳密な親子関係(階層構造)を持つように構造が根本からリファクタリングされていました。 


この段階でただのデータ移行ではなく、データの『再構築』をしながら、稼働中のDBに外科手術をするということに気が付きました。



   移行手法を考える


AWSにおけるデータベース移行といえば、いくつかの「王道」が存在します。例えば、Auroraのスナップショットからの復元、データダンプ(mysqldump 等)による一括インポート、あるいは高機能なデータ移行サービスである AWS DMS (Database Migration Service) の活用などです。  


しかし、今回の移行プロジェクトにおいて、これらのツールをそのまま適用することは、稼働中のシステムに深刻な影響を与えるリスクがありました。理由は大きく2つあります。 



課題①:ID衝突(Primary Key Collision)のリスク


前述の通り、移行先の新システムにはすでに稼働中のユーザーデータが存在します。このユーザーデータは旧システムと完全に一致せず、レコードの内容に差異がありました。


ここに旧システムのデータをそのままダンプして流し込むとどうなるでしょうか。旧システムで採番された「ユーザーID: 100」のデータが、新システムですでに活動している別の「ユーザーID: 100」のデータと衝突(Duplicate Key Error)を起こすか、最悪の場合は既存データを意図せず上書きしてしまいます。


本番環境のデータを破壊するリスクが少しでもある以上、ツールを用いた機械的な「全件コピー」という選択肢は早々に除外されました。



課題②:移行ツールは「ビジネスの文脈」を自動判定できない 


仮に、何らかの変換処理を挟んでIDの衝突問題をクリアできたとしても、最大の壁である「フラット構造から親子構造への変換」は自動化が極めて困難でした。


新システムでは、計算ジョブのデータを登録する前提として、親となる「プロジェクト」や「サブグループ」のデータがDB上に存在している必要があります。しかし、旧システムのデータにはそもそもその概念が存在しません。


「この計算ジョブは、新システムのどのプロジェクトに紐づけるのが正解か?」 


「親となるグループデータは、どのような名前で新しく生成すべきか?」


高度な移行ツールは、データ型を変換することは得意ですが、システムの歴史的経緯やビジネス要件の文脈までは汲み取ってくれません。文脈を持たないデータを無理やり流し込めば、アプリケーション側で「親が存在しない孤児データ」として致命的なエラーを引き起こすことになります。



結論:「ツールによる完全自動化」を見送る決断


システム移行においては、「いかにツールを活用して自動化・省力化するか」がベストプラクティスとされがちです。しかし、スキーマが根本から変わり、新たな親子関係が追加されるような複雑な要件において、ツールへの過信はシステム障害に直結します。


私はここで、あえて高度な自動化ツールを見送る決断をしました。


そこで採用したのが、表計算ソフト(Excel)を用いた「手動での新旧IDマッピング」という、非常に泥臭い手法だったのです。


一見効率的ではないと感じるかもしれませんが、全ての手順で後述するシェルスクリプトを組んでいるため、移行対象となるグループに対応した新旧IDマッピング表を更新するだけでデータ移行が実現できます。



\AWSの設定でお困りですか?専門エンジニアがサポートします/



   実践:移行作業本番


移行作業に丸2日を要し、その間、旧システムと新システムを停止して以下の作業を実施しました。


STEP 1:旧システムでのバックアップ取得と共有


稼働中の旧システムから、データベース(Aurora)の最新スナップショットと、計算結果ファイル(EFS)のバックアップを取得します。これらを、移行先である新システムのAWSアカウントから復元できるようにクロスアカウントでアクセスポリシーを付与し、安全に共有します。


STEP 2:新システム内での「一時作業場」の構築


共有されたバックアップを、新システムの本番VPC内に「作業用の一時DB 」として復元します。同時に、EFSのデータも踏み台サーバー(EC2)上の専用ディレクトリにマウントし、本番環境から完全に隔離されたデータ加工用の環境を構築します。 



STEP 3:旧データの抽出と「新旧IDマッピング表」の作成


一時DBから移行対象となるユーザーやジョブの情報を抽出し、エクセル上で「旧システムのID」と「新システムで新しく割り当てるID」を紐付けるマッピング表を作成します。 


▼実際に使用したマッピング表



STEP 4:受け皿の作成と、本番DBへの一括流し込み


新システムの要件(親子構造)を満たすため、本番DBに過去データを格納するための「ダミーの親プロジェクト」をひとつ作成します。その後、STEP 3で作ったマッピング表を元に、一時DBのデータを変換しながら本番DBへ安全に INSERT していくスクリプトを実行します。 


実際に使用したコマンド例:(※一部名称をマスキングしています) 

# ▼ 初期設定(環境変数の読み込みとDB接続準備)


# STEP3で作成したマッピング表(エクセルの別シート)から、対象グループのDBエンドポイントや新旧グループIDを動的に取得。 


# グループ専用の一時テーブル名を変数化し、他グループのデータとの混入を防止。 


#D3セルには参照先のシート名が記載されている


="H1="""&INDIRECT("'"&$D$3&"'!T8")&""";H2="""&INDIRECT("'"&$D$3&"'!BL8")&""";D1="""&INDIRECT("'"&$D$3&"'!T9")&""";Gv5="""&INDIRECT("'"&$D$3&"'!T17")&""";"


&"G6="""&INDIRECT("'"&$D$3&"'!BL17")&""";A_EMAIL="""&TRIM(INDIRECT("'"&$D$3&"'!BL20"))&""";"


&"VT=""v5_temp"";PJT=""pj_list_""$Gv5;MATT=""mat_list_""$Gv5;JOBT=""job_list_""$Gv5;read -sp ""PW: "" P;echo;"


&"M2=""mysql -h$H2 -u<DB_USER> -p$P -D<DB_NAME> -N -s"";"



# ▼ [1] 紐付け調整(一時DBでのデータ整形と孤児データの救済)


# いきなり本番環境は更新せず、一時DB内で旧データと新プロジェクトを紐付け。


# 親が見つからない孤児データ(NULL)は、ダミーの親(Floating_Project等)に強制的に逃がしてエラーを防ぐ。


&"echo ""--- [1] 紐付け調整 ---""; "


&"$M2 -e ""UPDATE $VT.$MATT mt JOIN <DB_NAME>.projects pr ON pr.name LIKE CONCAT(mt.name, '%') "


&"SET mt.pj_uuid=pr.pj_id WHERE pr.group_id=$G6 AND mt.name!='Floating_Material';""; "


&"$M2 -e ""SET @FPID=(SELECT pj_id FROM <DB_NAME>.projects WHERE group_id=$G6 AND name='Floating_Project' LIMIT 1); "


&"UPDATE $VT.$MATT SET pj_uuid=@FPID WHERE pj_uuid IS NULL OR pj_uuid='';""; "


&"$M2 -e ""UPDATE $VT.$MATT m JOIN <DB_NAME>.projects p ON m.pj_uuid=p.pj_id JOIN <DB_NAME>.users u ON p.user_id=u.id "


&"SET m.email=u.email WHERE p.group_id=$G6 AND m.name!='Floating_Material';""; "


&"$M2 -e ""UPDATE $VT.$MATT SET email='$A_EMAIL' WHERE name='Floating_Material';""; "


&"$M2 -e ""SET @FMID=(SELECT mat_uuid FROM $VT.$MATT WHERE name='Floating_Material' LIMIT 1); "


&"UPDATE $VT.$JOBT SET mat_uuid=@FMID WHERE mat_uuid NOT IN (SELECT mat_uuid FROM $VT.$MATT) OR mat_uuid IS NULL;""; "



# ▼ [2] インポート(本番DBへの安全な流し込み)


# 一時DBで「新システム仕様」に整形できたデータを、本番DBへ投入。データクレンジング済みのため、ここでは重複スキップ目的でIGNOREを使用 


&"echo ""--- [2] インポート ---""; "


&"$M2 -e ""UPDATE $VT.$JOBT j JOIN $VT.$MATT m ON j.mat_uuid=m.mat_uuid SET j.email=m.email;""; "


&"$M2 -e ""INSERT IGNORE INTO <DB_NAME>.materials (user_id,name,material_id,group_id,created_at,updated_at) "


&"SELECT u.id,m.name,m.mat_uuid,$G6,NOW(),NOW() FROM $VT.$MATT m JOIN <DB_NAME>.users u ON m.email=u.email WHERE u.group_id=$G6;""; "


&"$M2 -e ""INSERT IGNORE INTO <DB_NAME>.jobs (user_id,name,job_id,dependent_job_id,software,group_id,"


&"created_at,updated_at,status,job_type,display_flag) "


&"SELECT u.id,j.name,j.job_uuid,j.mat_uuid,j.software,$G6,NOW(),NOW(),v5_status,j_type,1 "


&"FROM $VT.$JOBT j JOIN <DB_NAME>.users u ON j.email=u.email WHERE u.group_id=$G6;""; "



# ▼ [3] 最終紐付け(中間テーブルの生成)


# 本番DBに実データが入った後、新システムの要件である複雑な親子構造(多対多の関連など)を表現するため、中間テーブル(materials_projects等)のレコードを自動生成して構造を完成させる。


&"echo ""--- [3] 最終紐付け ---""; "


&"$M2 -e ""INSERT IGNORE INTO <DB_NAME>.materials_projects (material_id,project_id) "


&"SELECT m.id,p.id FROM <DB_NAME>.materials m JOIN $VT.$MATT mt ON m.material_id=mt.mat_uuid "


&"JOIN <DB_NAME>.projects p ON mt.pj_uuid=p.pj_id WHERE m.group_id=$G6 AND p.group_id=$G6;""; "


&"$M2 -e ""INSERT IGNORE INTO <DB_NAME>.jobs_projects (job_id,project_id) SELECT j.id,p.id FROM <DB_NAME>.jobs j "


&"JOIN $VT.$JOBT jt ON j.job_id=jt.job_uuid JOIN $VT.$MATT mt ON jt.mat_uuid=mt.mat_uuid "


&"JOIN <DB_NAME>.projects p ON mt.pj_uuid=p.pj_id WHERE j.group_id=$G6 AND p.group_id=$G6;""; "



# ▼ 最終結果の確認


# 意図通りに紐付けられたか、件数を集計して画面出力させる


&"echo ""=== 最終結果 === ""; mysql -h$H2 -u<DB_USER> -p$P -D<DB_NAME> -t -e ""SELECT p.name as project, "


&"m.name as material, u.email, COUNT(DISTINCT j.id) as jobs FROM projects p "


&"JOIN materials_projects mp ON p.id=mp.project_id JOIN materials m ON mp.material_id=m.id "


&"JOIN users u ON m.user_id=u.id LEFT JOIN jobs j ON m.material_id=j.dependent_job_id "


&"WHERE p.group_id=$G6 GROUP BY m.id, u.email;"""

このようにシェルスクリプトを組み、実際の値をマッピング表から参照することで、グループ毎に動的な値を引用することができました。上記に類似したシェルスクリプトを他の手順でも準備して実行しました。


STEP 5:EFSの一括リネームと本番合流 


踏み台サーバーに、旧EFSのバックアップと新システムの本番EFSを同時にマウントします。次に、マッピング表をもとに専用スクリプトを実行し、フォルダ名を新システムのUUIDへと変換しながらデータをコピー(cp -r)しました。


コピー後は、新旧のファイル数や容量を比較して欠損がないかを確実にチェック。最後に、ファイルの所有権(chown)とパーミッション(chmod)を新仕様に合わせて書き換え、本番環境へ安全に合流させています。


▼ターミナルの実行ログ



\AWSの設定でお困りですか?専門エンジニアがサポートします/



   まとめ


今回のデータ移行プロジェクトは、要件定義から検証、そして本番環境への完全移行まで、インフラエンジニア1名・約2ヶ月という期間で完遂しました。


稼働中のシステムに対する複雑な要件を短期間で乗り切れた最大の理由は、早々に「移行ツールによる完全自動化」を諦めたことにあります。


エンジニアであれば誰しも、最新のツールを駆使し、ボタン一つで全てが完結するような「スマートで高度なアーキテクチャ」を組み上げたいという誘惑に駆られるものです。もちろん、単純なデータ移行であればそれがベストプラクティスです。


しかし、今回のようにフラットな構造から親子構造へスキーマが変わり、動的なIDマッピングが必要になるケースでは、無理にツールだけで完結させようとすると、かえってアーキテクチャが複雑化し障害リスクが高まってしまいます。 


今回選択した「手作業とスクリプトのハイブリッド」は一見すると泥臭い手法ですが、結果として既存システムに影響を与えることなく、最も確実でスピーディに移行を終える最短ルートになりました。


この記事が複雑なデータ移行プロジェクトに直面しているエンジニアの皆様にとって、具体的な解決策のヒントになれば幸いです。


最後までお読みいただき、ありがとうございました。



この記事を書いた人

M.R(AWSエンジニア)

🏅保持資格

・AWS Certified Cloud Practitioner

・AWS Certified SysOps Administrator - Associate

・AWS Certified Solutions Architect - Associate

・基本情報技術者試験 

保険業界向け業務システムのアプリケーション開発を経て、現在は研究者向け材料計算プラットフォームのプロジェクトに参画。インフラ構築からアプリ開発・運用まで、領域を問わずフルスタックに担当しています。



\サービス紹介資料 ダウンロード/




bottom of page