TerraformでCloudFrontを構築する基礎知識
CloudFrontとTerraformの関係性を理解しよう
Amazon CloudFrontは、AWSが提供するCDN(Content Delivery Network)サービスです。TerraformはこのCloudFrontの構成をコードとして管理できる強力なツールです。
CloudFrontの主要コンポーネントとTerraformリソースの対応:
CloudFrontコンポーネント | Terraformリソース | 説明 |
---|---|---|
Distribution | aws_cloudfront_distribution | CDNの基本設定を定義 |
Origin | origin ブロック | コンテンツの配信元を設定 |
Behavior | ordered_cache_behavior ブロック | URLパスごとの振る舞いを定義 |
Function | aws_cloudfront_function | エッジでの軽量な処理を実装 |
Terraform管理のメリット3つと注意点2つ
メリット1:インフラのコード化(IaC)
- 設定の一元管理が可能
- バージョン管理システムとの連携
- コードレビューによる品質担保
# CloudFront Distributionの基本的な定義例 resource "aws_cloudfront_distribution" "example" { origin { domain_name = aws_s3_bucket.example.bucket_regional_domain_name origin_id = "S3-${aws_s3_bucket.example.id}" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.example.cloudfront_access_identity_path } } enabled = true is_ipv6_enabled = true default_root_object = "index.html" # 他の設定... }
メリット2:環境の複製が容易
- 開発環境と本番環境の一貫性確保
- マルチリージョン展開の効率化
- ディザスタリカバリ対策の実装
メリット3:自動化との親和性
- CI/CDパイプラインとの統合
- 自動テストの実装
- デプロイの標準化
注意点1:状態管理の重要性
- tfstateファイルの適切な管理が必須
- リモートステート(S3+DynamoDB)の利用推奨
- 状態ロックによる同時実行制御
# バックエンド設定例 terraform { backend "s3" { bucket = "terraform-state-bucket" key = "cloudfront/terraform.tfstate" region = "ap-northeast-1" dynamodb_table = "terraform-state-lock" encrypt = true } }
注意点2:無効化(Invalidation)の考慮
- キャッシュ無効化の適切な実装
- コスト管理の必要性
- デプロイ戦略との整合性
このように、TerraformでCloudFrontを管理することで、インフラストラクチャの管理を効率化できます。ただし、適切な状態管理と運用戦略の検討が重要です。
Terraformによるクラウドフロント構築手順
基本的なディストリビューション設定の書き方
CloudFrontディストリビューションの基本設定には、以下の要素が含まれます:
resource "aws_cloudfront_distribution" "main" { # 基本設定 enabled = true is_ipv6_enabled = true comment = "My CloudFront Distribution" default_root_object = "index.html" # 価格クラスの選択(コスト最適化) price_class = "PriceClass_200" # アジア・ヨーロッパ・北米をカバー # アクセス制限 restrictions { geo_restriction { restriction_type = "whitelist" locations = ["JP", "US"] # 日本とアメリカのみアクセス可能 } } # ビューワー証明書の設定 viewer_certificate { acm_certificate_arn = aws_acm_certificate.cert.arn ssl_support_method = "sni-only" minimum_protocol_version = "TLSv1.2_2021" } }
オリジンの設定方法と最適な設定値
オリジンタイプごとの最適な設定:
1. S3バケットをオリジンとする場合
resource "aws_cloudfront_distribution" "s3_distribution" { origin { domain_name = aws_s3_bucket.website.bucket_regional_domain_name origin_id = "S3-${aws_s3_bucket.website.id}" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path } # オリジンシールドの設定(オプション) origin_shield { enabled = true origin_shield_region = "ap-northeast-1" } } }
2. ALBをオリジンとする場合
resource "aws_cloudfront_distribution" "alb_distribution" { origin { domain_name = aws_lb.example.dns_name origin_id = "ALB-${aws_lb.example.name}" custom_origin_config { http_port = 80 https_port = 443 origin_protocol_policy = "https-only" origin_ssl_protocols = ["TLSv1.2"] } custom_header { name = "X-Origin-Verify" value = var.origin_custom_header # セキュリティ強化 } } }
キャッシュ動作のカスタマイズ方法
キャッシュ設定は、パフォーマンスとコストに直接影響を与えます:
resource "aws_cloudfront_distribution" "web_app" { # デフォルトのキャッシュ動作 default_cache_behavior { allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] cached_methods = ["GET", "HEAD"] target_origin_id = local.origin_id # キャッシュポリシーの設定 cache_policy_id = aws_cloudfront_cache_policy.example.id # オリジンリクエストポリシー origin_request_policy_id = aws_cloudfront_origin_request_policy.example.id # レスポンスヘッダーポリシー response_headers_policy_id = aws_cloudfront_response_headers_policy.example.id viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 } # パスパターン別のキャッシュ動作 ordered_cache_behavior { path_pattern = "/api/*" allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = local.api_origin_id forwarded_values { query_string = true headers = ["Authorization"] cookies { forward = "whitelist" whitelisted_names = ["session-id"] } } viewer_protocol_policy = "https-only" min_ttl = 0 default_ttl = 0 # APIはキャッシュしない max_ttl = 0 } }
効果的なキャッシュ設定のポイント:
- 静的コンテンツは長めのTTL設定
- 動的コンテンツは適切なヘッダー転送設定
- セキュリティ要件に応じたCookieの管理
- キャッシュキーの最適化によるヒット率向上
実践的なTerraform設定例と解説
S3バケットをオリジンにした静的Webサイトの構築
# S3バケットの作成 resource "aws_s3_bucket" "website" { bucket = "example-static-website" } resource "aws_s3_bucket_public_access_block" "website" { bucket = aws_s3_bucket.website.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } # CloudFront OAIの作成 resource "aws_cloudfront_origin_access_identity" "oai" { comment = "OAI for ${aws_s3_bucket.website.bucket}" } # S3バケットポリシー resource "aws_s3_bucket_policy" "website" { bucket = aws_s3_bucket.website.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AllowCloudFrontOAI" Effect = "Allow" Principal = { AWS = aws_cloudfront_origin_access_identity.oai.iam_arn } Action = "s3:GetObject" Resource = "${aws_s3_bucket.website.arn}/*" } ] }) } # CloudFront Distribution resource "aws_cloudfront_distribution" "website" { origin { domain_name = aws_s3_bucket.website.bucket_regional_domain_name origin_id = "S3-${aws_s3_bucket.website.bucket}" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path } } enabled = true is_ipv6_enabled = true default_root_object = "index.html" custom_error_response { error_code = 404 response_code = 200 response_page_path = "/index.html" # SPAのルーティング対応 } default_cache_behavior { allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = "S3-${aws_s3_bucket.website.bucket}" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 compress = true } price_class = "PriceClass_200" restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { cloudfront_default_certificate = true } }
ALBをオリジンにしたアプリケーションの配信
# セキュリティグループの設定 resource "aws_security_group" "alb" { name = "allow-cloudfront-only" description = "Allow inbound traffic from CloudFront only" vpc_id = var.vpc_id ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = data.aws_ip_ranges.cloudfront.cidr_blocks } } # ALBの設定 resource "aws_lb" "app" { name = "app-alb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.alb.id] subnets = var.public_subnet_ids } # CloudFront Distribution resource "aws_cloudfront_distribution" "app" { origin { domain_name = aws_lb.app.dns_name origin_id = "ALB-${aws_lb.app.name}" custom_origin_config { http_port = 80 https_port = 443 origin_protocol_policy = "https-only" origin_ssl_protocols = ["TLSv1.2"] } custom_header { name = "X-Custom-Header" value = random_password.origin_secret.result } } enabled = true default_cache_behavior { allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"] cached_methods = ["GET", "HEAD"] target_origin_id = "ALB-${aws_lb.app.name}" forwarded_values { query_string = true headers = ["Host", "Authorization"] cookies { forward = "all" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 0 max_ttl = 0 } }
WAFとの連携でセキュリティを強化
# WAF IPレートリミッティングルール resource "aws_wafv2_web_acl" "cloudfront" { name = "cloudfront-waf" description = "WAF rules for CloudFront" scope = "CLOUDFRONT" default_action { allow {} } rule { name = "RateLimit" priority = 1 override_action { none {} } statement { rate_based_statement { limit = 2000 aggregate_key_type = "IP" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "RateLimitMetric" sampled_requests_enabled = true } } rule { name = "BlockBadBots" priority = 2 override_action { none {} } statement { byte_match_statement { field_to_match { single_header { name = "user-agent" } } positional_constraint = "CONTAINS" search_string = "BadBot" text_transformation { priority = 1 type = "NONE" } } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "BlockBadBotsMetric" sampled_requests_enabled = true } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "CloudFrontWAFMetric" sampled_requests_enabled = true } } # CloudFrontとWAFの連携 resource "aws_cloudfront_distribution" "protected" { # ... 他の設定 ... web_acl_id = aws_wafv2_web_acl.cloudfront.id # ... 他の設定 ... }
これらの設定例は、実際の本番環境で使用できる実践的なものです。必要に応じてカスタマイズして使用してください。
CloudFrontの運用管理をTerraformで効率化
証明書の自動更新の実装方法
ACM証明書の自動更新をTerraformで実装する主なポイント:
# プロバイダー設定 provider "aws" { alias = "virginia" region = "us-east-1" # CloudFront用証明書はバージニアリージョンが必須 } # ACM証明書の作成 resource "aws_acm_certificate" "main" { provider = aws.virginia domain_name = "example.com" validation_method = "DNS" subject_alternative_names = ["*.example.com"] lifecycle { create_before_destroy = true # 証明書の自動更新を有効化 } tags = { Name = "cloudfront-cert" } } # Route 53でのDNS検証レコード resource "aws_route53_record" "cert_validation" { provider = aws.virginia for_each = { for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = data.aws_route53_zone.main.zone_id } # 証明書の検証完了を待機 resource "aws_acm_certificate_validation" "cert" { provider = aws.virginia certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] } # CloudFrontに証明書を関連付け resource "aws_cloudfront_distribution" "main" { viewer_certificate { acm_certificate_arn = aws_acm_certificate.main.arn minimum_protocol_version = "TLSv1.2_2021" ssl_support_method = "sni-only" } # その他の設定... }
GitHubActionsでの自動デプロイ設定
name: 'Terraform CloudFront Deploy' on: push: branches: [ main ] paths: - 'terraform/**' pull_request: branches: [ main ] paths: - 'terraform/**' jobs: terraform: name: 'Terraform' runs-on: ubuntu-latest env: AWS_DEFAULT_REGION: ap-northeast-1 TF_WORKSPACE: production defaults: run: working-directory: ./terraform steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.5.0 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Terraform Format run: terraform fmt -check - name: Terraform Init run: | terraform init \ -backend-config="bucket=${{ secrets.TF_STATE_BUCKET }}" \ -backend-config="key=cloudfront/terraform.tfstate" - name: Terraform Plan if: github.event_name == 'pull_request' run: terraform plan -no-color continue-on-error: true - name: Terraform Apply if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve - name: Invalidate CloudFront if: success() && github.event_name == 'push' run: | aws cloudfront create-invalidation \ --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \ --paths "/*"
本番環境と開発環境の設定分離
設定分離のベストプラクティス:
# modules/cloudfront/main.tf locals { common_tags = { Environment = var.environment ManagedBy = "terraform" Project = var.project_name } } module "cloudfront" { source = "./modules/cloudfront" environment = terraform.workspace distribution_config = { price_class = local.env_configs[terraform.workspace].price_class waf_enabled = local.env_configs[terraform.workspace].waf_enabled logging_enabled = local.env_configs[terraform.workspace].logging_enabled } origin_config = { domain_name = local.env_configs[terraform.workspace].origin_domain custom_headers = local.env_configs[terraform.workspace].origin_headers } cache_behavior = { min_ttl = local.env_configs[terraform.workspace].cache_ttl.min default_ttl = local.env_configs[terraform.workspace].cache_ttl.default max_ttl = local.env_configs[terraform.workspace].cache_ttl.max } tags = local.common_tags } # environments/prod/main.tf locals { env_configs = { prod = { price_class = "PriceClass_200" waf_enabled = true logging_enabled = true origin_domain = "api.example.com" origin_headers = { "X-Environment" = "production" } cache_ttl = { min = 0 default = 3600 max = 86400 } } dev = { price_class = "PriceClass_100" waf_enabled = false logging_enabled = true origin_domain = "dev-api.example.com" origin_headers = { "X-Environment" = "development" } cache_ttl = { min = 0 default = 0 max = 0 } } } } # ワークスペース別の状態管理 terraform { backend "s3" { bucket = "terraform-state" key = "cloudfront/terraform.tfstate" region = "ap-northeast-1" dynamodb_table = "terraform-locks" encrypt = true } }
主な利点:
- 証明書の自動更新による可用性確保
- GitHubActionsによる安全なCI/CD
- 環境ごとの設定管理の一元化
- インフラのバージョン管理とロールバック機能
- コスト最適化のための環境別設定
トラブルシューティングと解決策20選
デプロイ時によくあるエラーと対処法
1. 証明書関連のエラー
# エラー: Error creating CloudFront Distribution: InvalidViewerCertificate # 原因: ACM証明書がus-east-1リージョンにない provider "aws" { alias = "virginia" region = "us-east-1" } resource "aws_acm_certificate" "cert" { provider = aws.virginia # 必ずバージニアリージョンを指定 # ... }
2. OAIの権限エラー
# S3バケットポリシーの修正 resource "aws_s3_bucket_policy" "website" { bucket = aws_s3_bucket.website.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AllowCloudFrontOAI" Effect = "Allow" Principal = { AWS = aws_cloudfront_origin_access_identity.oai.iam_arn } Action = "s3:GetObject" Resource = "${aws_s3_bucket.website.arn}/*" } ] }) }
3. tfstateの競合
# 状態ファイルのロック設定 terraform { backend "s3" { bucket = "terraform-state" key = "cloudfront/terraform.tfstate" region = "ap-northeast-1" dynamodb_table = "terraform-locks" # 必ずロックテーブルを指定 encrypt = true } }
4. 無効化エラー
# 無効化の適切な実装 resource "null_resource" "invalidation" { triggers = { distribution_id = aws_cloudfront_distribution.main.id } provisioner "local-exec" { command = <<EOF aws cloudfront create-invalidation \ --distribution-id ${aws_cloudfront_distribution.main.id} \ --paths "/*" EOF } }
パフォーマンス最適化のためのチェックポイント
1. オリジンシールドの設定
resource "aws_cloudfront_distribution" "optimized" { origin { domain_name = aws_s3_bucket.origin.bucket_regional_domain_name origin_id = local.origin_id origin_shield { enabled = true origin_shield_region = "ap-northeast-1" # 最も近いリージョン } } }
2. 圧縮設定の最適化
resource "aws_cloudfront_distribution" "optimized" { default_cache_behavior { compress = true forwarded_values { query_string = false headers = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"] } } }
3. キャッシュ設定の最適化
resource "aws_cloudfront_cache_policy" "optimized" { name = "optimized-caching" min_ttl = 1 default_ttl = 86400 # 24時間 max_ttl = 31536000 # 1年 parameters_in_cache_key_and_forwarded_to_origin { cookies_config { cookie_behavior = "none" } headers_config { header_behavior = "whitelist" headers { items = ["Origin", "Access-Control-Request-Headers", "Access-Control-Request-Method"] } } query_strings_config { query_string_behavior = "none" } } }
コスト最適化のためのベストプラクティス
1. 価格クラスの最適化
resource "aws_cloudfront_distribution" "cost_optimized" { price_class = "PriceClass_200" # アジア・ヨーロッパ・北米のみ }
2. キャッシュヒット率の監視
resource "aws_cloudwatch_metric_alarm" "cache_hit" { alarm_name = "cloudfront-cache-hit-rate" comparison_operator = "LessThanThreshold" evaluation_periods = "2" metric_name = "CacheHitRate" namespace = "AWS/CloudFront" period = "300" statistic = "Average" threshold = "90" alarm_description = "CloudFrontのキャッシュヒット率が90%を下回っています" dimensions = { DistributionId = aws_cloudfront_distribution.main.id } }
3. 不要なリージョンのブロック
resource "aws_cloudfront_distribution" "cost_optimized" { restrictions { geo_restriction { restriction_type = "whitelist" locations = ["JP", "US", "CA"] # 必要なリージョンのみ許可 } } }
トラブルシューティングのためのベストプラクティス
- デバッグ用ヘッダーの設定
resource "aws_cloudfront_distribution" "debug" { default_cache_behavior { response_headers_policy_id = aws_cloudfront_response_headers_policy.debug.id } } resource "aws_cloudfront_response_headers_policy" "debug" { name = "debug-headers" custom_headers_config { items { header = "X-Cache-Debug" override = true value = "true" } items { header = "X-Edge-Location" override = true value = "true" } } }
- エラーページのカスタマイズ
resource "aws_cloudfront_distribution" "debug" { custom_error_response { error_code = 403 response_code = 200 response_page_path = "/error/403.html" error_caching_min_ttl = 300 } custom_error_response { error_code = 404 response_code = 200 response_page_path = "/error/404.html" error_caching_min_ttl = 300 } }
- ログ設定の最適化
resource "aws_cloudfront_distribution" "logging" { logging_config { include_cookies = true bucket = "${aws_s3_bucket.logs.bucket_domain_name}" prefix = "cloudfront/" } } resource "aws_s3_bucket" "logs" { bucket = "cloudfront-logs-${data.aws_caller_identity.current.account_id}" } resource "aws_s3_bucket_lifecycle_rule" "logs" { bucket = aws_s3_bucket.logs.id expiration { days = 90 # 90日後に自動削除 } }
これらの設定とベストプラクティスを実装することで、多くの一般的な問題を防ぎ、効率的な運用が可能になります。