AWS Storage本番運用Vol2|Lifecycle×Tiering×Object Lambda×Express×CRR

目次

AWS Storage本番運用Vol2 — S3 Advanced|Lifecycle × Intelligent-Tiering × Object Lambda × Express One Zone × CRR 完全ガイド

S3 Advanced 全体アーキテクチャ|Lifecycle × Intelligent-Tiering × Object Lambda × Express One Zone × CRR

AWS本番運用 Storage本番運用シリーズ Vol2 S3 Advanced編 — Vol1 基礎本番運用からの拡張
本記事は Storage本番運用 Vol1 (S3/EFS/FSx/Storage Gateway 4本柱基礎) を完遂した中堅〜上級エンジニア向けの S3 Advanced編 です。Vol1 が「4ストレージサービスの基礎本番運用」を扱ったのに対し、Vol2 では S3 Lifecycle × Intelligent-Tiering × Object Lambda × Express One Zone × Cross-Region Replication の5本柱で、S3 Advanced機能をフル活用した本番品質設計力を確立します。

Vol1 (基礎4本柱) vs Vol2 (S3 Advanced 5本柱) 差別化マトリクス

  • 主軸: Vol1=S3/EFS/FSx/Storage Gateway 横断 / Vol2=S3 Advanced 5本柱
  • Lifecycle: Vol1=基本Transition Rule / Vol2=Versioning連動+多段階Tier運用
  • Tiering: Vol1=Storage Class紹介 / Vol2=Intelligent-Tiering AccessPattern分析+Archive/Deep Archive
  • 動的変換: Vol1=なし / Vol2=Object Lambda Access Point+マスキング+画像最適化
  • 高性能: Vol1=なし / Vol2=Express One Zone Single-AZ高性能+CRR RTC本番運用

本Vol2の対象: Vol1完遂後 S3 Lifecycle/Tiering/Object Lambda/Express/CRR を本番品質で運用したい中〜大規模チーム。


1. なぜ S3 Advanced編 か — Vol1基礎からの架橋 + 5本柱選定の現実解

Vol1完遂後に直面する5つの壁

Vol1で S3/EFS/FSx/Storage Gateway の基礎本番運用をマスターしたエンジニアが、次のステージで必ずぶつかる5つの壁がある。これらの壁は「S3の使い方は知っている」段階から「S3 Advancedを本番品質で設計・運用できる」段階へ進む際に現れる。

壁1: Lifecycle Rule管理の複雑化
プロジェクトが成熟するにつれ、Lifecycle Ruleは30本超に膨れ上がる。TransitionルールとExpirationルールが混在し、Versioning有効バケットでの削除マーカー管理が破綻しかける。Vol1で学んだ「Transition Ruleの基本」だけでは本番規模に対応できない。

壁2: Intelligent-Tiering vs 通常Storage Class の判断不能
「Intelligent-TieringはS3が自動で最適化してくれる」という理解で全バケットに一律適用した結果、128KB未満オブジェクトが大量の監視料金を生み出す。適用判断ロジックを体系化しないと、コスト最適化のはずが逆効果になる。

壁3: 動的変換をLambda関数の連鎖で実装
S3オブジェクトをクライアント要求に応じて動的に変換する処理を、APIゲートウェイ+Lambda+S3のチェーンで自作する。Object Lambda Access Pointを知らないと、リクエスト毎にLambdaが呼ばれる設計を別途構築し、維持コストが肥大化する。

壁4: Single-AZ高性能要件への対応
MLトレーニングやSpark処理の中間データ置き場として、S3 Standardより10倍高速な選択肢を求められる。Express One ZoneのSingle-AZリスクを正しく認識しないまま永続データに使用すると、AZ障害時のデータ消失が発生する。

壁5: CRR (Cross-Region Replication) のSLA設計
DR要件でCRRを設定したものの、「15分以内にレプリカ反映されるか」という保証ができない。Replication Time Control (RTC) の存在を知らないと、SLA要件を満たせないDR設計になってしまう。

5本柱選定の現実解

Vol2では以下5本柱を選定した。それぞれが「Advancedレベルの本番運用で最もROIが高い技術」という判断に基づく。

本柱課題解決本番インパクト
S3 Lifecycle 多段階運用Transition/Expiration管理破綻ストレージコスト30〜70%削減
Intelligent-Tiering 活用不規則アクセスのコスト最適化自動Tier管理で運用工数ゼロ化
Object Lambda動的変換の実装複雑化Lambda連鎖をAccess Point1本に置換
Express One ZoneML/Analytics一時データ高速化レイテンシ1桁ms / スループット向上
CRR + RTCDR要件の15分SLA保証本番フェイルオーバのRPO明示

本Vol2で得られる5つの成果

  1. Lifecycle多段階運用: TransitionルールとExpirationルールを体系設計し、Versioning連動を含む30本超Ruleの管理手法を確立
  2. Intelligent-Tiering活用: 128KB以上/不規則アクセス判断ロジックとArchive/Deep Archiveティア有効化で自動コスト最適化を実現
  3. Object Lambda実装: Lambda Access Pointを使ったPIIマスキング・画像最適化のパターンをTerraformで一気通貫実装
  4. Express One Zone設計: Single-AZリスクを踏まえたユースケース選定とML/Analytics基盤への適用
  5. CRR本番運用: RTC設定+CloudWatch Alarm+Failover設計で15分SLAを保証するDR構成

S3 Advanced 5本柱 本番運用ロードマップ

Vol2全体のロードマップを示す。各本柱は独立して実装できるが、推奨実装順序は「コスト最適化 → 動的変換 → 高性能/DR」の順だ。

フェーズ1: コスト最適化基盤の確立 (§2〜§3)
まずLifecycle多段階Tier運用 (§2) とIntelligent-Tiering (§3) で既存データのコストを削減する。新規プロジェクトでも初期からLifecycleルールを設計することでTCO (Total Cost of Ownership) を最適化できる。既存バケットへの適用は1TB以上のバケットから優先することで投資対効果が高い。

フェーズ2: 動的変換パイプラインの整備 (§4)
Object Lambda (§4) はアプリケーション側の変更なしにS3オブジェクトの動的変換を実現する。PIIマスキングやWebP変換などのユースケースが確定したタイミングで導入する。既存のAPI Gateway+Lambda連鎖を置き換えることでアーキテクチャを簡素化できる。

フェーズ3: 高性能/DR基盤の完成 (§5)
Express One Zone (§5) はML/Analytics基盤のパフォーマンスボトルネックが明確になった段階で導入する。CRR+RTCはDR要件定義後に設定する。SLA 15分以内の要件がある場合はRTCを必ず有効化する。

痛点5選: Vol1完遂者がS3 Advanced本番運用で直面する地雷

  • 痛点1: Lifecycle Rule 30+本管理破綻 (Transition/Expiration ルール衝突)
  • 痛点2: Intelligent-Tiering と通常Storage Class混在で月額コスト読めない
  • 痛点3: Object Lambda Access Point 設計でリクエスト課金爆発
  • 痛点4: Express One Zone Single-AZ で AZ障害時のデータ消失リスク認識不足
  • 痛点5: CRR RTC 15分SLA未満達成のための QoS 設定知識不在
本記事の読み方ガイド — 対象読者別
Vol1完遂したインフラエンジニア: §2のLifecycleから順番に読む。Vol1との差分を確認しながら段階的にAdvanced知識を積み上げる。

コスト削減を急ぐFinOpsエンジニア: §2 (Lifecycle) → §3 (Intelligent-Tiering) → コスト試算例の順で読む。即効性のある施策を優先的に把握できる。

DR/コンプライアンス担当者: §5のCRR+RTC+KMS連携を中心に読む。SLA設計とモニタリング設定に集中できる。

ML/Analytics基盤担当: §4のObject Lambda (動的変換) と §5のExpress One Zone (高性能) をセットで参照する。

Storage本番運用 Vol1 — S3/EFS/FSx/Storage Gateway 基礎本番運用


2. S3 Lifecycle本番運用 — Transition Rule × Expiration × Versioning連動 × Intelligent-Tiering連動

Lifecycle Rule の構造

S3 Lifecycle Configurationは1バケットあたり最大1,000件のルールを定義できる。各ルールは以下5つの要素で構成される。

{
  "Rules": [
 {
"ID": "production-logs-tiering",
"Status": "Enabled",
"Filter": {
  "And": {
 "Prefix": "logs/",
 "Tags": [{"Key": "env", "Value": "production"}],
 "ObjectSizeGreaterThan": 1024
  }
},
"Transitions": [
  {"Days": 30, "StorageClass": "STANDARD_IA"},
  {"Days": 90, "StorageClass": "GLACIER_IR"},
  {"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
],
"Expiration": {"Days": 2555},
"NoncurrentVersionTransitions": [
  {"NoncurrentDays": 30, "StorageClass": "STANDARD_IA"},
  {"NoncurrentDays": 90, "StorageClass": "DEEP_ARCHIVE"}
],
"NoncurrentVersionExpiration": {"NoncurrentDays": 365},
"AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
 }
  ]
}

各要素の役割:
Filter: どのオブジェクトにルールを適用するか (prefix/tag/size/複合条件)
Transitions: 何日後にどのStorage Classへ移行するか
Expiration: 何日後にオブジェクトを削除するか
NoncurrentVersionTransitions: バージョニング有効時の旧バージョン移行
NoncurrentVersionExpiration: バージョニング有効時の旧バージョン削除
AbortIncompleteMultipartUpload: 未完了Multipartアップロードの削除

Transition Rule 多段階運用

本番環境では以下の4段階Transition設計を基本とする。アクセス頻度とデータ特性に応じてカスタマイズする。

ステージStorage Class移行タイミング月額単価 (us-east-1)取得料金
Stage1S3 Standard作成時$0.023/GBなし
Stage2S3 Standard-IA30日後$0.0125/GB$0.01/GB
Stage3S3 Glacier Instant90日後$0.004/GB$0.03/GB
Stage4S3 Glacier Deep Archive365日後$0.00099/GB$0.02/GB

Transition最小サイズ制限の注意: Standard-IA・Glacier Instantへの移行は128KB未満オブジェクトが対象外 (課金は128KB換算)。小さいオブジェクトが多い場合はTransition先をDeep Archiveに絞る方がコスト効率が良い。

Transition最小保管期間: Standard-IAは最低30日分の課金が発生する。作成後30日未満でTransitionまたは削除しても30日分請求される。Deep Archiveは最低180日。

Terraform実装 — Lifecycle Configuration

resource "aws_s3_bucket_lifecycle_configuration" "production_logs" {
  bucket = aws_s3_bucket.production_logs.id

  rule {
 id  = "production-logs-tiering"
 status = "Enabled"

 filter {
and {
  prefix = "logs/"
  object_size_greater_than = 1024
  tags = {
 env = "production"
  }
}
 }

 transition {
days = 30
storage_class = "STANDARD_IA"
 }

 transition {
days = 90
storage_class = "GLACIER_IR"
 }

 transition {
days = 365
storage_class = "DEEP_ARCHIVE"
 }

 expiration {
days = 2555 # 7年保管 (コンプライアンス要件)
 }

 noncurrent_version_transition {
noncurrent_days = 30
storage_class= "STANDARD_IA"
 }

 noncurrent_version_transition {
noncurrent_days = 90
storage_class= "DEEP_ARCHIVE"
 }

 noncurrent_version_expiration {
noncurrent_days = 365
 }

 abort_incomplete_multipart_upload {
days_after_initiation = 7
 }
  }

  rule {
 id  = "temp-data-cleanup"
 status = "Enabled"

 filter {
prefix = "tmp/"
 }

 expiration {
days = 1
 }

 abort_incomplete_multipart_upload {
days_after_initiation = 1
 }
  }
}

重要: aws_s3_bucket_lifecycle_configuration リソースはバケット作成後に個別で管理する。aws_s3_bucket リソースの lifecycle_rule ブロック (旧API) は非推奨。

mermaid01: S3 Lifecycle Transition評価フロー

flowchart TD
 A[S3 Object 作成] --> B{Lifecycle Rule 評価}
 B --> C{Filter 条件マッチ?}
 C -- No --> D[ルール対象外: Standard維持]
 C -- Yes --> E{Object Age 判定}
 E --> F{Age ≥ 30日?}
 F -- No --> G[S3 Standard 維持]
 F -- Yes --> H[→ S3 Standard-IA 移行]
 H --> I{Age ≥ 90日?}
 I -- No --> J[Standard-IA 維持]
 I -- Yes --> K[→ S3 Glacier Instant 移行]
 K --> L{Age ≥ 365日?}
 L -- No --> M[Glacier Instant 維持]
 L -- Yes --> N[→ S3 Glacier Deep Archive 移行]
 N --> O{Age ≥ 2555日?}
 O -- No --> P[Deep Archive 維持]
 O -- Yes --> Q[Expiration: Object 削除]
 B --> R{Versioning 有効?}
 R -- Yes --> S{Noncurrent Version?}
 S -- Yes --> T[NoncurrentVersion Lifecycle 評価]
 T --> U[旧バージョン → IA/Deep Archive → 削除]
 style A fill:#4CAF50,color:#fff
 style Q fill:#f44336,color:#fff
 style D fill:#9E9E9E,color:#fff

Expiration Rule — Versioning有効時の削除マーカー管理

Versioning有効バケットでのExpirationは通常Expirationとは動作が異なる。

VersioningなしExpirationの動作:
Expiration.Days に達したオブジェクトは即削除
– シンプルだが、誤削除リスクがある
– バージョニングを有効にしていない本番バケットで誤削除が発生した場合は復旧不可

Versioning有効時の削除フロー:
1. Expirationルールが発動 → 削除マーカーが生成 (オブジェクト本体は残る)
2. 最新バージョンが削除マーカーの場合、ExpiredObjectDeleteMarker: true 設定で自動削除可能
3. NoncurrentVersionExpirationで旧バージョンを削除
4. newer_noncurrent_versions で保持するバージョン数を制限

resource "aws_s3_bucket_lifecycle_configuration" "versioning_example" {
  bucket = aws_s3_bucket.versioned.id

  rule {
 id  = "delete-marker-cleanup"
 status = "Enabled"

 filter {}

 expiration {
expired_object_delete_marker = true
 }

 noncurrent_version_expiration {
noncurrent_days  = 90
newer_noncurrent_versions = 3
 }

 abort_incomplete_multipart_upload {
days_after_initiation = 7
 }
  }
}

newer_noncurrent_versions の重要性: このパラメータを設定すると、「最新N件の旧バージョンを保持しつつ、それより古い旧バージョンはN日後に削除」という設計ができる。障害時のロールバック要件 (直近3バージョン保持など) に対応できる。

Versioning連動 — AbortIncompleteMultipartUpload

大容量ファイルのアップロードに使われるMultipart Uploadが途中で失敗すると、アップロード済みパーツがS3に残り課金が続く。これが「孤立Multipartアップロード問題」だ。

# 孤立Multipartアップロードの確認コマンド
aws s3api list-multipart-uploads \
  --bucket your-production-bucket \
  --query 'Uploads[].{Key:Key,Initiated:Initiated,UploadId:UploadId}' \
  --output table

# 手動クリーンアップ (Lifecycleルール設定前の緊急対応)
aws s3api abort-multipart-upload \
  --bucket your-production-bucket \
  --key "path/to/large-file.tar.gz" \
  --upload-id "UPLOAD_ID_HERE"

AbortIncompleteMultipartUpload は全バケット必須設定: 1〜7日の設定を推奨。ML/データ処理バケットで大容量ファイルを扱う場合は1日設定が安全。

S3コンソールからの確認方法: S3マネジメントコンソール → バケット → 「マルチパートアップロード」タブ → 7日以上前のアップロードがあれば孤立候補。Cost Explorerでも「S3 Multipart Upload Storage」として課金が見える。

Filter条件の設計パターン

Lifecycle Ruleのfilterを適切に設計することで、バケット内の異なる用途のオブジェクトに別々のLifecycleを適用できる。

prefix単独フィルタ (最もシンプル):

logs/ → 30日でIA移行
tmp/ → 1日で削除
archive/ → 0日でDeep Archive

prefix+tag複合フィルタ (本番推奨):
prefix のみだとパス設計変更の影響を受けるため、tag と組み合わせることで柔軟性が高まる。

フィルタ条件ユースケース設定例
prefix のみ用途別ディレクトリが固定prefix = "logs/"
tag のみディレクトリ横断で同一Lifecycletag env=production
prefix + tag用途×環境の組み合わせprefix = "logs/" AND tag env=production
ObjectSizeGreaterThan大容量のみTieringobject_size_greater_than = 131072 (128KB)

Filterなし (全オブジェクト対象): 特定のルール (AbortIncompleteMultipartUpload, delete marker cleanup) はfilterを空にして全オブジェクトに適用する。

Lifecycle Ruleの監視とCloudWatchアラーム

Lifecycle設定が意図通りに動作しているかを監視するためのCloudWatchメトリクス:

# S3 Storage Lensで各Storage Classの分布を確認
aws s3control get-storage-lens-dashboard \
  --account-id 123456789012 \
  --config-id my-storage-lens-dashboard \
  --region us-east-1

# Storage Class別のオブジェクト数と容量を確認
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name BucketSizeBytes \
  --dimensions Name=BucketName,Value=your-bucket Name=StorageType,Value=StandardIAStorage \
  --start-time 2026-01-01T00:00:00Z \
  --end-time 2026-01-31T00:00:00Z \
  --period 86400 \
  --statistics Average

S3 Storage Lensのダッシュボードを活用することで、Lifecycle Ruleが想定通りに動作しているか、Storage Classの分布が目標値に近づいているかを月次で確認できる。

Intelligent-Tiering との組み合わせパターン

Lifecycle Transition と Intelligent-Tiering は原則として排他運用を推奨するが、一部のユースケースでは組み合わせパターンが有効だ。

パターン1: 排他運用 (推奨)
– 規則的なアクセスパターン → Lifecycle Transition
– 不規則なアクセスパターン → Intelligent-Tiering
– 2つを同じオブジェクトに同時適用しない

パターン2: フェーズ別運用
1. 作成後90日は Intelligent-Tiering で自動最適化
2. 90日後にLifecycle Transitionで Glacier Deep Archive へ強制移行

resource "aws_s3_bucket_lifecycle_configuration" "combined_tiering" {
  bucket = aws_s3_bucket.analytics.id

  rule {
 id  = "intelligent-tiering-then-archive"
 status = "Enabled"

 filter {
prefix = "analytics/"
 }

 transition {
days = 0
storage_class = "INTELLIGENT_TIERING"
 }

 transition {
days = 365
storage_class = "DEEP_ARCHIVE"
 }
  }
}

パターン2の注意点: Intelligent-Tiering から Deep Archive へのTransitionは、Intelligent-Tieringの最低監視期間 (30日) が経過していないと移行できない。days = 365 は作成後365日を起点にするため、実際には「少なくとも365日後にDeep Archiveへ移行」となる。

コスト試算例 — 1TB/月のログデータ本番運用

以下の条件で Lifecycle多段階Tier運用のコスト効果を試算する:
– データ量: 1TB/月の新規ログ蓄積
– 保管期間: 7年 (2,555日)
– アクセス頻度: 作成後30日は頻繁 / 90日以降はほぼアクセスなし

経過年データ量累積ストレージクラス分布月額コスト試算
1年目12TBStandard:1TB / IA:11TB約 $151
2年目24TBStandard:1TB / IA:1TB / Glacier Instant:10TB / Deep Archive:12TB約 $62
3年目36TBStandard:1TB / Deep Archive:35TB約 $37

3年目の削減効果: 全てStandard維持した場合 $828/月 → Lifecycle最適化後 $37/月 = 95.5%削減

Transition発生時の操作料金:
– Standard → IA: $0.01/1,000リクエスト (無視できる規模)
– IA → Glacier Instant: $0.05/1,000リクエスト
– Glacier Deep Archive復元: $0.02/GB (緊急復元は$0.03/GB)

S3 Lifecycle 本番運用5原則

  • 原則1: Transition Rule は段階的設計 (Standard → IA → Glacier Instant → Deep Archive 4段階推奨)
  • 原則2: Noncurrent Version は別Lifecycle Rule で管理 (Current より短期間で削除)
  • 原則3: AbortIncompleteMultipartUpload 7日 を全Bucket必須設定 (孤立Upload削除)
  • 原則4: Filter は prefix+tag 複合 (用途別Bucket管理が現実的)
  • 原則5: Intelligent-Tiering と Lifecycle Transition は排他 (重複コスト発生防止)
Storage Class 選定マトリクス (Lifecycle視点)

  • S3 Standard: 頻繁アクセス / 即時取得 / $0.023/GB — 作成直後〜30日
  • S3 Standard-IA: 月数回アクセス / 即時取得 / $0.0125/GB — 30〜90日
  • S3 Glacier Instant: 月1回アクセス / 即時取得 / $0.004/GB — 90〜365日
  • S3 Glacier Flexible: 年数回アクセス / 数分〜数時間取得 / $0.0036/GB — 1〜7年
  • S3 Glacier Deep Archive: 年1回未満 / 12時間取得 / $0.00099/GB — 長期アーカイブ
アンチパターン: Lifecycle Rule を後から大量追加
既存バケットに後から30本超のLifecycle Ruleを追加すると、ルール間の優先順位衝突が発生する。特に、Transitionルールが複数の異なるStorage Classへの移行を指定している場合、S3は「最も早いTransition」を採用するため意図しないクラス変更が起きる。

対策: バケット設計初期からLifecycle Ruleを計画する。既存バケットへの追加時は小規模テストバケットで動作確認後に本番適用する。AWS S3 Storage Lensでオブジェクトのストレージクラス分布を確認し、ルール適用前後を比較する。


3. S3 Intelligent-Tiering本番運用 — AccessPattern分析 × Archive × Deep Archive × トレードオフ

S3 Intelligent-Tieringは、オブジェクト単位のアクセス履歴を自動追跡し、アクセス頻度に応じて最適なストレージ層へ自動移行するマネージドサービスである。規則性のないアクセスパターンを持つ大量オブジェクトに対して、手動のLifecycleルール設計を不要にしながら最大95%のコスト削減を実現する。本セクションでは、本番環境での導入判断基準から、Archive/Deep Archive Tierの活性化、Terraform実装、コスト試算まで体系的に解説する。

3.1 Intelligent-Tiering の仕組み — 自動Tier管理メカニズム

Intelligent-Tieringはオブジェクト単位でアクセス日時を記録し、最後のアクセスからの経過日数に応じて下位Tierへ自動移行する。移行処理そのものは無料だが、各Tierの保管料金と取得料金は異なる。アクセスが発生した場合はFrequent Accessへ即時復帰する(Archive系Tierからの復元を除く)。

自動移行タイムライン(デフォルト設定):

状態移行先Tier保管料金(us-east-1)取得料金取得時間
初期配置またはアクセス直後Frequent Access$0.023/GB/月無料即時
30日間アクセスなしInfrequent Access$0.0125/GB/月無料即時
90日間アクセスなしArchive Instant Access$0.004/GB/月無料即時
90日〜設定日数(要有効化)Archive Access$0.0045/GB/月$0.02/GB1分〜5時間
180日〜設定日数(要有効化)Deep Archive Access$0.00099/GB/月$0.05/GB12時間

Infrequent AccessとArchive Instant Accessは取得料金がゼロである点が、S3 Standard-IAやGlacier Instantと比較した際の大きな優位性となる。アクセス予測が困難なデータでも、取得コストを心配せずに保管できる。

監視コスト(モニタリング料金)の構造:

Intelligent-Tieringを使用する際は、オブジェクト数に比例した監視料金が発生する。この料金がストレージ削減効果を上回ると逆効果になるため、導入前の試算が必須である。

  • 料金: $0.0025 per 1,000 objects / 月
  • 10万オブジェクトの場合: $0.25/月(監視料金の影響は軽微)
  • 100万オブジェクトの場合: $2.50/月(削減効果との対比が重要)
  • 1,000万オブジェクトの場合: $25.00/月(大規模環境では無視できない固定費)
S3 Intelligent-Tiering 本番運用5原則

  • 原則1: 128KB以上+不規則アクセスObjectにIntelligent-Tieringを適用する
  • 原則2: Archive Access (90日) + Deep Archive Access (180日) を必ず有効化する(デフォルト無効のため)
  • 原則3: 規則的アクセス (毎日/毎週) はLifecycle Transitionの方が安価であり、Intelligent-Tieringを選ばない
  • 原則4: Storage LensでTier分布を月次監視し、想定外のArchive偏りや監視コスト超過を早期検知する
  • 原則5: Lifecycle TransitionとIntelligent-Tieringを同一Bucketで混在させない(重複コスト発生防止)

3.2 Tier構成と選定基準 — Frequent から Deep Archive Access まで

Intelligent-Tiering内の5Tierは、アクセス頻度と取得時間要件に応じて使い分ける。自動移行する上位3Tier(Frequent/Infrequent/Archive Instant Access)は追加設定不要だが、Archive AccessとDeep Archive Accessは明示的な有効化が必要である。

Tier詳細スペックと推奨用途:

Tier名移行条件保管料金(us-east-1)取得料金取得時間推奨用途
Frequent Access初期/アクセス直後$0.023/GB/月無料即時頻繁アクセスObject
Infrequent Access30日アクセスなし$0.0125/GB/月無料即時月数回アクセス
Archive Instant Access90日アクセスなし$0.004/GB/月無料即時四半期程度のアクセス
Archive Access90日〜(要有効化)$0.0045/GB/月$0.02/GB1分〜5時間非同期でよいバックアップ参照
Deep Archive Access180日〜(要有効化)$0.00099/GB/月$0.05/GB12時間法規制対応の長期保管データ

Archive系Tier選択の判断基準:

Archive Accessは復元後の即時利用が不要なワークロードに適する。典型例としては、月次バックアップの定期整合性検証、内部監査用の過去ログ参照、DR演習時の旧バージョンデータ取得などが挙げられる。Deep Archive Accessは復元に12時間を要するため、前日申請が必須となる用途(コンプライアンス保管ログの年次提出など)に限定する。

復元頻度が不規則な場合は、Archive Instant Access(取得料金無料・即時復帰)の方が、取得コスト込みの総コストで優位になるケースが多い。Archive Accessの取得料金$0.02/GBが積み上がると、削減効果を相殺することがある。

アンチパターン: 全Bucket一律Intelligent-Tiering適用
128KB未満のObjectが多数存在するBucketにIntelligent-Tieringを適用すると、監視料金($0.0025/1,000 objects/月)だけが発生し、Tier移行のコスト削減効果をほとんど得られない。例えば100万件の1KBサムネイル画像BucketにIntelligent-Tieringを設定すると、$2.5/月の監視料金が発生する一方でコスト削減はほぼゼロとなる。対策としてStorage Lensで「平均Objectサイズ」を事前確認し、128KB以上のObjectが大半を占めるBucketのみに適用範囲を限定すること。

3.3 本番適用判断基準 — 128KBルールとROI計算

Intelligent-Tiering導入の是非は「監視コストに対するストレージ削減効果(ROI)」で判断する。以下の手順で適用対象Bucketを特定し、費用対効果を事前試算する。

STEP 1: Storage Lensで対象Bucketのオブジェクトサイズ分布を確認する

# Storage Lens設定一覧表示
aws s3control list-storage-lens-configurations \
  --account-id $(aws sts get-caller-identity --query Account --output text)

# 対象BucketのオブジェクトサイズをCloudWatch Metricsで確認
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name BucketSizeBytes \
  --dimensions \
 Name=BucketName,Value=my-target-bucket \
 Name=StorageType,Value=StandardStorage \
  --start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
  --end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
  --period 86400 \
  --statistics Average \
  --output json | jq '.Datapoints[-1].Average / 1024 / 1024 / 1024 | "Total GB: \(.)"'

# オブジェクト数確認
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name NumberOfObjects \
  --dimensions \
 Name=BucketName,Value=my-target-bucket \
 Name=StorageType,Value=AllStorageTypes \
  --start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
  --end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
  --period 86400 \
  --statistics Average \
  --output json | jq '.Datapoints[-1].Average | "Object Count: \(.)"'

STEP 2: 簡易ROI試算(Python)

# Intelligent-Tiering導入可否のROI計算スクリプト
total_objects = 1_000_000 # 対象Bucket内Object数
total_gb= 10_000 # 合計データ量 (GB)
ia_ratio= 0.60# Infrequent以下に移行すると仮定する割合
archive_ratio = 0.30# Archive Instant以下に移行すると仮定する割合

# 監視コスト (月額)
monitoring_cost = (total_objects / 1000) * 0.0025

# S3 Standard 想定コスト
standard_cost = total_gb * 0.023

# Intelligent-Tiering 想定コスト (Frequent: 10%, IA: 60%, Archive Instant: 30%)
frequent_gb= total_gb * 0.10
ia_gb= total_gb * ia_ratio
archive_instant_gb = total_gb * archive_ratio

it_storage_cost = (frequent_gb * 0.023) + (ia_gb * 0.0125) + (archive_instant_gb * 0.004)
it_total_cost= it_storage_cost + monitoring_cost

storage_saving  = standard_cost - it_total_cost
roi_percent  = (storage_saving / standard_cost) * 100

print(f"Standard月額: ${standard_cost:,.2f}")
print(f"Intelligent-Tiering月額: ${it_total_cost:,.2f} (監視料金 ${monitoring_cost:.2f} 含む)")
print(f"月額削減効果:  ${storage_saving:,.2f} ({roi_percent:.1f}%削減)")
print(f"ROI判定:{'POSITIVE → 導入推奨' if storage_saving > 0 else 'NEGATIVE → 導入見送り'}")

適用判断サマリ:

条件判断理由
平均Object ≥ 128KB適用検討Tier移行の恩恵を受けられる
Object数 ≥ 10万件適用検討監視コストに対する削減効果が出やすい
アクセスパターンが不規則Intelligent-Tiering優位自動最適化が最もROI高い
アクセスパターンが規則的Lifecycle Transition優位監視料金不要で予測可能なコスト
平均Object < 128KB適用非推奨監視料金のみ発生でコスト削減ゼロ

3.4 Archive Access Tier 活性化と運用 — 90日/180日設定

Archive AccessとDeep Archive Accessはデフォルトで無効のため、Intelligent-Tiering設定時に明示的な有効化が必要である。有効化すると、設定した日数以上アクセスのないオブジェクトが自動的にArchive層へ移行し、保管コストが大幅に削減される。

Archive Tier有効化パラメータ詳細:

パラメータArchive AccessDeep Archive Access
設定値ARCHIVE_ACCESSDEEP_ARCHIVE_ACCESS
最小設定日数90日180日
推奨設定日数90〜150日180〜365日
取得速度 (Expedited)1〜5分非対応
取得速度 (Standard)3〜5時間12時間
取得料金 (Standard)$0.02/GB$0.05/GB
復元後の挙動Frequent Accessへ自動復帰同左

復元操作(RestoreObject API):

# Archive Access Tierからのオブジェクト復元 (Standard: 3〜5時間)
aws s3api restore-object \
  --bucket my-intelligent-tiering-bucket \
  --key archives/database-backup-2025-01.tar.gz \
  --restore-request '{
 "Days": 7,
 "GlacierJobParameters": {
"Tier": "Standard"
 }
  }'

# Expedited復元 (Archive Access Tierのみ: 1〜5分)
aws s3api restore-object \
  --bucket my-intelligent-tiering-bucket \
  --key archives/recent-backup-2025-03.tar.gz \
  --restore-request '{
 "Days": 3,
 "GlacierJobParameters": {
"Tier": "Expedited"
 }
  }'

# 復元ステータス確認 (Ongoing-Request: true = 進行中, false = 完了)
aws s3api head-object \
  --bucket my-intelligent-tiering-bucket \
  --key archives/database-backup-2025-01.tar.gz \
  --query '{RestoreStatus: Restore, StorageClass: StorageClass}'

Archive Tierへの移行後に発生するコスト:

  • 復元リクエスト料金: $0.02/1,000 requests(Archive Access)、$0.05/1,000 requests(Deep Archive Access)
  • 取得料金: $0.02/GB(Archive Access Standard)、$0.05/GB(Deep Archive Access)
  • Expedited取得料金: $0.10/GB + $0.03/1,000 requests(Archive Accessのみ)
  • 復元後の保管: 指定Daysの間Frequent Accessへ復帰(追加ストレージ料金なし)

3.5 Terraform実装 — aws_s3_bucket_intelligent_tiering_configuration

Terraformでは aws_s3_bucket_intelligent_tiering_configuration リソースでIntelligent-Tiering設定を管理する。Archive Access/Deep Archive Access有効化も同リソース内で定義する。

# S3 Bucket本体
resource "aws_s3_bucket" "intelligent_tiering" {
  bucket = var.bucket_name

  tags = {
 Environment = var.environment
 CostCenter  = var.cost_center
 ManagedBy= "terraform"
  }
}

# バージョニング有効化 (Intelligent-Tiering環境での推奨設定)
resource "aws_s3_bucket_versioning" "intelligent_tiering" {
  bucket = aws_s3_bucket.intelligent_tiering.id
  versioning_configuration {
 status = "Enabled"
  }
}

# Intelligent-Tiering設定 (Bucket全体 + Archive系Tier有効化)
resource "aws_s3_bucket_intelligent_tiering_configuration" "main" {
  bucket = aws_s3_bucket.intelligent_tiering.id
  name= "EntireBucketWithArchive"

  # Archive Access Tier有効化: 90日アクセスなしで自動移行
  tiering {
 access_tier = "ARCHIVE_ACCESS"
 days  = 90
  }

  # Deep Archive Access Tier有効化: 180日アクセスなしで自動移行
  tiering {
 access_tier = "DEEP_ARCHIVE_ACCESS"
 days  = 180
  }
}

# prefix+tagでフィルタリング (128KB以上Object専用Prefixに限定適用)
resource "aws_s3_bucket_intelligent_tiering_configuration" "large_objects" {
  bucket = aws_s3_bucket.intelligent_tiering.id
  name= "LargeObjectsArchivePolicy"

  filter {
 prefix = "large-objects/"
 tags = {
ObjectSizeClass = "large"
 }
  }

  tiering {
 access_tier = "ARCHIVE_ACCESS"
 days  = 120
  }

  tiering {
 access_tier = "DEEP_ARCHIVE_ACCESS"
 days  = 270
  }
}

# 128KB未満Object用 Lifecycle Rule (Intelligent-Tiering対象外を別管理)
resource "aws_s3_bucket_lifecycle_configuration" "small_objects" {
  bucket = aws_s3_bucket.intelligent_tiering.id

  rule {
 id  = "small-objects-expiration"
 status = "Enabled"

 filter {
object_size_less_than = 131072  # 128KB = 131072 bytes
 }

 expiration {
days = 365
 }

 abort_incomplete_multipart_upload {
days_after_initiation = 7
 }
  }
}

Terraform適用コマンド:

# 初期化と計画確認
terraform init
terraform plan \
  -var="bucket_name=my-intelligent-tiering-prod" \
  -var="environment=production" \
  -var="cost_center=engineering-storage"

# 適用
terraform apply \
  -var="bucket_name=my-intelligent-tiering-prod" \
  -var="environment=production" \
  -var="cost_center=engineering-storage" \
  -auto-approve

# Intelligent-Tiering設定確認
aws s3api get-bucket-intelligent-tiering-configuration \
  --bucket my-intelligent-tiering-prod \
  --id EntireBucketWithArchive

3.6 コスト比較と費用試算 — Standard vs Standard-IA vs Intelligent-Tiering

実際の費用試算では「アクセス頻度の不規則性」と「オブジェクトサイズ分布」を加味した比較が重要である。以下は1TB (= 約10万件 × 10MB/件) のデータを管理する場合の月額コスト試算例(us-east-1リージョン)。

前提条件:
– データ量: 1TB
– オブジェクト数: 100,000件
– アクセスパターン: 不規則(月平均30%がアクセスあり)
– Archive Access設定: 有効化済み(90日)

月額コスト比較表 (1TB / us-east-1):

ストレージクラス保管料金取得料金(想定)監視料金月額合計S3 Standard比
S3 Standard (全量)$23.55$0.00$0.00$23.55基準
S3 Standard-IA (全量)$12.80$3.07 (30GB取得)$0.00$15.87-33%
Intelligent-Tiering (FA40%/IA60%)$17.10$0.00$0.25$17.35-26%
Intelligent-Tiering (FA30%/IA40%/AIA30%)$12.50$0.00$0.25$12.75-46%
Intelligent-Tiering (+Archive有効化)変動取得頻度依存$0.25$7〜12-49〜70%

Archive Access有効化で長期的な削減効果は最大になるが、取得コストが発生するため復元頻度との兼ね合いで総コストが変動する。取得頻度ゼロの場合は$7程度まで下がり、S3 Standardの70%削減を実現できる。

アクセスパターン別推奨クラス:

{
  "access_patterns": [
 {
"pattern": "daily_access",
"description": "毎日アクセスあり",
"recommended": "S3 Standard",
"reason": "Intelligent-Tiering監視コストがオーバーヘッドになる"
 },
 {
"pattern": "weekly_access",
"description": "週数回アクセス、規則的",
"recommended": "S3 Standard-IA with Lifecycle",
"reason": "規則的パターンはLifecycle Transitionが予測可能でコスト安定"
 },
 {
"pattern": "irregular_monthly",
"description": "月1〜数回、不規則",
"recommended": "S3 Intelligent-Tiering",
"reason": "自動最適化で手動管理不要、取得料金無料Tierが有効"
 },
 {
"pattern": "rare_irregular",
"description": "年数回アクセス、不規則",
"recommended": "Intelligent-Tiering + Archive Access有効化",
"reason": "Archive Tierで最大コスト削減、復元時間を許容できる用途向け"
 },
 {
"pattern": "compliance_archive",
"description": "法規制対応、年1回未満",
"recommended": "Intelligent-Tiering + Deep Archive有効化",
"reason": "12時間復元を許容できる保管データに最低単価を適用"
 }
  ]
}

3.7 アクセスパターン分析 — CloudWatch + Athena による可視化

Intelligent-Tiering導入前の適用対象特定と、導入後のTier分布監視にはCloudWatch S3 Access MetricsとAthena S3 Server Access Logsを組み合わせる。

CloudWatch S3 Access Metrics(有料: $0.10/メトリクス/月):

# S3 Access Metrics有効化 (Bucket全体)
aws s3api put-bucket-metrics-configuration \
  --bucket my-intelligent-tiering-bucket \
  --id AllObjectMetrics \
  --metrics-configuration '{"Id": "AllObjectMetrics"}'

# Intelligent-Tiering Tier別ストレージ量の確認 (Frequent Access)
aws cloudwatch get-metric-statistics \
  --namespace AWS/S3 \
  --metric-name BucketSizeBytes \
  --dimensions \
 Name=BucketName,Value=my-intelligent-tiering-bucket \
 Name=StorageType,Value=IntelligentTieringFAStorage \
  --start-time $(date -u -d '30 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
  --end-time $(date -u '+%Y-%m-%dT%H:%M:%SZ') \
  --period 86400 \
  --statistics Average \
  --output table

StorageType一覧(Intelligent-Tiering関連):

StorageType値対象Tier確認目的
IntelligentTieringFAStorageFrequent Access頻繁アクセスObject量の把握
IntelligentTieringIAStorageInfrequent Access30日非アクセスObject量の把握
IntelligentTieringAIAStorageArchive Instant Access90日非アクセスObject量の把握
IntelligentTieringAAStorageArchive AccessArchive移行済みObject量の把握
IntelligentTieringDAAStorageDeep Archive Access最深層Object量の把握

S3 Server Access Logsの有効化:

# ログ格納Bucketへのロギング設定
aws s3api put-bucket-logging \
  --bucket my-intelligent-tiering-bucket \
  --bucket-logging-status '{
 "LoggingEnabled": {
"TargetBucket": "my-access-logs-bucket",
"TargetPrefix": "s3-logs/intelligent-tiering/"
 }
  }'

Athena: Object単位アクセス頻度集計:

-- Athena外部テーブル作成 (S3 Server Access Logs)
CREATE EXTERNAL TABLE IF NOT EXISTS s3_access_logs (
  bucketowner STRING,
  bucketSTRING,
  requestdatetime STRING,
  remoteip STRING,
  requesterSTRING,
  requestidSTRING,
  operationSTRING,
  keySTRING,
  requesturi  STRING,
  httpstatus  STRING,
  bytessentBIGINT,
  objectsize  BIGINT
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
  'input.regex' = '([^ ]*) ([^ ]*) \\[(.*?)\\] ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) (".*?"|-)(-|[0-9]*) ([^ ]*) ([^ ]*) .*'
)
LOCATION 's3://my-access-logs-bucket/s3-logs/intelligent-tiering/';

-- Object別アクセス回数集計 (過去30日)
SELECT
  key,
  objectsize,
  COUNT(*) AS access_count,
  MAX(requestdatetime) AS last_accessed,
  CASE
 WHEN COUNT(*) >= 30 THEN 'Frequent (毎日)'
 WHEN COUNT(*) >= 4  THEN 'Infrequent (週1回)'
 WHEN COUNT(*) >= 1  THEN 'Rare (月数回)'
 ELSE 'Not Accessed'
  END AS access_pattern
FROM s3_access_logs
WHERE operation = 'REST.GET.OBJECT'
  AND date_parse(requestdatetime, '%d/%b/%Y:%H:%i:%S %z')
> current_timestamp - interval '30' day
GROUP BY key, objectsize
ORDER BY access_count ASC
LIMIT 200;

-- 128KB未満Object数の割合確認 (Intelligent-Tiering適用可否判断)
SELECT
  CASE
 WHEN objectsize < 131072 THEN '128KB未満 (IT対象外)'
 WHEN objectsize < 1048576 THEN '128KB〜1MB'
 WHEN objectsize < 104857600 THEN '1MB〜100MB'
 ELSE '100MB以上'
  END AS size_tier,
  COUNT(*) AS object_count,
  ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentage
FROM s3_access_logs
WHERE date_parse(requestdatetime, '%d/%b/%Y:%H:%i:%S %z')
> current_timestamp - interval '7' day
GROUP BY 1
ORDER BY 2 DESC;
Intelligent-Tiering 月次監視チェックリスト

  • Tier分布確認: CloudWatch StorageType MetricsでFrequent/IA/Archive Instant/Archive比率を確認する(Archive偏りが大きすぎる場合は取得コスト増大リスクを評価する)
  • 監視コスト対効果: (ストレージ削減額 – 監視料金) のROIを月次で算出し、負になっていないか確認する
  • 新規Object追加状況: Storage Lensで128KB未満の新規Object増加がないか確認する(増加している場合は専用Lifecycle Ruleで削除または除外する)
  • Archive復元コスト追跡: Cost ExplorerでArchive/Deep Archive取得料金の月次推移を確認し、異常な復元頻度を検知する
  • Lifecycle重複チェック: 同一BucketにLifecycle TransitionルールとIntelligent-Tieringが重複設定されていないかTerraform tfstateで確認する
Intelligent-Tiering vs Lifecycle Transition 選択マトリクス

  • 不規則アクセス + 大量Object (10万件以上) + 平均128KB以上 → Intelligent-Tiering: 自動最適化で運用工数ゼロ化、ROI最大
  • 規則的アクセス (日次/週次バッチ処理) → Lifecycle Transition: 監視料金不要で予測可能なコスト構造
  • 128KB未満Object多数のBucket → Lifecycle Transition + 削除ルール: Intelligent-Tiering対象外のため監視料金のみ発生を防ぐ
  • 法規制対応の長期保管データ → Intelligent-Tiering + Deep Archive有効化: 自動アーカイブで運用不要、最低保管単価を実現
  • 判断が難しいケース → Storage Lens 1ヶ月観察後に判断: 実アクセスデータでObject Size分布とアクセス頻度を把握してから適用対象を絞り込む

4. S3 Object Lambda本番運用 — Lambda Access Point × 動的変換 × マスキング × 画像最適化

S3 Object Lambda Access Point + 動的変換フロー (PIIマスキング/画像最適化)

S3 Object Lambda は、S3 からオブジェクトを取得する際に Lambda 関数を透過的に介在させ、データを動的変換して返却する仕組みだ。アプリケーション側のコード変更は不要で、Object Lambda Access Point の URL に向けた通常の GET リクエストが Lambda を経由する。個人情報マスキング・画像フォーマット変換・CSV→JSON 変換などを S3 側で完結できるため、アプリケーション層の複雑さを大幅に削減できる。

4-1. Object Lambda アーキテクチャ概要

flowchart TD
 Client["クライアント\nGET リクエスト"]
 OL_AP["S3 Object Lambda\nAccess Point"]
 Lambda["Lambda Function\n(変換ロジック)"]
 S3_AP["Supporting\nAccess Point"]
 S3["S3 バケット\n(原本データ)"]
 Client -->|"GET s3-object-lambda://..."| OL_AP
 OL_AP -->|"Lambda Invoke\n(S3 Object Lambda event)"| Lambda
 Lambda -->|"GetObject (内部)"| S3_AP
 S3_AP -->|"原本オブジェクト取得"| S3
 S3 -->|"原本データ返却"| Lambda
 Lambda -->|"WriteGetObjectResponse\n(変換済みデータ)"| OL_AP
 OL_AP -->|"変換済みレスポンス"| Client

Object Lambda の実行フロー (上図):

  1. クライアントが Object Lambda Access Point の URL へ GET リクエストを送信する
  2. S3 が Lambda 関数を呼び出し、S3 Object Lambda イベントを渡す
  3. Lambda は イベント内の getObjectContext.inputS3Url を使って Supporting Access Point 経由で原本オブジェクトを取得する
  4. Lambda は変換ロジック(マスキング/リサイズ等)を適用し、WriteGetObjectResponse API で変換済みデータを返却する
  5. クライアントは変換済みオブジェクトをレスポンスとして受け取る

Supporting Access Point との関係: Object Lambda Access Point は必ず既存の S3 Access Point (Supporting Access Point) に紐付く。Supporting Access Point はバケットへの標準的なアクセスを提供し、Object Lambda が原本データを取得するための経路として機能する。

S3 Object Lambda 本番運用5原則

  • 原則1: Lambda Memory は 1 GB 以上 (Object 変換時の OOM 予防)
  • 原則2: Provisioned Concurrency で Cold Start 予防 (Critical Path 用途)
  • 原則3: Stream Response でレスポンス時間最小化 (大 Object 変換)
  • 原則4: IAM Role は Object Lambda 専用 (二重 ACL 予防)
  • 原則5: 課金監視 (Lambda + S3 Object Lambda の二重課金を Cost Explorer Tag 追跡)

4-2. Terraform による Lambda Access Point 構築

Object Lambda の構成要素は 4 つ: S3 バケット / Supporting Access Point / Lambda 関数 / Object Lambda Access Point だ。Terraform で一括管理することで、環境間の差異を排除できる。

# S3 バケット (原本データ格納)
resource "aws_s3_bucket" "source" {
  bucket = "${var.project}-object-lambda-source"
}

resource "aws_s3_bucket_versioning" "source" {
  bucket = aws_s3_bucket.source.id
  versioning_configuration {
 status = "Enabled"
  }
}

# Supporting Access Point (Object Lambda が原本取得に使用)
resource "aws_s3_access_point" "supporting" {
  bucket = aws_s3_bucket.source.id
  name= "${var.project}-supporting-ap"

  vpc_configuration {
 vpc_id = var.vpc_id
  }
}

# Lambda 実行 IAM Role
resource "aws_iam_role" "object_lambda" {
  name = "${var.project}-object-lambda-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "object_lambda" {
  role = aws_iam_role.object_lambda.id
  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "s3-object-lambda:WriteGetObjectResponse"
  ]
  Resource = "*"
},
{
  Effect = "Allow"
  Action = ["s3:GetObject"]
  Resource = "${aws_s3_bucket.source.arn}/*"
},
{
  Effect = "Allow"
  Action = [
 "logs:CreateLogGroup",
 "logs:CreateLogStream",
 "logs:PutLogEvents"
  ]
  Resource = "arn:aws:logs:*:*:*"
}
 ]
  })
}

# Lambda 関数 (変換ロジック)
resource "aws_lambda_function" "object_lambda_transform" {
  function_name = "${var.project}-object-lambda-transform"
  role = aws_iam_role.object_lambda.arn
  handler = "handler.lambda_handler"
  runtime = "python3.12"
  filename= data.archive_file.lambda_zip.output_path
  timeout = 60
  memory_size= 1024  # OOM 予防: 最低 1 GB 推奨

  environment {
 variables = {
TRANSFORM_MODE = var.transform_mode  # "pii_mask" or "image_optimize"
 }
  }
}

# Object Lambda Access Point
resource "aws_s3control_object_lambda_access_point" "main" {
  name = "${var.project}-object-lambda-ap"

  configuration {
 supporting_access_point = aws_s3_access_point.supporting.arn

 transformation_configuration {
actions = ["GetObject"]

content_transformation {
  aws_lambda {
 function_arn = aws_lambda_function.object_lambda_transform.arn
  }
}
 }
  }
}

# Object Lambda Access Point ポリシー
resource "aws_s3control_object_lambda_access_point_policy" "main" {
  name = aws_s3control_object_lambda_access_point.main.name

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { AWS = var.allowed_role_arn }
Action = "s3-object-lambda:GetObject"
Resource  = aws_s3control_object_lambda_access_point.main.arn
 }]
  })
}

Lambda Memory の設定根拠: Object Lambda で画像変換や PII マスキングを行う場合、メモリ不足 (OOM) が最も頻出する障害だ。128 MB では PIL/Pillow による画像処理中に OOM Kill が発生する。1 GB 以上を設定することで、2 MB 以下の画像処理に余裕が生まれる。コスト計算上も、Lambda の実行時間が短縮されるため 1 GB 設定の方がトータルコストが低くなるケースが多い。

4-3. PIIデータマスキング実装

クレジットカード番号・社会保障番号・メールアドレスなどの PII (個人識別可能情報) を S3 から返却する前に動的マスキングする実装例を示す。GDPR / PCI-DSS 対応で有効なパターンだ。

import boto3
import re
import urllib.request
import json

s3_client = boto3.client("s3")

# PII マスキングパターン定義
PII_PATTERNS = [
 # クレジットカード (4桁-4桁-4桁-4桁)
 (r'\b(\d{4})[- ]?(\d{4})[- ]?(\d{4})[- ]?(\d{4})\b',
  lambda m: f"{m.group(1)}-****-****-{m.group(4)}"),
 # メールアドレス
 (r'\b([a-zA-Z0-9._%+\-]+)@([a-zA-Z0-9.\-]+\.[a-zA-Z]{2,})\b',
  lambda m: f"{'*' * min(len(m.group(1)), 3)}***@{m.group(2)}"),
 # 電話番号 (日本: 0X0-XXXX-XXXX)
 (r'\b(0\d{1,4})[- ]?(\d{2,4})[- ]?(\d{4})\b',
  lambda m: f"{m.group(1)}-****-{m.group(3)}"),
]


def mask_pii(text: str) -> str:
 for pattern, replacement in PII_PATTERNS:
  text = re.sub(pattern, replacement, text)
 return text


def lambda_handler(event, context):
 object_context = event["getObjectContext"]
 request_route = object_context["outputRoute"]
 request_token = object_context["outputToken"]
 s3_url = object_context["inputS3Url"]

 # Supporting Access Point 経由で原本取得
 with urllib.request.urlopen(s3_url) as response:
  original_content = response.read().decode("utf-8")

 # PII マスキング適用
 masked_content = mask_pii(original_content)

 # 変換済みデータを WriteGetObjectResponse で返却
 s3_client.write_get_object_response(
  Body=masked_content.encode("utf-8"),
  RequestRoute=request_route,
  RequestToken=request_token,
  ContentType="text/plain",
  StatusCode=200,
 )

 return {"status_code": 200}

WriteGetObjectResponse の必須フィールド:
RequestRoute / RequestToken: イベントの getObjectContext から取得するルーティングトークン。これが欠けると S3 側でタイムアウトが発生する
Body: 変換済みオブジェクトのバイナリ/文字列
StatusCode: 成功時は 200。Lambda が例外でクラッシュした場合、クライアントへは 500 が返る

PIIマスキング 実装注意点

  • 文字エンコード: UTF-8 以外のファイル (Shift-JIS 等) は decode 失敗に注意。Content-Type ヘッダを確認してエンコードを動的取得する
  • バイナリファイル対応: CSV/TXT 以外のバイナリ (PDF/画像) は text decode 不可。ファイル種別を Content-Type で判定し、非対応ファイルは原本そのまま返却するフォールバックを実装する
  • 大容量ファイル: 10 MB 超のファイルは streaming で処理する。全データをメモリに展開すると OOM になる
  • 正規表現の誤検知: クレジットカードパターンは電話番号や ID と重複しやすい。本番前に十分なテストデータで誤検知率を測定する

4-4. 画像最適化パターン (WebP変換 / リサイズ)

CloudFront + S3 Object Lambda の組み合わせで、リクエストヘッダに応じた動的な画像最適化を実現できる。Accept: image/webp ヘッダを持つブラウザには WebP を返し、非対応ブラウザには JPEG を返す構成が代表的だ。

import boto3
import urllib.request
import io
from PIL import Image

s3_client = boto3.client("s3")

MAX_WIDTH = 1920
MAX_HEIGHT = 1080


def lambda_handler(event, context):
 object_context = event["getObjectContext"]
 request_route = object_context["outputRoute"]
 request_token = object_context["outputToken"]
 s3_url = object_context["inputS3Url"]

 # ユーザーリクエストヘッダから Accept と width パラメータ取得
 user_request = event.get("userRequest", {})
 headers = {k.lower(): v for k, v in user_request.get("headers", {}).items()}
 accept_header = headers.get("accept", "")
 query_params = user_request.get("url", "")

 # 出力フォーマット決定
 use_webp = "image/webp" in accept_header

 # 原本画像取得
 with urllib.request.urlopen(s3_url) as response:
  image_data = response.read()

 # Pillow で画像変換
 img = Image.open(io.BytesIO(image_data))

 # EXIF 情報を考慮した自動回転
 img = correct_orientation(img)

 # リサイズ (アスペクト比維持)
 img.thumbnail((MAX_WIDTH, MAX_HEIGHT), Image.LANCZOS)

 # フォーマット変換
 output_buffer = io.BytesIO()
 if use_webp:
  img.save(output_buffer, format="WEBP", quality=85, method=6)
  content_type = "image/webp"
 else:
  img.save(output_buffer, format="JPEG", quality=85, optimize=True)
  content_type = "image/jpeg"

 output_buffer.seek(0)

 s3_client.write_get_object_response(
  Body=output_buffer.read(),
  RequestRoute=request_route,
  RequestToken=request_token,
  ContentType=content_type,
  StatusCode=200,
  CacheControl="public, max-age=86400",  # CDN キャッシュ: 1日
 )

 return {"status_code": 200}


def correct_orientation(img: Image.Image) -> Image.Image:
 try:
  exif = img._getexif()
  if exif:
orientation = exif.get(274)  # EXIF Orientation tag
rotations = {3: 180, 6: 270, 8: 90}
if orientation in rotations:
 img = img.rotate(rotations[orientation], expand=True)
 except Exception:
  pass
 return img

CloudFront との統合: CloudFront の Origin に Object Lambda Access Point の URL を設定することで、CDN キャッシュと動的変換を組み合わせられる。CacheControl ヘッダを WriteGetObjectResponse で明示的に設定することで、CloudFront が変換済みオブジェクトをキャッシュする期間を制御できる。同一のオブジェクトへの 2 回目以降のリクエストは CloudFront キャッシュから返されるため、Lambda の呼び出し回数と S3 Object Lambda リクエスト料金を削減できる。

4-5. コスト設計

S3 Object Lambda は Lambda 実行コストS3 Object Lambda リクエスト料金 の 2 種類が発生する。従来の S3 GET 料金だけでなくこれらを加算した月額を見積もる必要がある。

コスト項目単価 (ap-northeast-1)備考
S3 Object Lambda GET$0.0040 / 1,000 リクエスト通常 S3 GET の 8倍
Lambda 実行 (1 GB・100 ms)$0.0000016667 / 回メモリ×時間課金
Lambda Provisioned Concurrency$0.0000041667 / GB-秒常時確保コスト
S3 Supporting AP GET (内部)$0.0000004 / リクエストほぼ無視できる水準

月額コスト試算 (例: 100万リクエスト / 月、画像変換、Lambda 1 GB × 500 ms):

S3 Object Lambda リクエスト: 1,000,000 × $0.004 / 1,000 = $4.00
Lambda 実行: 1,000,000 × 0.5秒 × 1GB × $0.0000166667 / GB-秒 = $8.33
-----------------------------------------------------------------------
合計: 約 $12.33 / 月

vs. EC2 変換サーバー比較: 同等の変換処理を t3.small EC2 (1 vCPU / 2 GB) で行う場合、インスタンス費用だけで $15 ~ 20 / 月となる。スパイク対応には Auto Scaling 設定も必要だ。Object Lambda は従量課金かつ自動スケールするため、リクエスト数が不安定な用途では経済的な選択肢になる。

Object Lambda 用途選定マトリクス

  • PIIマスキング: クレジットカード/個人情報の動的マスキング / GDPR・PCI-DSS 対応
  • 画像最適化: WebP 変換 / リサイズ / 圧縮 / CloudFront 統合
  • フォーマット変換: CSV→JSON / Parquet→JSON / XML→JSON
  • 動的 Watermark: ユーザー毎の透かし追加 / Audit Trail 埋め込み
  • 採用しない方がよいケース: 全オブジェクトへの一律変換 (Lambda 前処理で S3 に保存済みのオブジェクトを作る方が安い) / 10 MB 超の大容量ファイルの高頻度変換 (Lambda タイムアウト・コスト)

4-6. キャッシュ戦略 — CloudFront + Object Lambda

[CloudFront Distribution]
  ↓ Cache Miss 時のみ
[Object Lambda Access Point]  ─→  [Lambda] ─→ [S3]
  ↑
[Edge Cache (Hit 率 80%+ を目標)]

CloudFront Origin 設定:

resource "aws_cloudfront_distribution" "with_object_lambda" {
  origin {
 domain_name = "${aws_s3control_object_lambda_access_point.main.alias[0].value}.s3-object-lambda.ap-northeast-1.amazonaws.com"
 origin_id= "object-lambda-origin"

 s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.main.cloudfront_access_identity_path
 }

 custom_header {
name  = "X-Custom-Auth"
value = var.origin_auth_token
 }
  }

  default_cache_behavior {
 allowed_methods  = ["GET", "HEAD"]
 cached_methods= ["GET", "HEAD"]
 target_origin_id = "object-lambda-origin"
 viewer_protocol_policy = "redirect-to-https"

 cache_policy_id = aws_cloudfront_cache_policy.image_cache.id

 # WebP 対応: Accept ヘッダをキャッシュキーに含める
 origin_request_policy_id = aws_cloudfront_origin_request_policy.forward_accept.id
  }
}

# Accept ヘッダをオリジンに転送 (WebP 判定に必要)
resource "aws_cloudfront_origin_request_policy" "forward_accept" {
  name = "${var.project}-forward-accept-header"

  headers_config {
 header_behavior = "whitelist"
 headers {
items = ["Accept"]
 }
  }

  cookies_config { cookie_behavior = "none" }
  query_strings_config { query_string_behavior = "none" }
}

キャッシュキーへの Accept ヘッダ追加が必須: WebP 対応ブラウザと非対応ブラウザが同じキャッシュキーを共有すると、WebP をサポートしないブラウザに WebP 画像が返されてしまう。Accept ヘッダをキャッシュキーに含めることで、変換フォーマット別にキャッシュを分離する。

4-7. 制限事項と本番運用の落とし穴

制限項目内容対応策
PUT/DELETE 非対応Object Lambda は GET のみ対応書き込みは通常の S3 PUT を使用
Lambda タイムアウト最大 60 秒 (S3 Object Lambda 固有の上限)大容量ファイルは Stream 処理
レスポンスサイズ1 object あたり最大 40 GB分割ダウンロードは Range リクエストで対応
Range リクエストサポートあり (Lambda 側で Range ヘッダを処理)userRequest.headers から Range を取得し部分返却
Supporting AP 削除Object Lambda AP が存在する間は削除不可Terraform destroy の順序に注意
VPC アクセスSupporting AP に VPC 設定があれば VPC 内からのみPublic アクセスは Supporting AP の Public Access Block を解除

Lambda タイムアウトの罠: S3 Object Lambda に固有の 60 秒制限は、Lambda 関数の一般設定 (timeout) とは別に適用される。Lambda 関数を timeout = 300 に設定しても、Object Lambda 経由のリクエストは 60 秒でタイムアウトする。巨大ファイルのリアルタイム変換には向かないため、事前変換 + キャッシュ保存のハイブリッド構成を検討する。

Object Lambda 本番運用 落とし穴3選

  • 落とし穴1: Supporting Access Point の Public Access Block
    Object Lambda Access Point から Supporting AP にアクセスするとき、Supporting AP の Public Access Block がオンになっていると 403 Access Denied が発生する。Supporting AP 自体は VPC 設定または適切な IAM ポリシーで保護し、Public Access Block は緩和する必要がある場合がある。Terraform で aws_s3_access_pointpublic_access_block_configuration を確認すること
  • 落とし穴2: Lambda の IAM Role に s3-object-lambda:WriteGetObjectResponse が必要
    通常の s3:PutObject ではなく専用の s3-object-lambda:WriteGetObjectResponse アクションが必要。これが欠けると Lambda が正常終了してもクライアントには 500 が返る。CloudWatch Logs には Lambda 側のエラーが出ないため原因特定が難しい
  • 落とし穴3: Provisioned Concurrency なしでの Cold Start
    初回アクセス時の Lambda Cold Start (Python で 1〜3 秒) が S3 Object Lambda タイムアウトに積み上がる。Provisioned Concurrency を最低 2 に設定することで Critical Path での Cold Start を予防する。Cost Explorer で Lambda PC 費用を月次で確認し、アクセスが少ない時間帯はスケジュール縮小を検討する

5. S3 Express One Zone + Cross-Region Replication本番運用 — Single-AZ高性能 × CRR × RTC × KMS連携

S3 Express One Zone + CRR (RTC) 本番運用構成

S3 Express One Zone と Cross-Region Replication (CRR) は、「高速一時処理」と「リージョン間冗長化」という対照的な要件を担う2本柱だ。Express One Zone は ML/Analytics ワークロードのレイテンシボトルネックを解消し、CRR は規制対応・DR・Multi-Region Active アーキテクチャを支える。本セクションでは両機能の内部構造から Terraform 実装、KMS 連携、Failover 設計まで体系的に解説する。

S3 Express One Zone + CRR 本番運用5原則

  • 原則1: Express One Zone は一時データ専用 — 永続化要件のあるデータは Standard + CRR に格納
  • 原則2: CRR SLA が15分以内なら Replication Time Control (RTC) は必須 (別料金 $0.015/GB)
  • 原則3: KMS Customer Managed Key + CRR は Replica側 Region で別 Key 作成が必須 (Cross-Region 暗号化)
  • 原則4: ReplicationLatency CloudWatch Alarm を15分閾値で設定し、超過時はオンコールアラート連携
  • 原則5: ExistingObjectReplication を有効化して過去 Object も Replication 対象に含める

5-1. S3 Express One Zone アーキテクチャ

S3 Express One Zone は 2023年11月に GA となった Directory Bucket 型ストレージサービスだ。通常の S3 バケット (General Purpose Bucket) が複数 AZ に分散して耐久性を確保するのに対し、Express One Zone は 単一 AZ 内の専用ハードウェア にデータを格納し、リクエストオーバーヘッドを大幅に削減する。

内部アーキテクチャの特徴:

通常の S3 API コールは、リクエストルーティング → S3 コントロールプレーン認証 → データノードという経路をたどる。Express One Zone はこの経路を短縮し、CreateSession API で取得した一時トークンを使って専用エンドポイントへ直接アクセスする。セッションは最大5分間有効で、その間は認証オーバーヘッドなしに大量 PUT/GET が実行できる。

パフォーマンス数値:

指標S3 StandardS3 Express One Zone
PUT レイテンシ (p50)~11ms~1ms
GET レイテンシ (p50)~11ms~1ms
スループット向上基準最大10倍
単価 (us-east-1)$0.023/GB~$0.016/GB (約30%低)
取得料金なしなし

バケット命名規則 (通常バケットと異なる):

Express One Zone バケット名は {バケット名}--{AZ-ID}--x-s3 形式が必須だ。AZ-ID は use1-az5 のような形式で、AZ 名 (us-east-1a) とは異なるため注意が必要。

# AZ-ID 確認
aws ec2 describe-availability-zones \
  --region us-east-1 \
  --query 'AvailabilityZones[*].{Name:ZoneName,Id:ZoneId}' \
  --output table
# 例: us-east-1a → use1-az1, us-east-1b → use1-az2

5-2. Express One Zone ユースケースと適用判断

適用すべきユースケース:

  1. ML/Analytics 一時データ: SageMaker Training Job や EMR Spark ジョブの中間ファイル。処理中のみ存在し、結果は S3 Standard に書き出す設計。
  2. Apache Spark Shuffle データ: Shuffle Write → Shuffle Read のレイテンシが Express One Zone により 40〜60% 削減される実測値が報告されている。
  3. Log 一時保管: 大量ログを Express One Zone に集約し、集計後に S3 Standard-IA へ移動する2段構成。
  4. CDN オリジンキャッシュバッファ: CloudFront オリジン手前のバッファとして活用し、オリジンレスポンスを改善。

適用すべきでないユースケース:

  • 永続保存が必要なユーザーデータ (単一AZ障害でデータ消失)
  • Versioning が必要なケース (Express One Zone は Versioning 非対応)
  • Cross-Region アクセスが発生するケース (同一 AZ の EC2/ECS からのアクセスが前提)
Express One Zone vs S3 Standard vs EFS 三択比較

  • S3 Express One Zone: レイテンシ=~1ms / 可用性=Single-AZ / 用途=一時高速処理 / Versioning=× / 価格=~$0.016/GB
  • S3 Standard: レイテンシ=~11ms / 可用性=Multi-AZ (99.999999999%) / 用途=永続オブジェクト / Versioning=○ / 価格=$0.023/GB
  • Amazon EFS (Standard): レイテンシ=~1ms (POSIX) / 可用性=Multi-AZ / 用途=共有ファイルシステム / POSIX API=○ / 価格=$0.30/GB
  • 選定基準: POSIX必要→EFS / 低コスト永続→S3 Standard / 超低レイテンシ一時→Express One Zone

5-3. Express One Zone Terraform 実装

Express One Zone バケットは aws_s3_directory_bucket リソースで作成する。通常の aws_s3_bucket とは異なるリソースタイプである点に注意が必要だ。

# Express One Zone Directory Bucket
resource "aws_s3_directory_bucket" "ml_temp" {
  bucket = "ml-processing-temp--${var.az_id}--x-s3"

  location {
 name = var.az_id  # 例: "use1-az5"
 type = "AvailabilityZone"
  }

  # Express One Zone はデフォルトで SSE-S3 暗号化
  # SSE-KMS は現時点で非対応 (2024年時点)
}

# ローカル変数でバケット名を管理
locals {
  express_bucket_name = aws_s3_directory_bucket.ml_temp.bucket
  express_bucket_arn  = "arn:aws:s3express:${var.region}:${data.aws_caller_identity.current.account_id}:bucket/${local.express_bucket_name}"
}

# アクセスポリシー: 同一 AZ の EC2/ECS Task のみ許可
resource "aws_s3_bucket_policy" "ml_temp_policy" {
  bucket = aws_s3_directory_bucket.ml_temp.bucket

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Sid = "AllowVPCAccess"
  Effect = "Allow"
  Principal = {
 AWS = aws_iam_role.ml_task_role.arn
  }
  Action = [
 "s3express:CreateSession"
  ]
  Resource = local.express_bucket_arn
  Condition = {
 StringEquals = {
"aws:SourceVpc" = var.vpc_id
 }
  }
}
 ]
  })
}

# ECS Task 用 IAM Role
resource "aws_iam_role" "ml_task_role" {
  name = "ml-processing-task-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "ml_task_express_policy" {
  name = "express-one-zone-access"
  role = aws_iam_role.ml_task_role.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["s3express:CreateSession"]
  Resource = local.express_bucket_arn
},
{
  Effect = "Allow"
  Action = [
 "s3express:PutObject",
 "s3express:GetObject",
 "s3express:DeleteObject",
 "s3express:ListBucket"
  ]
  Resource = [
 local.express_bucket_arn,
 "${local.express_bucket_arn}/*"
  ]
}
 ]
  })
}

CreateSession を使った Python SDK 実装例:

import boto3

# Express One Zone 専用クライアント
s3_express = boto3.client(
 's3',
 endpoint_url=f'https://s3express-{az_id}.{region}.amazonaws.com'
)

# セッション取得 (最大5分有効)
session = s3_express.create_session(
 Bucket=f'ml-processing-temp--{az_id}--x-s3',
 SessionMode='ReadWrite'
)

# 以降のリクエストにセッションクレデンシャルを使用
credentials = session['Credentials']
Express One Zone 導入チェックリスト

  • □ バケット名が {name}--{AZ-ID}--x-s3 形式になっている
  • □ アプリケーション (EC2/ECS) が同一 AZ に配置されている
  • □ CreateSession API を使った認証フローが実装されている
  • □ 一時データのみ格納 — 永続化が必要なデータは S3 Standard に分離
  • □ AZ 障害時のフォールバック先 (S3 Standard) が設計されている
  • □ バケットポリシーで VPC 外からのアクセスをブロックしている
  • □ Versioning が必要な要件がないことを確認済み

5-4. Cross-Region Replication 本番設計

CRR は ソース Bucket のオブジェクトをリアルタイムで別 Region の Destination Bucket にコピーするサービスだ。Versioning が両 Bucket で有効であることが前提条件となる。

Replication Configuration の構成要素:

要素必須説明
Rule IDルール識別子 (重複不可)
StatusEnabled / Disabled
Filterprefix / tag / AND 条件 / 省略で全オブジェクト対象
Priority複数ルール時の優先度 (大きい数値が優先)
Destination Bucket ARNReplica 先 Bucket の ARN
IAM RoleS3 が Assume するレプリケーション用 IAM Role
DeleteMarkerReplication削除マーカーを Replica に反映するか
ExistingObjectReplicationルール作成前の既存 Object もレプリケート
RTC15分SLA保証の Replication Time Control

Terraform による CRR 実装:

# Source Bucket (ap-northeast-1)
resource "aws_s3_bucket" "source" {
  provider = aws.tokyo
  bucket= "app-data-source-${var.account_id}"
}

resource "aws_s3_bucket_versioning" "source" {
  provider = aws.tokyo
  bucket= aws_s3_bucket.source.id
  versioning_configuration {
 status = "Enabled"
  }
}

# Destination Bucket (us-east-1)
resource "aws_s3_bucket" "replica" {
  provider = aws.virginia
  bucket= "app-data-replica-${var.account_id}"
}

resource "aws_s3_bucket_versioning" "replica" {
  provider = aws.virginia
  bucket= aws_s3_bucket.replica.id
  versioning_configuration {
 status = "Enabled"
  }
}

# CRR IAM Role
resource "aws_iam_role" "crr_role" {
  provider = aws.tokyo
  name  = "s3-crr-replication-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "s3.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy" "crr_policy" {
  provider = aws.tokyo
  role  = aws_iam_role.crr_role.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["s3:GetReplicationConfiguration", "s3:ListBucket"]
  Resource = aws_s3_bucket.source.arn
},
{
  Effect= "Allow"
  Action= ["s3:GetObjectVersionForReplication", "s3:GetObjectVersionAcl",
  "s3:GetObjectVersionTagging"]
  Resource = "${aws_s3_bucket.source.arn}/*"
},
{
  Effect= "Allow"
  Action= ["s3:ReplicateObject", "s3:ReplicateDelete", "s3:ReplicateTags"]
  Resource = "${aws_s3_bucket.replica.arn}/*"
}
 ]
  })
}

# CRR Replication Configuration
resource "aws_s3_bucket_replication_configuration" "crr" {
  provider = aws.tokyo
  role  = aws_iam_role.crr_role.arn
  bucket= aws_s3_bucket.source.id

  depends_on = [aws_s3_bucket_versioning.source]

  rule {
 id  = "replicate-all-objects"
 status = "Enabled"

 # DeleteMarker も Replica に反映
 delete_marker_replication {
status = "Enabled"
 }

 # 既存 Object もレプリケート
 existing_object_replication {
status = "Enabled"
 }

 destination {
bucket  = aws_s3_bucket.replica.arn
storage_class = "STANDARD"

# RTC 有効化 (15分SLA)
replication_time {
  status = "Enabled"
  time {
 minutes = 15
  }
}

# RTC Metrics 有効化 (CloudWatch連携)
metrics {
  status = "Enabled"
  event_threshold {
 minutes = 15
  }
}
 }
  }
}

Filter 活用パターン (用途別レプリケーション制御):

rule {
  id  = "replicate-critical-prefix"
  status = "Enabled"
  priority = 10

  # prefix + tag の複合フィルタ
  filter {
 and {
prefix = "critical/"
tags = {
  replication = "required"
  tier  = "production"
}
 }
  }

  # ... destination 設定
}

rule {
  id  = "exclude-temp-prefix"
  status = "Enabled"
  priority = 5

  filter {
 prefix = "temp/"
  }

  # temp/ は Replica しない (Destination なし = 実質スキップ)
  delete_marker_replication {
 status = "Disabled"
  }
  # ... destination 設定で storage_class = "GLACIER" など
}

5-5. CRR Replication Time Control (RTC) 設定

RTC の仕組み:

通常の CRR は SLA を保証しないが、RTC を有効にすると99.99%のオブジェクトを15分以内にレプリケートすることを AWS が SLA として保証する。対象オブジェクトサイズは 256MB 以下。

料金:

  • RTC 有効化: $0.015/GB (転送されるデータ量に対して課金)
  • Replication Metrics (CloudWatch): 追加費用なし
  • 通常 CRR データ転送料: 別途発生

CloudWatch RTC Metrics 一覧:

メトリクス名説明推奨アラーム閾値
ReplicationLatency最古のオブジェクトの待機時間 (秒)900秒 (15分) 超
BytesPendingReplicationReplica 待ちデータ量 (Bytes)用途に依存
OperationsPendingReplicationReplica 待ち件数10000件超
OperationsFailedReplication失敗したレプリケーション件数1件超でアラート
# CloudWatch Alarm: ReplicationLatency
resource "aws_cloudwatch_metric_alarm" "crr_latency" {
  alarm_name = "s3-crr-replication-latency-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name= "ReplicationLatency"
  namespace  = "AWS/S3"
  period  = 300
  statistic  = "Maximum"
  threshold  = 900  # 15分

  dimensions = {
 SourceBucket= aws_s3_bucket.source.id
 DestinationBucket = aws_s3_bucket.replica.id
 RuleId= "replicate-all-objects"
  }

  alarm_description = "CRR Replication Latency exceeded 15 minutes (RTC SLA boundary)"
  alarm_actions  = [aws_sns_topic.oncall_alerts.arn]
}

# CloudWatch Alarm: FailedReplication
resource "aws_cloudwatch_metric_alarm" "crr_failures" {
  alarm_name = "s3-crr-replication-failures"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name= "OperationsFailedReplication"
  namespace  = "AWS/S3"
  period  = 60
  statistic  = "Sum"
  threshold  = 0

  dimensions = {
 SourceBucket= aws_s3_bucket.source.id
 DestinationBucket = aws_s3_bucket.replica.id
 RuleId= "replicate-all-objects"
  }

  alarm_description = "CRR Replication failures detected"
  alarm_actions  = [aws_sns_topic.oncall_alerts.arn]
}

5-6. KMS暗号化 + CRR 連携

CRR で KMS 暗号化オブジェクトをレプリケートする場合、Source Region と Destination Region で別々の KMS Key が必要になる。KMS Key は Regional サービスのため、Cross-Region での直接参照ができないためだ。

必要な設定要素:

  1. Source Bucket: aws/s3 マネージドキー or Customer Managed Key (CMK) で暗号化
  2. Destination Bucket: Destination Region で作成した CMK を指定
  3. CRR IAM Role: Source CMK の Decrypt 権限 + Destination CMK の GenerateDataKey/Encrypt 権限
# Source KMS Key (ap-northeast-1)
resource "aws_kms_key" "source_key" {
  provider = aws.tokyo
  description = "CRR Source Bucket Encryption Key"
  deletion_window_in_days = 30
  enable_key_rotation  = true

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Sid = "Enable IAM User Permissions"
  Effect = "Allow"
  Principal = { AWS = "arn:aws:iam::${var.account_id}:root" }
  Action = "kms:*"
  Resource  = "*"
},
{
  Sid = "Allow CRR Role to use the key"
  Effect = "Allow"
  Principal = { AWS = aws_iam_role.crr_role.arn }
  Action = [
 "kms:Decrypt",
 "kms:DescribeKey"
  ]
  Resource = "*"
}
 ]
  })
}

# Destination KMS Key (us-east-1)
resource "aws_kms_key" "replica_key" {
  provider = aws.virginia
  description = "CRR Replica Bucket Encryption Key"
  deletion_window_in_days = 30
  enable_key_rotation  = true

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Sid = "Enable IAM User Permissions"
  Effect = "Allow"
  Principal = { AWS = "arn:aws:iam::${var.account_id}:root" }
  Action = "kms:*"
  Resource  = "*"
},
{
  Sid = "Allow CRR Role to encrypt replicated objects"
  Effect = "Allow"
  Principal = { AWS = aws_iam_role.crr_role.arn }
  Action = [
 "kms:GenerateDataKey",
 "kms:Encrypt",
 "kms:DescribeKey"
  ]
  Resource = "*"
}
 ]
  })
}

# IAM Policy 更新: KMS Cross-Region 操作を追加
resource "aws_iam_role_policy" "crr_kms_policy" {
  provider = aws.tokyo
  role  = aws_iam_role.crr_role.id
  name  = "crr-kms-access"

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["kms:Decrypt", "kms:DescribeKey"]
  Resource = aws_kms_key.source_key.arn
},
{
  Effect = "Allow"
  Action = [
 "kms:GenerateDataKey",
 "kms:Encrypt",
 "kms:DescribeKey"
  ]
  Resource = aws_kms_key.replica_key.arn
}
 ]
  })
}

# Replication Configuration with KMS
resource "aws_s3_bucket_replication_configuration" "crr_with_kms" {
  provider = aws.tokyo
  role  = aws_iam_role.crr_role.arn
  bucket= aws_s3_bucket.source.id

  rule {
 id  = "replicate-with-kms"
 status = "Enabled"

 source_selection_criteria {
sse_kms_encrypted_objects {
  status = "Enabled"
}
 }

 destination {
bucket  = aws_s3_bucket.replica.arn
storage_class = "STANDARD"

encryption_configuration {
  replica_kms_key_id = aws_kms_key.replica_key.arn
}

replication_time {
  status = "Enabled"
  time { minutes = 15 }
}

metrics {
  status = "Enabled"
  event_threshold { minutes = 15 }
}
 }
  }
}
KMS + CRR 設計5原則

  • 原則1: Source と Destination で KMS Key を必ず分離 — 同一 Key の Cross-Region 参照は不可
  • 原則2: CMK の Key Rotation は両 Region で有効化 (enable_key_rotation = true)
  • 原則3: CRR IAM Role に付与する KMS 権限は最小化 — Source=Decrypt のみ / Destination=GenerateDataKey+Encrypt のみ
  • 原則4: Key Policy の Principal に CRR Role ARN を明示 — AWS アカウントのルート委任だけでは不十分
  • 原則5: source_selection_criteria の sse_kms_encrypted_objects を “Enabled” に設定しないと暗号化 Object がスキップされる

5-7. CRR DR 設計パターン

CRR を活用した DR 設計には主に3パターンがある。RTO/RPO 要件と運用コストのトレードオフで選択する。

パターン比較:

パターンRTORPOコスト複雑度
Active-Passive (Cold)30〜60分CRR遅延 (通常~秒)
Active-Passive (Warm)5〜15分CRR遅延+RTC (15分)
Active-Active~1分ほぼゼロ

Active-Passive (Warm) パターン — 推奨構成:

[Source Region: ap-northeast-1]
  App Tier (ECS/EKS)
  ↓ PUT/GET
  S3 Source Bucket (Versioning ON, SSE-KMS)
  ↓ CRR + RTC (15分SLA)

[Destination Region: us-east-1]
  S3 Replica Bucket (Versioning ON, SSE-KMS)
  ↑ Failover 時: Route53 DNS 切替
  Standby App Tier (スケール0 → Failover 時に起動)

Active-Active パターン — Bidirectional CRR:

Active-Active 構成では Source ↔ Destination の双方向レプリケーションを設定する。この場合、レプリケーションループを防ぐ Replication Rule Filter が必須だ。

# Region A → Region B レプリケーション
resource "aws_s3_bucket_replication_configuration" "a_to_b" {
  provider = aws.region_a
  bucket= aws_s3_bucket.bucket_a.id
  role  = aws_iam_role.crr_role_a.arn

  rule {
 id  = "a-to-b"
 status = "Enabled"

 # Region B からレプリケートされた Object を再レプリケートしない
 # aws:s3:replication-status が REPLICA の Object を除外
 filter {
and {
  prefix = ""
  tags= {}
}
 }

 destination {
bucket = aws_s3_bucket.bucket_b.arn
# ... RTC + KMS 設定
 }
  }
}

Failover 手順 (Active-Passive Warm):

#!/bin/bash
# DR Failover Script: Source Region 障害時の Destination 昇格

DEST_BUCKET="app-data-replica-${ACCOUNT_ID}"
DEST_REGION="us-east-1"

# 1. Replica Bucket のレプリケーション設定を無効化 (書込み受付開始)
aws s3api put-bucket-replication \
  --bucket "$DEST_BUCKET" \
  --replication-configuration '{"Role":"","Rules":[]}' \
  --region "$DEST_REGION"

# 2. アプリケーション用 IAM Role に Destination Bucket への書込み権限付与
aws iam attach-role-policy \
  --role-name "app-task-role" \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/replica-bucket-readwrite"

# 3. Route53 フェイルオーバールーティングで Destination Region に切替
aws route53 change-resource-record-sets \
  --hosted-zone-id "$HOSTED_ZONE_ID" \
  --change-batch file://failover-dns-change.json

# 4. Destination Region のアプリ Tier をスケールアウト
aws ecs update-service \
  --cluster "app-cluster-${DEST_REGION}" \
  --service "app-service" \
  --desired-count 3 \
  --region "$DEST_REGION"

echo "Failover to ${DEST_REGION} completed."
CRR DR設計 失敗パターンと対策

  • 失敗1: Replica Bucket に Write しようとして “Read-Only” エラー — CRR が有効な Bucket はデフォルトで書込み保護される。Failover 時は必ず CRR 設定を無効化してから書き込む。
  • 失敗2: KMS Key ローテーション後に CRR が失敗 — Key Rotation はバックエンドで新旧キーを管理するため CRR は継続動作する。ただし Key を手動削除すると暗号化 Object の Decrypt が不可能になる。削除前に全 Object の移行を完了させること。
  • 失敗3: DeleteMarkerReplication = Disabled で削除が Replica に反映されない — ソース側の意図的な削除も Replica に反映したい場合は Enabled に設定する。誤削除の伝播リスクがある場合は MFA Delete と組み合わせる。
  • 失敗4: ExistingObjectReplication を設定せず過去 Object が欠損 — CRR 設定前のオブジェクトはデフォルトでレプリケートされない。Batch Replication ジョブを別途実行するか ExistingObjectReplication = Enabled を設定する。

5-8. 運用監視ダッシュボード設計

本番環境では CRR の健全性を継続的に観測する必要がある。S3 Storage Lens と CloudWatch を組み合わせた監視構成を推奨する。

必須監視メトリクス一覧:

監視項目ソースアラーム条件
ReplicationLatencyCloudWatch S3> 900秒 (15分)
OperationsFailedReplicationCloudWatch S3> 0件/5分
BytesPendingReplicationCloudWatch S3容量設計に依存
Bucket Size (Source)S3 Storage Lens異常増加 (前日比200%超)
Bucket Size (Replica)S3 Storage LensSource との乖離 > 10%
KMS API Error RateCloudWatch KMSDecryptError > 0件/分
# CloudWatch Dashboard for CRR Monitoring
resource "aws_cloudwatch_dashboard" "crr_monitoring" {
  dashboard_name = "s3-crr-replication-health"

  dashboard_body = jsonencode({
 widgets = [
{
  type= "metric"
  width  = 12
  height = 6
  properties = {
 title  = "CRR Replication Latency (Max)"
 period = 60
 stat= "Maximum"
 metrics = [
["AWS/S3", "ReplicationLatency",
  "SourceBucket", aws_s3_bucket.source.id,
  "DestinationBucket", aws_s3_bucket.replica.id,
  "RuleId", "replicate-all-objects"]
 ]
 annotations = {
horizontal = [{
  value = 900
  label = "RTC SLA Boundary (15min)"
  color = "#ff0000"
}]
 }
  }
},
{
  type= "metric"
  width  = 12
  height = 6
  properties = {
 title  = "CRR Failed Operations"
 period = 60
 stat= "Sum"
 metrics = [
["AWS/S3", "OperationsFailedReplication",
  "SourceBucket", aws_s3_bucket.source.id,
  "DestinationBucket", aws_s3_bucket.replica.id,
  "RuleId", "replicate-all-objects"]
 ]
  }
}
 ]
  })
}

以上が §5 S3 Express One Zone + CRR 本番運用の全実装詳細だ。Express One Zone の Single-AZ 高速処理と CRR の Cross-Region 耐久性を適切に組み合わせることで、パフォーマンスと可用性の両軸で要件を満たすストレージ設計が実現できる。

6. 詰まりポイント7選 — S3 Advanced本番運用の地雷とフィックス

S3 Advanced本番運用で実際に現場エンジニアが引っかかる「なぜ詰まるか → どう解くか」を2段構成で解説する。


詰まり1: Lifecycle Transition/Expirationルール競合(同一prefix/複数ルール)

なぜ詰まるか

Lifecycle Ruleを複数設定した際、同一prefixに対してTransitionとExpirationが重複指定されると、どちらが優先されるか直感的に分からず動作が予測不能になる。特に「30日でGlacier移行 → 90日で削除」のつもりが「30日経過前に削除ルールが発火する」という事態が頻発する。Rule間にはPriority設定が必要だが、Console上では非表示になっているケースもある。

どう解くか

まずS3 API GetBucketLifecycleConfiguration でRule全体をJSON出力し、prefix/tag Filterが重複していないか確認する。TransitionとExpirationを同一ルール内にまとめ、Rule数を20本以下に正規化する。Terraformでは aws_s3_bucket_lifecycle_configuration resource内で rule ブロックを明示的に整理し、GitでRule変更を追跡できるようにする。

# Rule一覧確認
aws s3api get-bucket-lifecycle-configuration \
  --bucket your-bucket-name \
  --query 'Rules[*].{ID:ID,Filter:Filter,Status:Status}' \
  --output table
Lifecycle Rule競合チェックリスト

  • 同一prefix/tagに複数Ruleが存在しないか確認 (GetBucketLifecycleConfiguration)
  • Transition → Expiration の日数順序が論理的に正しいか確認
  • Versioning有効Bucketでは Current/Noncurrent を分けてRule設定
  • Rule数 20本超なら prefix+tag複合Filter でRule統合を検討

詰まり2: Intelligent-Tiering 128KB未満オブジェクットで監視コスト赤字

なぜ詰まるか

Intelligent-Tieringは「アクセスパターンで自動階層化するから全Bucketに適用すれば最適」と思いがちだが、128KB未満のオブジェクットには監視コスト($0.0025/1,000オブジェクット/月)がかかる一方、階層間の移動による削減額がほぼゼロとなり、コストが純増する。小ファイル中心のBucket(ログファイル、JSONレスポンスキャッシュ等)で適用すると月数万円の余分なコストが発生する。

どう解くか

適用前にS3 Storage Lensで対象Bucketのオブジェクットサイズ分布を確認する。128KB以上のオブジェクットが多数を占めるBucketのみに限定適用する。

# Storage Lens でオブジェクットサイズ分布確認 (Athena経由)
SELECT
  bucket_name,
  COUNT(*) as object_count,
  AVG(object_size) as avg_size_bytes,
  SUM(CASE WHEN object_size < 131072 THEN 1 ELSE 0 END) as small_objects
FROM storage_lens_metrics
WHERE record_type = 'BUCKET'
GROUP BY bucket_name
ORDER BY small_objects DESC;
Intelligent-Tiering 適用判定フロー

  • Step1: Storage Lens でオブジェクット数/サイズ分布を確認
  • Step2: 128KB未満オブジェクットが全体の 50%超 → Intelligent-Tiering 非適用
  • Step3: アクセスパターンが不規則 (30日以上非アクセスが発生) → 適用候補
  • Step4: Archive Instant Tierまで自動移行させる場合 → OptionalTier有効化を明示的に設定

詰まり3: Object Lambda 課金二重(GET + Lambda実行コスト見落とし)

なぜ詰まるか

S3 Object Lambdaを使うと、「S3 GETリクエスト料金」に加えて「Lambda実行料金(実行時間 × メモリ)」「Object Lambda API呼び出し料金」が別途発生する。既存のS3コスト見積もりにLambda実行コストを含めていないため、月次請求で予想外の増額が発覚するパターンが多い。さらにLambda実行時間が長い変換処理(画像リサイズ・PII置換等)ではコストが数十倍に跳ね上がることがある。

どう解くか

Cost Explorerでサービス別・タグ別にObject Lambda分のコストを追跡する。Lambda関数にはリソースタグ CostCenter=object-lambda を付与し、月次予算アラートを設定する。変換処理にはキャッシュ戦略(CloudFrontキャッシュ or S3バージョニングキャッシュ)を組み合わせ、同一オブジェクットへの重複Lambda実行を削減する。

resource "aws_lambda_function" "object_lambda_transform" {
  function_name = "s3-object-lambda-transform"
  memory_size= 1024
  timeout = 30

  tags = {
 CostCenter = "object-lambda"
 Environment = "production"
  }
}

resource "aws_budgets_budget" "object_lambda_monthly" {
  name= "object-lambda-monthly-budget"
  budget_type  = "COST"
  limit_amount = "100"
  limit_unit= "USD"
  time_unit = "MONTHLY"

  cost_filter {
 name= "TagKeyValue"
 values = ["user:CostCenter$object-lambda"]
  }

  notification {
 comparison_operator = "GREATER_THAN"
 threshold  = 80
 threshold_type= "PERCENTAGE"
 notification_type= "ACTUAL"
 subscriber_email_addresses = ["infra-alert@example.com"]
  }
}

詰まり4: Express One Zone Single-AZ障害でデータ消失

なぜ詰まるか

S3 Express One Zoneは単一AZ内に配置され、通常S3(11 nines耐久性)とは異なり、AZ障害発生時にデータが消失するリスクがある。「S3だから耐久性は問題ない」という既存の認識でExpress One Zoneに永続化データを保存すると、AZ障害発生時にリカバリ不能になる。ML推論のキャッシュや一時中間データならリスク許容できるが、トランザクションデータや顧客データを保存するのは致命的な設計ミスとなる。

どう解くか

Express One Zoneは「高速アクセスが必要な一時データ・キャッシュ専用」として用途を明確に限定する。永続化要件があるデータはStandard S3に保存し、CRRで別Regionにレプリカを持つ構成にする。

# Express One Zone: キャッシュ専用
resource "aws_s3_bucket" "express_cache" {
  bucket = "my-express-cache--use1-az1--x-s3"

  tags = {
 DataClassification = "cache-only"
 Persistence  = "non-persistent"
  }
}

# Standard S3: 永続化データ本体
resource "aws_s3_bucket" "persistent_data" {
  bucket = "my-persistent-data-primary"

  tags = {
 DataClassification = "persistent"
 CRR = "enabled"
  }
}
Express One Zone 利用可否判定

  • ✅ 利用可: MLモデル推論キャッシュ / 中間処理データ / セッションキャッシュ / 一時ファイル
  • ❌ 利用不可: 顧客トランザクションデータ / DBバックアップ / 監査ログ / 法規制対象データ
  • ⚠ 注意: AZ障害でデータ消失 → 重要データは必ずStandard S3 + CRRで分離

詰まり5: CRR RTC SLA超過(RTC有効化忘れ / CloudWatch Metrics監視漏れ)

なぜ詰まるか

CRR(Cross-Region Replication)はデフォルトでベストエフォートのレプリケーションであり、15分以内の到達は保証されない。DR要件で「15分以内にReplica Regionで復旧可能」と設計したにもかかわらず、RTC(Replication Time Control)を有効化し忘れると、大容量オブジェクットや高負荷時に数時間かかることがある。さらにCloudWatch Metricsでレプリケーション遅延を監視していないと、SLA違反を検知できない。

どう解くか

RTCを有効化し、CloudWatch ReplicationLatency / OperationsPendingReplication メトリクスにアラームを設定する。

# RTC有効化確認
aws s3api get-bucket-replication \
  --bucket your-source-bucket \
  --query 'ReplicationConfiguration.Rules[*].DestinationReplicationTimeControl'

# CloudWatch Alarmでレプリ遅延監視
aws cloudwatch put-metric-alarm \
  --alarm-name "CRR-ReplicationLatency-High" \
  --metric-name ReplicationLatency \
  --namespace AWS/S3 \
  --dimensions Name=SourceBucket,Value=your-source-bucket \
  --statistic Maximum \
  --period 300 \
  --threshold 900 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --alarm-actions arn:aws:sns:ap-northeast-1:123456789012:infra-alerts

詰まり6: KMS Cross-Region キー設定漏れ(CRR + KMS のIAMロール権限不足)

なぜ詰まるか

CRRでKMS暗号化済みオブジェクットをレプリケーションする場合、Source BucketとReplica Bucketそれぞれに対応するKMSキーが必要で、IAMロールにも両リージョンのKMSキーへのアクセス権限が必要になる。KMSキー設定漏れやIAMポリシーの権限不足により、レプリケーションが静かに失敗し、Replica側にオブジェクットが届いていないことに気づかないケースが多い。

どう解くか

Replication IAMロールにはSource側KMSとReplica側KMSの両方への権限を付与する。Replica RegionでもKMS Multi-Region Keyを利用するか、独立したKMSキーを作成してKey Policyで権限を設定する。

# CRR IAMロール: KMS両リージョン権限
resource "aws_iam_role_policy" "crr_replication_kms" {
  name = "crr-kms-policy"
  role = aws_iam_role.crr_replication.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "kms:Decrypt",
 "kms:GenerateDataKey"
  ]
  Resource = var.source_kms_key_arn
},
{
  Effect = "Allow"
  Action = [
 "kms:Encrypt",
 "kms:GenerateDataKey"
  ]
  Resource = var.replica_kms_key_arn
}
 ]
  })
}

詰まり7: AbortIncompleteMultipartUpload 未設定で孤立アップロード費爆増

なぜ詰まるか

マルチパートアップロードはアップロードが途中で失敗しても、既にアップロードされたパートがS3に残り続ける。これらの「孤立パーツ」はオブジェクットとして完成していないにもかかわらず、GBあたりのストレージ料金が発生する。大容量ファイルを頻繁にアップロードするBucketでは、孤立パーツのコストが正規オブジェクットのコストを超えることがある。

どう解くか

全BucketにLifecycle Rule AbortIncompleteMultipartUpload を設定し、7日以内に未完了のマルチパートアップロードを自動削除する。既存の孤立パーツは list-multipart-uploads で確認し abort-multipart-upload で削除する。

# 既存の孤立パーツ確認
aws s3api list-multipart-uploads \
  --bucket your-bucket-name \
  --output table

# Lifecycle Rule: AbortIncompleteMultipartUpload
aws s3api put-bucket-lifecycle-configuration \
  --bucket your-bucket-name \
  --lifecycle-configuration '{
 "Rules": [{
"ID": "abort-incomplete-multipart",
"Status": "Enabled",
"AbortIncompleteMultipartUpload": {
  "DaysAfterInitiation": 7
},
"Filter": {"Prefix": ""}
 }]
  }'
詰まりポイント7選 クイックリファレンス

  • Lifecycle競合: GetBucketLifecycleConfiguration → Rule数20本以下に正規化
  • IT高コスト: Storage Lens → 128KB未満 50%超のBucketは除外
  • Object Lambda課金: CostExplorerタグ追跡 + 月次予算アラート設定
  • Express消失: キャッシュ専用限定 → 永続化はStandard + CRR
  • CRR RTC: RTC有効化 + ReplicationLatency Alarm (閾値15分)
  • KMS権限: IAMロールにSource/Replica両方のKMS Decrypt/Encrypt権限付与
  • Multipart孤立: 全Bucketに AbortIncompleteMultipartUpload 7日設定

7. アンチパターン→正解パターン変換演習5問

実際の現場でよく見る誤設計パターンを「NGパターン → 問題点 → 正解パターン」の3段構成で解説する。


演習1: Lifecycle Rule 全バケット一律適用

NGパターン

コスト削減施策として「全BucketのオブジェクットをStandard → Standard-IA → Glacier → Glacier Deep Archiveの順に段階移行」するルールを一律適用した。

問題点

Bucket用途を無視した一律適用により、頻繁にアクセスされるデータがStandard-IAに移行され、取り出し料金($0.01/GB)が発生してコストが逆に増加した。また、アプリケーションのレスポンスタイムが悪化しSLAを超過した。

正解パターン

BucketをアクセスパターンでグルーピングしてLifecycle Ruleを分離する。Storage Lensでアクセスパターンを分析してからルールを適用する。

# 分析用: Storage Lens アクセスパターン確認後に適用
locals {
  # 用途別Bucket設定
  hot_buckets  = ["application-uploads", "api-responses"]# アクセス頻度高 → Lifecycle不要
  warm_buckets = ["logs-aggregated", "reports-monthly"]  # 30日後Standard-IA移行
  archive_buckets = ["backup-data", "audit-logs-archive"]# 90日後Glacier移行
}

resource "aws_s3_bucket_lifecycle_configuration" "warm" {
  for_each = toset(local.warm_buckets)
  bucket= each.value

  rule {
 id  = "warm-to-ia"
 status = "Enabled"

 transition {
days = 30
storage_class = "STANDARD_IA"
 }

 expiration {
days = 365
 }
  }
}

Trade-off: Bucket別管理になるためTerraform Moduleで共通化が必要。ただし用途別の最適化によりコスト削減率が向上する。


演習2: Intelligent-Tiering 128KB未満オブジェクットBucketへの適用

NGパターン

ログ収集BucketにIntelligent-Tieringを適用して自動最適化を期待した。しかし対象Bucketには平均10KBのJSONログファイルが数千万件蓄積されていた。

問題点

128KB未満オブジェクットへの監視コスト $0.0025/1,000件が大量発生。10,000万件のオブジェクットで月$250の純増となり、階層化による削減ゼロで完全にコスト増。

正解パターン

Intelligent-Tiering適用前にStorage Lensでオブジェクットサイズ分布を必ず確認する。128KB未満が多いBucketはStandard-IA + Lifecycle Ruleの組み合わせで対応する。

# Storage Lens: オブジェクットサイズ分布確認
aws s3control get-storage-lens-configuration \
  --config-id default \
  --account-id $(aws sts get-caller-identity --query Account --output text)

# 小オブジェクット向け: Standard-IA + Expiration
# (Intelligent-Tiering は使わない)
aws s3api put-bucket-lifecycle-configuration \
  --bucket your-log-bucket \
  --lifecycle-configuration '{
 "Rules": [{
"ID": "log-standard-ia-expiry",
"Status": "Enabled",
"Transition": {"Days": 30, "StorageClass": "STANDARD_IA"},
"Expiration": {"Days": 90},
"Filter": {"Prefix": "logs/"}
 }]
  }'

Trade-off: Intelligent-Tieringの「自動最適化」を手放す代わりに監視コストを削減できる。アクセスパターンが予測可能なログBucketにはLifecycleの方が適している。


演習3: Object Lambda フル変換(常時高負荷Lambda)

NGパターン

S3 Object Lambdaで全GETリクエストに対してPII(個人情報)マスキング変換を行う設定を本番導入した。変換処理はPythonのregexで1KB〜10MBの各種ドキュメントを処理する。

問題点

リクエスト数のスケールにともないLambda実行コストが急増。月200万リクエストで約$80/月の予期外コストが発生。また大容量ファイルではLambda Timeout(30秒)に達してエラーになるケースも発生した。

正解パターン

変換結果をS3にキャッシュしてLambda重複実行を削減する。大容量ファイルはStreaming Responseで処理し、CloudFrontをObject Lambdaの前段に配置してキャッシュを活用する。

import boto3
import json

def lambda_handler(event, context):
 s3_client = boto3.client('s3')
 object_context = event['getObjectContext']
 request_route = object_context['outputRoute']
 request_token = object_context['outputToken']
 s3_url = object_context['inputS3Url']

 # Streaming response で大容量ファイル対応
 response = s3_client.get_object(Bucket='source-bucket', Key=event['userRequest']['url'].split('/')[-1])

 # PII マスキング (Streaming処理)
 body = response['Body'].read().decode('utf-8')
 masked_body = mask_pii(body)

 s3_client.write_get_object_response(
  Body=masked_body,
  RequestRoute=request_route,
  RequestToken=request_token,
  ContentType=response['ContentType']
 )

 return {'statusCode': 200}

def mask_pii(text):
 import re
 # メールアドレスマスキング
 text = re.sub(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', '[EMAIL]', text)
 # 電話番号マスキング
 text = re.sub(r'0\d{1,4}-\d{1,4}-\d{4}', '[PHONE]', text)
 return text

Trade-off: キャッシュ戦略を追加することで実装複雑度が上がるが、コストを70〜80%削減できる。変換処理の冪等性(同一入力→同一出力)が前提条件。


演習4: CRR Versioning未有効化

NGパターン

DR構成として東京 → 大阪のCRRを設定しようとしたが、「Versioningは後で有効化すればいい」として先にCRR設定を進めた。

問題点

CRRはSourceとDestinationの両Bucketでバージョニングが有効化されていることが前提条件。バージョニングなしでCRRを設定しようとすると InvalidRequest エラーが返り、設定自体が完了しない。さらにすでにBucketに存在するオブジェクットは自動的にレプリケーションされない。

正解パターン

CRR設定前にSource/Destination両Bucketのバージョニングを有効化する。既存オブジェクットは S3 Batch Operations を使ってバックフィルする。

# Step1: 両BucketでVersioning有効化(必須)
resource "aws_s3_bucket_versioning" "source" {
  bucket = aws_s3_bucket.source.id
  versioning_configuration {
 status = "Enabled"
  }
}

resource "aws_s3_bucket_versioning" "replica" {
  provider = aws.replica
  bucket= aws_s3_bucket.replica.id
  versioning_configuration {
 status = "Enabled"
  }
}

# Step2: Versioning有効化後にCRR設定
resource "aws_s3_bucket_replication_configuration" "crr" {
  depends_on = [
 aws_s3_bucket_versioning.source,
 aws_s3_bucket_versioning.replica
  ]

  bucket = aws_s3_bucket.source.id
  role= aws_iam_role.crr_replication.arn

  rule {
 id  = "full-replication"
 status = "Enabled"

 destination {
bucket  = aws_s3_bucket.replica.arn
storage_class = "STANDARD"
 }
  }
}

Trade-off: バージョニング有効化後は削除マーカーも管理対象になるため、Lifecycle Ruleで古いバージョンの削除ポリシーも合わせて設定する必要がある。


演習5: Express One Zone 重要データ単独保存

NGパターン

ML推論の高速化を目的にS3 Express One Zoneを導入した際、「どうせS3だから大丈夫」と判断して推論に使う学習済みモデルファイル(再生成に数日かかるもの)もExpress One Zoneのみに保存した。

問題点

Express One ZoneはSingle-AZ構成のため、AZ障害発生時にモデルファイルにアクセス不能(最悪消失)となる。モデルファイルの再学習には3〜5日かかるため、ML推論サービスが長期停止するリスクが発生する。

正解パターン

Express One Zoneは「推論キャッシュ(一時的な中間データ)」のみ格納し、モデルファイル本体はStandard S3(+ CRRでDR)に保存して常時同期する設計にする。

# モデルファイル本体: Standard S3 (永続化)
resource "aws_s3_bucket" "model_storage" {
  bucket = "ml-model-storage-primary"

  tags = {
 DataType = "ml-model"
 Persistence = "permanent"
 DR = "required"
  }
}

# 推論キャッシュ: Express One Zone (一時データ)
resource "aws_s3_bucket" "inference_cache" {
  bucket = "ml-inference-cache--use1-az1--x-s3"

  tags = {
 DataType = "inference-cache"
 Persistence = "ephemeral"
  }
}

# アプリケーション側: フォールバック設計
# 1. Express One Zoneからキャッシュ読み込み試行
# 2. キャッシュミス or エラー時はStandard S3からフォールバック
# 3. Standard S3からキャッシュにコピーして以降高速化

Trade-off: 管理するBucketが増えるが、AZ障害耐性が向上しRPO=0(データ消失リスクなし)を達成できる。Express One ZoneからStandard S3へのフォールバックロジックをアプリケーション側に実装する必要がある。

アンチパターン5選 総括チェックリスト

  • Lifecycle: 用途別BucketグルーピングでRule分離 → 一律適用は禁止
  • Intelligent-Tiering: Storage Lensで128KB未満比率を事前確認 → 50%超は除外
  • Object Lambda: CloudFrontキャッシュ前段配置 + Streaming Response でコスト最適化
  • CRR: Versioning両Bucket先行有効化 → CRR設定は depends_on で順序保証
  • Express One Zone: キャッシュ専用限定 → モデル/永続データはStandard S3 + CRR

8. まとめ — S3 Advanced本番運用 Vol2 完全制覇

Vol2 5本柱の習得成果

本記事ではS3 Advanced本番運用の核心となる5本柱を解説した。

1. Lifecycle多段階Tier運用
Transition/Expirationルールの競合を避け、用途別BucketでRule設計する。AbortIncompleteMultipartUploadを必ず設定し、ストレージコストの最適化を継続的に行う。

2. Intelligent-Tiering 自動最適化
Storage Lensによるアクセスパターン分析を前提に、128KB以上・アクセス不規則なBucketのみに適用する。Archive Instant / Archive / Deep Archive Tierへの自動移行を活用して長期コスト削減を実現する。

3. Object Lambda 動的変換アーキテクチャ
PII除去・画像最適化・フォーマット変換をS3レイヤーで透過的に実装する。Lambda Access Pointによる変換パイプラインを構築し、CloudFrontキャッシュと組み合わせてコストと性能を両立する。

4. S3 Express One Zone 高性能活用
Single-AZ高速ストレージとしてML推論キャッシュや一時処理データに限定活用する。永続化要件があるデータは必ずStandard S3 + CRRとの組み合わせ設計にする。

5. CRR + RTC による本番DR設計
RTC有効化で15分SLAを保証し、KMS Cross-Region暗号化でセキュリティを担保する。CloudWatch ReplicationLatency監視で遅延異常を即時検知する体制を構築する。


落とし穴10選 — S3 Advanced本番運用の最重要チェックポイント

落とし穴10選: 本番投入前に必ずチェック

  • 穴1: Lifecycle Rule同一prefix重複 → GetBucketLifecycleConfiguration でRule数とFilter確認
  • 穴2: Intelligent-Tiering 128KB未満適用 → Storage Lensで事前サイズ分布確認必須
  • 穴3: Object Lambda Timeout超過 → 大容量ファイルにはStreaming Response対応必須
  • 穴4: Express One Zone 永続データ保存 → Single-AZリスク理解 + Standard S3との分離設計
  • 穴5: CRR RTC未有効化 → デフォルトはベストエフォート / 15分SLAにはRTC明示設定
  • 穴6: KMS IAMロール権限不足 → Source + Replica両方のKMS Decrypt/Encrypt権限付与
  • 穴7: AbortIncompleteMultipartUpload未設定 → 全Bucketに7日ルール設定 (孤立パーツコスト防止)
  • 穴8: CRR Versioning未有効化 → Source/Destination両Bucket先行バージョニング必須
  • 穴9: Object Lambda コスト未見積もり → S3 GET + Lambda実行料金の両方をCost Explorerで追跡
  • 穴10: Intelligent-Tiering Archive Tier設定漏れ → OptionalTierを明示有効化しないと深層Tierに移行されない

Storage Vol1 ↔ Vol2 双方向連続リンク

Storage本番運用シリーズ 全体像

  • Vol1 基礎4本柱: S3基礎設計 / EFS本番運用 / FSx for Windows&Lustre / Storage Gateway多拠点
  • Vol2 S3 Advanced (本記事): Lifecycle多段階 / Intelligent-Tiering / Object Lambda / Express One Zone / CRR
  • Vol3 予告: EFS Advanced (Performance Mode) / FSx for OpenZFS深掘り / Storage Gateway Multi-Site HA
  • Vol1 + Vol2でS3中心のAWS本番ストレージ運用を完全カバー

← Storage本番運用 Vol1 — S3基礎×EFS×FSx×Storage Gateway へ戻る

↑ Storage本番運用 Vol2 — 記事トップへ戻る


AWS本番運用 全シリーズ クロスリンクナビ

AWS本番運用 全シリーズ 横断ナビ — 関連記事へのショートカット