こんにちは、サーバーワークスの岡部です。
AWS re:Invent 2025 にて 「STG306: Build protection and cost-optimize using Amazon EBS snapshots」 というワークショップに参加してきました。
はじめに
このセッション、タイトルだけ見ると「スナップショットの取り方でしょ?」と思われがちですが、実は中身は 「本番運用で事故らないためのデータ保護設計」 と 「限界までコストを削るためのアーカイブ戦略」 という、非常に実践的な内容でした。
本記事では、ワークショップの作業ログではなく、そこから得られた 「明日からの設計に使える仕様と判断基準」 を共有します。
特に、「DLM と AWS Backup の使い分け」や「Linux 上での静止点確保の挙動」については、ドキュメントだけでは見落としがちなポイントも解説します。
- はじめに
- 1. そのスナップショット、本当に復元できますか?(静止点の壁)
- 2. コスト削減の切り札「EBS Snapshots Archive」
- 3. 【設計判断】DLM vs AWS Backup どっちを使う?
- まとめ
- 補足
1. そのスナップショット、本当に復元できますか?(静止点の壁)
EBS スナップショットを取得する際、最も意識すべきは 「データの整合性(Consistency)」 です。
バックアップは "取得" の設定に目がいきがちですが、"復元" はそれ以上に重要な項目です。取得後は必ず復元(復旧)テストを行って、サービスが復旧できるかまで確認する必要があります。
まず、今回のワークショップでは、復元について以下の 2 つの違いが明確に定義されており、この違いを意識することが重要です。
クラッシュコンシステント (Crash-Consistent)
- 状態: 電源を突然引き抜いた時と同じ状態。ディスク上のデータは保存されているが、メモリ上の(書き込み途中の)データは含まれない。
- 用途: 通常の OS 領域や、クラッシュリカバリ機能を持つファイルシステム向け。
- 課題: データベース(MySQLなど)の場合、トランザクションログの不整合で最悪起動しない可能性がある。
アプリケーションコンシステント (Application-Consistent)
- 状態: アプリケーションの I/O を一時停止(Quiesce)し、メモリ内のデータをディスクにフラッシュした状態で取得したもの。
- 用途: データベースやミッションクリティカルなアプリケーション。
- 実装: AWS Systems Manager (SSM) Run Command を Data Lifecycle Manager (DLM) と連携させる必要があります。
【Deep Dive】静止点確保の「正解」
今回のワークショップでは静止点を確保するために、DLM 内にて SSM ドキュメントを用いて Pre 処理、Post 処理を行いました。
SSM ドキュメントも紹介します。
SSM ドキュメント(クリックで展開)
{ "schemaVersion": "2.2", "description": "DLM pre/post Hook with Auto-Thaw Capability", "parameters": { "command": { "type": "String", "description": "(Required) Specifies whether pre-hook or post-hook should be executed.", "allowedValues": [ "pre-script", "post-script", "dry-run" ], "default": "dry-run" }, "executionId": { "type": "String", "description": "(Optional) Specifies the unique identifier associated with a pre and/or post script execution", "default": "None" } }, "mainSteps": [ { "action": "aws:runShellScript", "name": "runShellScript", "precondition": { "StringEquals": [ "platformType", "Linux" ] }, "inputs": { "runCommand": [ "#!/bin/bash", "#Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.", "#Permission is hereby granted, free of charge, to any person obtaining a copy of this", "#software and associated documentation files (the \"Software\"), to deal in the Software", "#without restriction, including without limitation the rights to use, copy, modify,", "#merge, publish, distribute, sublicense, and/or sell copies of the Software, and to", "#permit persons to whom the Software is furnished to do so.", "#THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", "#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", "#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT", "#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", "#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE", "#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "", "### Error Codes", "###", "# 1 Pre-script failed during execution 201", "# 2 Post-script failed during execution 202", "# 3 Auto thaw occurred before Post-stage trigger 203", "# 4 Pre-Script triggered when instance was frozen 204", "# 5 Post-Script triggered when instance was not frozen 205", "# 6 Application Not ready for Pre/Post execution 206", "", "FS_ALREADY_FROZEN_ERROR=\"freeze failed: Device or resource busy\"", "FS_ALREADY_THAWED_ERROR=\"unfreeze failed: Invalid argument\"", "", "# Pre-Script operation", "execute_pre_script() {", " # Commands to quiesce the DB and freeze", " echo \"INFO: Execute MYSQL Flush and Lock.\"", " mysql -e \"FLUSH TABLES WITH READ LOCK;\"", " sync", " for target in $(lsblk -nlo MOUNTPOINTS)", " do", " # Do not freeze root and boot mountpoints", " if [[ \"$target\" == \"/\" ]]; then continue; fi", " if [[ \"$target\" == \"/boot\" ]]; then continue; fi", " echo \"INFO: Freezing $target\"", " error_message=$(sudo fsfreeze -f $target 2>&1)", " if [ $? -ne 0 ]", " then", " # Check if application is already in pre-script stage, for application consistent use case check if DB is locked", " # If the DB is already in pre-script stage, return error code 204", " if [[ \"$error_message\" == *\"$FS_ALREADY_FROZEN_ERROR\"* ]]", " then", " echo \"ERROR: Application already in pre-script completion state. Pre-Script operation failed.\"", " mysql -e \"UNLOCK TABLES;\"", " exit 204", " fi", " # If the Pre-script failed due to any reason other than the application already being in pre-script stage", " echo \"ERROR: Failed to execute the pre-script commands due to error $error_message\"", " mysql -e \"UNLOCK TABLES;\"", " exit 201", " fi", " echo \"INFO: Freezing complete on $target\"", " done", " echo \"INFO: Schedule Auto Thaw to execute after a delay\"", " $(nohup bash -c execute_schedule_auto_thaw >/dev/null 2>&1 &)", "}", "", "# Disable Auto Thaw if it is still enabled", "execute_disable_auto_thaw() {", " echo \"INFO: Attempt to disable Auto Thaw if enabled\"", " auto_thaw_pgid=$(pgrep -f execute_schedule_auto_thaw | xargs -i ps -fp {} -o pgid)", " if [ -n \"$auto_thaw_pgid\" ]; then", " echo \"INFO: execute_schedule_auto_thaw process found with pgid ${auto_thaw_pgid}\"", " sudo pkill -g ${auto_thaw_pgid}", " rc=$?", " if [ $rc -ne 0 ]; then", " echo \"ERROR: Unable to kill execute_schedule_auto_thaw process. retval=${rc}\"", " else", " echo \"INFO: Auto Thaw has been disabled\"", " fi", " fi", "}", "", "# Post script operation", "execute_post_script() {", " for target in $(lsblk -nlo MOUNTPOINTS)", " do", " # Do not unfreeze root and boot mountpoints", " if [[ \"$target\" == \"/\" ]]; then continue; fi", " if [[ \"$target\" == \"/boot\" ]]; then continue; fi", " echo \"INFO: Thawing $target\"", " error_message=$(sudo fsfreeze -u $target 2>&1)", " if [ $? -ne 0 ]", " then", " # Check if application is already in post-script stage", " if [[ \"$error_message\" == *\"$FS_ALREADY_THAWED_ERROR\"* ]]", " then", " echo \"ERROR: Application is already in thaw state.\"", " exit 205", " fi", " # If the Post-script failed due to any reason other than the application already in thaw state, return error", " echo \"ERROR: Failed to thaw the DB due to error $error_message\"", " exit 202", " fi", " echo \"INFO: Thaw complete on $target\"", " done", " echo \"INFO: Execute MYSQL Unlock \"", " mysql -e \"UNLOCK TABLES;\"", "}", "", "# Execute Auto Thaw after a delay", "execute_schedule_auto_thaw() {", " sleep 60", " execute_post_script", "}", "", "export -f execute_schedule_auto_thaw", "export -f execute_post_script", "", "# get the command from the parameters passed to the document", "operation=\"{{ command }}\"", "", "case $operation in", " pre-script)", " execute_pre_script", " ;;", " post-script)", " execute_disable_auto_thaw", " execute_post_script", " ;;", " dry-run)", " ;;", " *)", " echo \"ERROR: Invalid operation. Please use pre-script, post-script, dry-run.\"", " exit 1", " ;;", "esac" ] } } ] }
単に FLUSH TABLES するだけでなく、OS レベルのフリーズと 「失敗時の自動解凍(Auto-Thaw)」 が組み込まれていました。
実際の処理フローは以下の通りです。
- DB ロック: mysql -e "FLUSH TABLES WITH READ LOCK;" で DB への書き込みをブロック。
- ファイルシステム凍結: fsfreeze -f $target で OS レベルの書き込みを停止。
- スナップショット取得: (この間に AWS 側でスナップショット処理が走る)
- 【重要】Auto Thaw のスケジュール: バックグラウンドで「1分後に強制的に解凍するプロセス」を仕掛ける。これにより、万が一後続処理がコケても DB がロックされ続ける事故を防ぐ。
- 解凍処理: fsfreeze -u と UNLOCK TABLES で通常状態に戻す。
自前でスクリプトを書く際は、この 「プロセスが死んでも DB を道連れにしない(Auto-Thaw)」 実装がマストであると学びました。
2. コスト削減の切り札「EBS Snapshots Archive」
「コンプライアンス要件で 5 年分のバックアップが必要。でもアクセスの可能性はほぼゼロ」。そんな時に使うのが EBS Snapshots Archive です。
仕様と制約
- コスト削減率: 標準階層と比較して最大 75% のストレージコスト削減が可能。
- 保持期間の縛り: 最低 90日 の保持が必要。90日未満で削除や復元を行うと、早期削除料金が発生します。
- データ構造: アーカイブ化すると、増分(Incremental)ではなく フルバックアップ として保存されます。
- 【ポイント】: 変更率が高いボリュームの場合、毎回フルバックアップ扱いでアーカイブすると逆に高くなる可能性があります。ワークショップ資料では「変更率 25% 以上ならアーカイブの方が高くなる可能性がある」との示唆がありました。
- (標準階層は増分課金ですが、アーカイブはフルバックアップ課金となるため)
いつ使うべきか?
- プロジェクト終了時の「最終状態」の保存。
- 四半期、年次などの長期保存用バックアップ。
※ 日次バックアップには不向きです。
3. 【設計判断】DLM vs AWS Backup どっちを使う?
今回のワークショップでは Data Lifecycle Manager (DLM) を使用しましたが、AWS のバックアップ機能としては AWS Backup もあります。
実際に検討する際、どう使い分けるべきかを整理しました。
| 比較項目 | Data Lifecycle Manager (DLM) | AWS Backup |
|---|---|---|
| 対象リソース | 主に EBS と EC2 (EBS-backed AMI) に特化 | EBS, RDS, DynamoDB, EFS, S3 など AWS 全体 |
| 静止点確保 (Linux) | SSM Run Command との密な連携が可能。今回のように fsfreeze や DB コマンドを細かく制御したい場合に最適 | 基本的にはオンライン(静止点なし)バックアップとなる。Linux で静止点を取るには自前での作り込みやエージェント設定が別途必要 |
| 管理の粒度 | タグベースでポリシーを適用。リソース単位での柔軟な制御が得意 | バックアッププランで一元管理。組織全体のガバナンスを効かせたい場合に有利 |
| コスト | DLM 自体は無料(作成されたスナップショット料金のみ) | AWS Backup 自体は無料だが、一部機能(クロスアカウント管理等)で料金体系が異なる場合がある |
結論:こう使い分ける
- DLM を推奨するケース:
- 「EC2 上の自前 DB (MySQL/PostgreSQL) の整合性を完璧に取りたい」 場合。今回のワークショップのように、SSM ドキュメントで細かい静止点制御を行うなら DLM が最もスムーズです。
- コストを最小限に抑えたい、EBS 以外のバックアップ要件がない場合。
- AWS Backup を推奨するケース:
- 「RDS も EFS もまとめてバックアップを取得したい」 場合。
- AWS Organizations 全体でバックアップポリシーを強制したい場合。
- Vault Lock (WORM) 機能を使って、ランサムウェア対策などで「削除できないバックアップ」を作りたい場合。
まとめ
EBS スナップショットは、AWS コンソールから数クリックで作成できるため「簡単」と思われがちです。しかし、コスト効率と復元確実性を両立させるためには、以下のポイントを正しく判断する必要があります。
- 整合性の確保: 自前 DB の場合、クラッシュコンシステントで妥協せず、DLM + SSM で確実に静止点を確保する
- コストの最適化: 90日以上の長期保存かつ変更率が低いデータを見極めて Archive Tier を活用する
- ツールの選定: 要件(EC2 特化なのか、組織全体のガバナンスなのか)に応じて DLM と AWS Backup を適切に使い分ける
機能は知っていても、日々の運用の中で「とりあえずデフォルト設定のまま」になっているスナップショットポリシーはありませんか?
ぜひこの機会に、ご自身の環境のバックアップ設定やリストア手順を一度見直してみてください。意外なコスト削減のチャンスや、設定の落とし穴が見つかるかもしれません。
補足
re:Invent 2025 セッション情報
- Title STG306: Build protection and cost-optimize using Amazon EBS Snapshots
- Complexity level 300
- Topics Storage
- AWS services Amazon Elastic Block Store (Amazon EBS), Amazon Elastic Compute Cloud (Amazon EC2)
- Description Data protection and reducing long-term data retention costs are top priorities for most organizations. Join this session to learn how to protect Amazon EBS volumes with EBS Snapshots and Amazon Data Lifecycle Manager (DLM), which allows you to define policies that help you automate snapshot lifecycle management. Simple, secure data protection for your block storage data.
岡部 純 (執筆記事の一覧)
カスタマーサクセス部所属
AWS資格全冠取得、2024 - 2025 All Certifications Engineers
マルチアカウント、AWS Organizations 運用を得意としています