iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Build on AWS

30 天將工作室 SaaS 產品部署起來系列 第 18

Day 18: 30天部署SaaS產品到AWS-雲端效能優化策略

  • 分享至 

  • xImage
  •  

前情提要

在 Day 17 我們建立了微服務部署架構,今天我們要聚焦在 AWS 雲端效能優化。從 CloudFront CDN 配置、Auto Scaling 智能擴展、到 RDS 效能調校,我們將實現企業級的雲端效能最佳化方案,確保 SaaS 系統在雲端環境中達到最佳效能表現。

CloudFront CDN 進階優化

智能快取策略與 Lambda@Edge

# infrastructure/cloudfront-optimization.yml
Resources:
  OptimizedCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        # 基礎配置
        Enabled: true
        HttpVersion: http2
        PriceClass: PriceClass_All
        IPV6Enabled: true

        # 來源配置
        Origins:
          - Id: S3-StaticAssets
            DomainName: !GetAtt S3Bucket.DomainName
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOAI}"
            OriginCustomHeaders:
              - HeaderName: X-Forwarded-Proto
                HeaderValue: https

          - Id: ALB-API
            DomainName: !GetAtt ApplicationLoadBalancer.DNSName
            CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginProtocolPolicy: https-only
              OriginSSLProtocols: [TLSv1.2]
              OriginReadTimeout: 30
              OriginKeepaliveTimeout: 5

        # 快取行為配置
        DefaultCacheBehavior:
          TargetOriginId: S3-StaticAssets
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: !Ref OptimizedCachePolicy
          OriginRequestPolicyId: !Ref CORSRequestPolicy
          ResponseHeadersPolicyId: !Ref SecurityHeadersPolicy
          Compress: true

          # Lambda@Edge 功能
          LambdaFunctionAssociations:
            - EventType: viewer-request
              LambdaFunctionARN: !GetAtt EdgeOptimizationFunction.FunctionArn
            - EventType: origin-response
              LambdaFunctionARN: !GetAtt CompressionOptimizationFunction.FunctionArn

        CacheBehaviors:
          # API 快取行為
          - PathPattern: "/api/*"
            TargetOriginId: ALB-API
            ViewerProtocolPolicy: https-only
            CachePolicyId: !Ref APICachePolicy
            OriginRequestPolicyId: !Ref APIRequestPolicy
            AllowedMethods: [GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE]
            CachedMethods: [GET, HEAD, OPTIONS]
            Compress: true

          # 靜態資源快取行為
          - PathPattern: "/static/*"
            TargetOriginId: S3-StaticAssets
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: !Ref StaticAssetsCachePolicy
            Compress: true

          # 動態內容快取行為
          - PathPattern: "/app/*"
            TargetOriginId: S3-StaticAssets
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: !Ref DynamicContentCachePolicy
            OriginRequestPolicyId: !Ref CORSRequestPolicy
            Compress: true

        # 自訂錯誤頁面
        CustomErrorResponses:
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
            ErrorCachingMinTTL: 300
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
            ErrorCachingMinTTL: 300

  # 優化的快取策略
  OptimizedCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: OptimizedCachePolicy
        DefaultTTL: 86400      # 1 天
        MaxTTL: 31536000       # 1 年
        MinTTL: 0
        ParametersInCacheKeyAndForwardedToOrigin:
          EnableAcceptEncodingGzip: true
          EnableAcceptEncodingBrotli: true
          QueryStringsConfig:
            QueryStringBehavior: whitelist
            QueryStrings: [version, locale, theme]
          HeadersConfig:
            HeaderBehavior: whitelist
            Headers:
              - Accept-Language
              - Accept-Encoding
              - User-Agent
          CookiesConfig:
            CookieBehavior: whitelist
            Cookies: [session-id, user-preferences]

  # API 專用快取策略
  APICachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: APICachePolicy
        DefaultTTL: 300        # 5 分鐘
        MaxTTL: 3600          # 1 小時
        MinTTL: 0
        ParametersInCacheKeyAndForwardedToOrigin:
          EnableAcceptEncodingGzip: true
          EnableAcceptEncodingBrotli: true
          QueryStringsConfig:
            QueryStringBehavior: all
          HeadersConfig:
            HeaderBehavior: whitelist
            Headers:
              - Authorization
              - Content-Type
              - X-API-Key
              - Accept
          CookiesConfig:
            CookieBehavior: none

  # 靜態資源快取策略
  StaticAssetsCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: StaticAssetsCachePolicy
        DefaultTTL: 604800     # 7 天
        MaxTTL: 31536000       # 1 年
        MinTTL: 604800         # 7 天
        ParametersInCacheKeyAndForwardedToOrigin:
          EnableAcceptEncodingGzip: true
          EnableAcceptEncodingBrotli: true
          QueryStringsConfig:
            QueryStringBehavior: whitelist
            QueryStrings: [v, version]
          HeadersConfig:
            HeaderBehavior: none
          CookiesConfig:
            CookieBehavior: none

Lambda@Edge 效能優化函數

// lambda-edge/edge-optimization.js
'use strict';

// 檢視器請求優化
exports.viewerRequest = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // 1. 裝置類型檢測與優化
    const userAgent = headers['user-agent'] ? headers['user-agent'][0].value : '';
    const isMobile = /Mobile|Android|iPhone|iPad/i.test(userAgent);

    if (isMobile) {
        // 行動裝置路由到優化版本
        if (request.uri.startsWith('/static/')) {
            request.uri = request.uri.replace('/static/', '/static/mobile/');
        }
    }

    // 2. 地理位置路由
    const countryCode = headers['cloudfront-viewer-country'] ?
        headers['cloudfront-viewer-country'][0].value : 'US';

    // 為特定地區提供本地化內容
    if (['CN', 'JP', 'KR'].includes(countryCode)) {
        headers['x-region'] = [{ key: 'X-Region', value: 'asia' }];
    }

    // 3. 壓縮算法協商
    const acceptEncoding = headers['accept-encoding'] ?
        headers['accept-encoding'][0].value : '';

    if (acceptEncoding.includes('br')) {
        headers['x-preferred-encoding'] = [{ key: 'X-Preferred-Encoding', value: 'br' }];
    } else if (acceptEncoding.includes('gzip')) {
        headers['x-preferred-encoding'] = [{ key: 'X-Preferred-Encoding', value: 'gzip' }];
    }

    // 4. 快取優化 Headers
    if (request.uri.match(/\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$/)) {
        headers['cache-control'] = [{
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable'
        }];
    }

    // 5. 安全性 Headers
    headers['strict-transport-security'] = [{
        key: 'Strict-Transport-Security',
        value: 'max-age=31536000; includeSubDomains; preload'
    }];

    headers['x-content-type-options'] = [{
        key: 'X-Content-Type-Options',
        value: 'nosniff'
    }];

    headers['x-frame-options'] = [{
        key: 'X-Frame-Options',
        value: 'SAMEORIGIN'
    }];

    callback(null, request);
};

// 來源響應優化
exports.originResponse = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const response = event.Records[0].cf.response;

    // 1. 自動 WebP 轉換
    const uri = request.uri;
    const acceptHeader = request.headers.accept ? request.headers.accept[0].value : '';

    if (uri.match(/\.(jpg|jpeg|png)$/) && acceptHeader.includes('image/webp')) {
        // 檢查是否有 WebP 版本
        const webpUri = uri.replace(/\.(jpg|jpeg|png)$/, '.webp');

        // 如果來源回應 404,嘗試獲取 WebP 版本
        if (response.status === '404') {
            const modifiedRequest = {
                ...request,
                uri: webpUri
            };

            // 重新導向到 WebP 版本
            response.status = '302';
            response.headers.location = [{ key: 'Location', value: webpUri }];
        }
    }

    // 2. 動態壓縮優化
    const preferredEncoding = request.headers['x-preferred-encoding'] ?
        request.headers['x-preferred-encoding'][0].value : '';

    if (preferredEncoding && !response.headers['content-encoding']) {
        // 為未壓縮的內容添加壓縮指示
        response.headers['vary'] = [{ key: 'Vary', value: 'Accept-Encoding' }];
    }

    // 3. 快取優化
    const contentType = response.headers['content-type'] ?
        response.headers['content-type'][0].value : '';

    if (contentType.startsWith('text/') ||
        contentType.includes('application/json') ||
        contentType.includes('application/javascript')) {

        // 文字內容快取配置
        response.headers['cache-control'] = [{
            key: 'Cache-Control',
            value: 'public, max-age=300, s-maxage=3600'
        }];
    } else if (contentType.startsWith('image/')) {
        // 圖片內容長期快取
        response.headers['cache-control'] = [{
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable'
        }];
    }

    // 4. 效能 Headers
    response.headers['x-cache'] = [{
        key: 'X-Cache',
        value: `HIT-${context.logStreamName}`
    }];

    callback(null, response);
};

// 壓縮優化函數
exports.compressionOptimization = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    // 檢查內容大小和類型
    const contentLength = response.headers['content-length'] ?
        parseInt(response.headers['content-length'][0].value) : 0;

    const contentType = response.headers['content-type'] ?
        response.headers['content-type'][0].value : '';

    // 只對大於 1KB 的可壓縮內容進行處理
    if (contentLength > 1024 && isCompressible(contentType)) {
        const acceptEncoding = request.headers['accept-encoding'] ?
            request.headers['accept-encoding'][0].value : '';

        // 設定最適合的壓縮算法
        if (acceptEncoding.includes('br') && contentType.includes('javascript')) {
            response.headers['content-encoding'] = [{ key: 'Content-Encoding', value: 'br' }];
            response.headers['vary'] = [{ key: 'Vary', value: 'Accept-Encoding' }];
        } else if (acceptEncoding.includes('gzip')) {
            response.headers['content-encoding'] = [{ key: 'Content-Encoding', value: 'gzip' }];
            response.headers['vary'] = [{ key: 'Vary', value: 'Accept-Encoding' }];
        }
    }

    callback(null, response);
};

function isCompressible(contentType) {
    const compressibleTypes = [
        'text/',
        'application/javascript',
        'application/json',
        'application/xml',
        'image/svg+xml'
    ];

    return compressibleTypes.some(type => contentType.includes(type));
}

Auto Scaling 智能擴展策略

ECS Fargate 自動擴展配置

# infrastructure/auto-scaling-config.yml
Resources:
  # ECS 服務自動擴展
  ECSAutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      ServiceNamespace: ecs
      ResourceId: !Sub service/${ECSCluster}/${ECSService}
      ScalableDimension: ecs:service:DesiredCount
      MinCapacity: 2
      MaxCapacity: 20
      RoleARN: !GetAtt ECSAutoScalingRole.Arn

  # CPU 使用率擴展策略
  CPUScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: CPUTargetTrackingScaling
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ECSAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70.0
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        ScaleOutCooldown: 300   # 5 分鐘
        ScaleInCooldown: 300    # 5 分鐘
        DisableScaleIn: false

  # 記憶體使用率擴展策略
  MemoryScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: MemoryTargetTrackingScaling
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ECSAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 80.0
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageMemoryUtilization
        ScaleOutCooldown: 300
        ScaleInCooldown: 600    # 更長的縮容冷卻時間

  # 自訂指標擴展策略 - 請求數量
  RequestCountScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: RequestCountTargetTracking
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ECSAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 1000.0
        CustomizedMetricSpecification:
          MetricName: RequestCountPerTarget
          Namespace: AWS/ApplicationELB
          Dimensions:
            - Name: TargetGroup
              Value: !GetAtt ALBTargetGroup.TargetGroupFullName
          Statistic: Sum
        ScaleOutCooldown: 180   # 3 分鐘 - 快速擴展
        ScaleInCooldown: 900    # 15 分鐘 - 保守縮容

  # 預測性擴展策略
  PredictiveScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: PredictiveScaling
      PolicyType: PredictiveScaling
      ScalingTargetId: !Ref ECSAutoScalingTarget
      PredictiveScalingPolicyConfiguration:
        MetricSpecifications:
          - TargetValue: 70.0
            PredefinedMetricSpecification:
              PredefinedMetricType: ECSServiceAverageCPUUtilization
        Mode: ForecastAndScale     # 預測並自動擴展
        SchedulingBufferTime: 300  # 5 分鐘緩衝時間
        MaxCapacityBreachBehavior: HonorMaxCapacity
        MaxCapacityBuffer: 20      # 20% 容量緩衝

  # 時間排程擴展
  ScheduledScalingAction:
    Type: AWS::ApplicationAutoScaling::ScheduledAction
    Properties:
      ServiceNamespace: ecs
      ResourceId: !Sub service/${ECSCluster}/${ECSService}
      ScalableDimension: ecs:service:DesiredCount
      Schedule: cron(0 8 * * MON-FRI)  # 週一到週五早上 8 點
      ScalableTargetAction:
        MinCapacity: 5
        MaxCapacity: 25
      Timezone: Asia/Taipei

  # 週末縮容排程
  WeekendScaleDownAction:
    Type: AWS::ApplicationAutoScaling::ScheduledAction
    Properties:
      ServiceNamespace: ecs
      ResourceId: !Sub service/${ECSCluster}/${ECSService}
      ScalableDimension: ecs:service:DesiredCount
      Schedule: cron(0 20 * * FRI)     # 週五晚上 8 點
      ScalableTargetAction:
        MinCapacity: 1
        MaxCapacity: 10
      Timezone: Asia/Taipei

Application Load Balancer 自動擴展

# ALB 目標群組自動擴展配置
Resources:
  # ALB 目標群組
  OptimizedTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: kyo-optimized-targets
      Port: 3000
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: ip

      # 健康檢查優化
      HealthCheckEnabled: true
      HealthCheckPath: /health
      HealthCheckProtocol: HTTP
      HealthCheckIntervalSeconds: 15      # 更頻繁的健康檢查
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2            # 更快標記為健康
      UnhealthyThresholdCount: 3
      Matcher:
        HttpCode: 200

      # 目標群組屬性優化
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: 30                       # 快速離線
        - Key: slow_start.duration_seconds
          Value: 60                       # 新目標暖身時間
        - Key: load_balancing.algorithm.type
          Value: least_outstanding_requests # 最少未完成請求
        - Key: stickiness.enabled
          Value: false                    # 禁用黏性以改善負載分散
        - Key: target_group_health.dns_failover.minimum_healthy_targets.count
          Value: 1
        - Key: target_group_health.unhealthy_state_routing.minimum_healthy_targets.count
          Value: 1

  # ALB 監聽器規則優化
  OptimizedListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      ListenerArn: !Ref ALBListener
      Priority: 100
      Conditions:
        - Field: path-pattern
          Values: ["/api/*"]
      Actions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref OptimizedTargetGroup
                Weight: 100
            TargetGroupStickinessConfig:
              Enabled: false

  # CloudWatch 警報 - 高延遲
  HighLatencyAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: ALB-HighLatency
      AlarmDescription: ALB target response time is too high
      MetricName: TargetResponseTime
      Namespace: AWS/ApplicationELB
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 1.0                    # 1 秒
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: LoadBalancer
          Value: !GetAtt ApplicationLoadBalancer.LoadBalancerFullName
        - Name: TargetGroup
          Value: !GetAtt OptimizedTargetGroup.TargetGroupFullName
      AlarmActions:
        - !Ref SNSAlarmTopic

  # CloudWatch 警報 - 錯誤率
  HighErrorRateAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: ALB-HighErrorRate
      AlarmDescription: ALB error rate is too high
      MetricName: HTTPCode_Target_5XX_Count
      Namespace: AWS/ApplicationELB
      Statistic: Sum
      Period: 300
      EvaluationPeriods: 2
      Threshold: 10
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: LoadBalancer
          Value: !GetAtt ApplicationLoadBalancer.LoadBalancerFullName
        - Name: TargetGroup
          Value: !GetAtt OptimizedTargetGroup.TargetGroupFullName
      AlarmActions:
        - !Ref SNSAlarmTopic

RDS 效能調校與最佳化

PostgreSQL RDS 最佳化配置

# infrastructure/rds-optimization.yml
Resources:
  # 優化的 RDS 實例
  OptimizedRDSInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: kyo-postgres-optimized
      DBInstanceClass: db.r6g.xlarge     # 記憶體最佳化實例
      Engine: postgres
      EngineVersion: 15.4
      AllocatedStorage: 100
      MaxAllocatedStorage: 1000          # 自動擴展儲存
      StorageType: gp3                   # 最新一代 SSD
      StorageEncrypted: true
      Iops: 3000                         # 高 IOPS 配置

      # 多可用區部署
      MultiAZ: true
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref DatabaseSecurityGroup

      # 備份配置
      BackupRetentionPeriod: 7
      PreferredBackupWindow: "03:00-04:00"
      PreferredMaintenanceWindow: "sun:04:00-sun:05:00"
      DeleteAutomatedBackups: false

      # 效能洞察
      EnablePerformanceInsights: true
      PerformanceInsightsRetentionPeriod: 7

      # 監控配置
      MonitoringInterval: 60
      MonitoringRoleArn: !GetAtt RDSMonitoringRole.Arn
      EnableCloudwatchLogsExports:
        - postgresql

      # 參數群組
      DBParameterGroupName: !Ref OptimizedDBParameterGroup

  # 最佳化參數群組
  OptimizedDBParameterGroup:
    Type: AWS::RDS::DBParameterGroup
    Properties:
      Description: Optimized PostgreSQL parameters for SaaS workload
      Family: postgres15
      Parameters:
        # 記憶體配置
        shared_buffers: "{DBInstanceClassMemory/4}"        # 25% 記憶體
        effective_cache_size: "{DBInstanceClassMemory*3/4}" # 75% 記憶體
        work_mem: "16MB"                                    # 每個操作工作記憶體
        maintenance_work_mem: "256MB"                       # 維護操作記憶體

        # 連線配置
        max_connections: "200"                              # 最大連線數
        shared_preload_libraries: "pg_stat_statements"     # 預載入擴展

        # 檢查點配置
        checkpoint_completion_target: "0.9"                # 檢查點完成目標
        checkpoint_timeout: "10min"                        # 檢查點間隔
        max_wal_size: "2GB"                                # WAL 檔案大小

        # 查詢優化
        random_page_cost: "1.1"                           # SSD 隨機讀取成本
        seq_page_cost: "1.0"                              # 順序讀取成本
        effective_io_concurrency: "200"                   # I/O 併發數

        # 統計資訊
        default_statistics_target: "500"                   # 統計樣本數

        # 日誌配置
        log_min_duration_statement: "1000"                # 記錄超過 1 秒的查詢
        log_checkpoints: "on"                             # 記錄檢查點
        log_lock_waits: "on"                              # 記錄鎖等待
        log_temp_files: "0"                               # 記錄暫存檔案

        # 自動清理配置
        autovacuum: "on"
        autovacuum_max_workers: "4"
        autovacuum_naptime: "30s"
        autovacuum_vacuum_threshold: "50"
        autovacuum_analyze_threshold: "50"

  # 讀取副本
  ReadReplica:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: kyo-postgres-read-replica
      DBInstanceClass: db.r6g.large      # 較小的讀取副本
      SourceDBInstanceIdentifier: !Ref OptimizedRDSInstance
      PubliclyAccessible: false
      MultiAZ: false                     # 讀取副本不需要多 AZ

      # 效能洞察
      EnablePerformanceInsights: true
      PerformanceInsightsRetentionPeriod: 7

      # 監控
      MonitoringInterval: 60
      MonitoringRoleArn: !GetAtt RDSMonitoringRole.Arn

      Tags:
        - Key: Purpose
          Value: ReadReplica

  # RDS Proxy 配置
  RDSProxy:
    Type: AWS::RDS::DBProxy
    Properties:
      DBProxyName: kyo-postgres-proxy
      EngineFamily: POSTGRESQL
      Auth:
        - AuthScheme: SECRETS
          SecretArn: !Ref DatabaseCredentials
      RoleArn: !GetAtt RDSProxyRole.Arn
      VpcSubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      VpcSecurityGroupIds:
        - !Ref ProxySecurityGroup

      # 連線池配置
      MaxConnectionsPercent: 100
      MaxIdleConnectionsPercent: 50
      RequireTLS: true
      IdleClientTimeout: 1800            # 30 分鐘閒置超時

      # 目標群組
      TargetGroups:
        - DBInstanceIdentifiers:
            - !Ref OptimizedRDSInstance
          DBClusterIdentifiers: []
          ConnectionPoolConfig:
            MaxConnectionsPercent: 80
            MaxIdleConnectionsPercent: 20
            ConnectionBorrowTimeout: 120
            SessionPinningFilters:
              - EXCLUDE_VARIABLE_SETS

      Tags:
        - Key: Name
          Value: KyoPostgresProxy

資料庫效能監控與警報

# CloudWatch 警報配置
Resources:
  # CPU 使用率警報
  DatabaseCPUAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: RDS-HighCPU
      AlarmDescription: RDS CPU utilization is too high
      MetricName: CPUUtilization
      Namespace: AWS/RDS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 80
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: !Ref OptimizedRDSInstance
      AlarmActions:
        - !Ref SNSAlarmTopic

  # 連線數警報
  DatabaseConnectionsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: RDS-HighConnections
      AlarmDescription: RDS connection count is too high
      MetricName: DatabaseConnections
      Namespace: AWS/RDS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 160                     # 80% of max_connections
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: !Ref OptimizedRDSInstance
      AlarmActions:
        - !Ref SNSAlarmTopic

  # 讀取延遲警報
  DatabaseReadLatencyAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: RDS-HighReadLatency
      AlarmDescription: RDS read latency is too high
      MetricName: ReadLatency
      Namespace: AWS/RDS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 0.02                    # 20ms
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: !Ref OptimizedRDSInstance
      AlarmActions:
        - !Ref SNSAlarmTopic

  # 寫入延遲警報
  DatabaseWriteLatencyAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: RDS-HighWriteLatency
      AlarmDescription: RDS write latency is too high
      MetricName: WriteLatency
      Namespace: AWS/RDS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 0.05                    # 50ms
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: !Ref OptimizedRDSInstance
      AlarmActions:
        - !Ref SNSAlarmTopic

  # 磁碟佇列深度警報
  DatabaseQueueDepthAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: RDS-HighQueueDepth
      AlarmDescription: RDS disk queue depth is too high
      MetricName: DiskQueueDepth
      Namespace: AWS/RDS
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 64
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DBInstanceIdentifier
          Value: !Ref OptimizedRDSInstance
      AlarmActions:
        - !Ref SNSAlarmTopic

ElastiCache Redis 叢集最佳化

Redis 叢集效能配置

# infrastructure/elasticache-optimization.yml
Resources:
  # 優化的 Redis 叢集
  OptimizedRedisCluster:
    Type: AWS::ElastiCache::ReplicationGroup
    Properties:
      ReplicationGroupId: kyo-redis-optimized
      Description: Optimized Redis cluster for high performance caching

      # 節點配置
      NodeType: cache.r7g.large         # 記憶體最佳化實例
      NumCacheClusters: 6               # 3 主節點 + 3 副本節點
      NumNodeGroups: 3                  # 3 個分片
      ReplicasPerNodeGroup: 1           # 每個分片 1 個副本

      # 引擎配置
      Engine: redis
      EngineVersion: 7.0
      Port: 6379

      # 網路配置
      CacheSubnetGroupName: !Ref CacheSubnetGroup
      SecurityGroupIds:
        - !Ref CacheSecurityGroup
      PreferredCacheClusterAZs:
        - !Sub "${AWS::Region}a"
        - !Sub "${AWS::Region}b"
        - !Sub "${AWS::Region}c"

      # 參數群組
      CacheParameterGroupName: !Ref OptimizedRedisParameterGroup

      # 備份配置
      SnapshotRetentionLimit: 7
      SnapshotWindow: "03:00-05:00"
      PreferredMaintenanceWindow: "sun:05:00-sun:07:00"

      # 安全性
      AtRestEncryptionEnabled: true
      TransitEncryptionEnabled: true
      AuthToken: !Sub "{{resolve:secretsmanager:${RedisAuthSecret}:SecretString:token}}"

      # 自動故障切換
      AutomaticFailoverEnabled: true
      MultiAZEnabled: true

      # 通知
      NotificationTopicArn: !Ref SNSAlarmTopic

      # 日誌配置
      LogDeliveryConfigurations:
        - DestinationType: cloudwatch-logs
          DestinationDetails:
            LogGroup: !Ref RedisLogGroup
          LogFormat: json
          LogType: slow-log

  # Redis 參數群組優化
  OptimizedRedisParameterGroup:
    Type: AWS::ElastiCache::ParameterGroup
    Properties:
      CacheParameterGroupFamily: redis7.x
      Description: Optimized Redis parameters for high performance
      Properties:
        # 記憶體配置
        maxmemory-policy: allkeys-lru              # LRU 淘汰策略
        maxmemory-samples: 10                      # 樣本數量

        # 持久化配置
        save: "900 1 300 10 60 10000"             # RDB 快照策略
        rdbcompression: "yes"                      # 啟用 RDB 壓縮
        rdbchecksum: "yes"                         # 啟用 RDB 校驗

        # 網路配置
        tcp-keepalive: "300"                       # TCP keepalive
        timeout: "0"                               # 客戶端超時時間
        tcp-backlog: "511"                         # TCP 積壓佇列

        # 效能優化
        hash-max-ziplist-entries: "512"           # 雜湊表壓縮閾值
        hash-max-ziplist-value: "64"              # 雜湊值壓縮閾值
        list-max-ziplist-size: "-2"               # 列表壓縮大小
        set-max-intset-entries: "512"             # 整數集合閾值
        zset-max-ziplist-entries: "128"           # 有序集合壓縮閾值
        zset-max-ziplist-value: "64"              # 有序集合值壓縮閾值

        # 監控配置
        slowlog-log-slower-than: "10000"          # 慢查詢閾值 (微秒)
        slowlog-max-len: "128"                    # 慢查詢日誌長度

  # Redis 連線監控
  RedisConnectionsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: Redis-HighConnections
      AlarmDescription: Redis connection count is too high
      MetricName: CurrConnections
      Namespace: AWS/ElastiCache
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 300
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: CacheClusterId
          Value: !Sub "${OptimizedRedisCluster}-001"
      AlarmActions:
        - !Ref SNSAlarmTopic

  # Redis 記憶體使用率監控
  RedisMemoryAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: Redis-HighMemoryUsage
      AlarmDescription: Redis memory usage is too high
      MetricName: DatabaseMemoryUsagePercentage
      Namespace: AWS/ElastiCache
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 85
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: CacheClusterId
          Value: !Sub "${OptimizedRedisCluster}-001"
      AlarmActions:
        - !Ref SNSAlarmTopic

  # Redis CPU 使用率監控
  RedisCPUAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: Redis-HighCPUUsage
      AlarmDescription: Redis CPU usage is too high
      MetricName: CPUUtilization
      Namespace: AWS/ElastiCache
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 80
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: CacheClusterId
          Value: !Sub "${OptimizedRedisCluster}-001"
      AlarmActions:
        - !Ref SNSAlarmTopic

成本優化策略

AWS Cost Optimization 配置

# infrastructure/cost-optimization.yml
Resources:
  # Savings Plans 建議
  ComputeSavingsPlan:
    Type: AWS::SavingsPlans::SavingsPlansTemplate
    Properties:
      SavingsPlansType: Compute
      TermInYears: 1
      PaymentOption: Partial Upfront
      Commitment: 100                    # 每小時承諾金額 (USD)

  # 預留實例建議
  RDSReservedInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      # 使用預留實例可節省 30-60% 成本
      DBInstanceClass: db.r6g.xlarge
      ReservedDBInstancesOfferingId: "reserved-offering-id"

  # Spot 執行個體配置 (適用於非關鍵工作負載)
  SpotFleetConfig:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfig:
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        AllocationStrategy: diversified
        TargetCapacity: 2
        SpotPrice: "0.05"                # 最高出價
        LaunchSpecifications:
          - ImageId: ami-12345678
            InstanceType: t3.medium
            KeyName: !Ref KeyPair
            SecurityGroups:
              - GroupId: !Ref ApplicationSecurityGroup
            SubnetId: !Ref PrivateSubnet1
            UserData:
              Fn::Base64: !Sub |
                #!/bin/bash
                # 安裝應用程式

  # 自動化成本優化
  CostOptimizationLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: cost-optimization-automation
      Runtime: python3.9
      Handler: index.lambda_handler
      Role: !GetAtt CostOptimizationRole.Arn
      Timeout: 300
      Code:
        ZipFile: |
          import boto3
          import json
          from datetime import datetime, timedelta

          def lambda_handler(event, context):
              ec2 = boto3.client('ec2')
              rds = boto3.client('rds')

              # 1. 識別未使用的 EBS 卷
              volumes = ec2.describe_volumes(
                  Filters=[{'Name': 'state', 'Values': ['available']}]
              )

              unused_volumes = []
              for volume in volumes['Volumes']:
                  create_time = volume['CreateTime']
                  if (datetime.now(create_time.tzinfo) - create_time).days > 7:
                      unused_volumes.append(volume['VolumeId'])

              # 2. 識別空閒的 RDS 實例
              db_instances = rds.describe_db_instances()
              idle_instances = []

              for db in db_instances['DBInstances']:
                  # 檢查 CloudWatch 指標判斷是否空閒
                  # 這裡應該加入實際的 CloudWatch 指標檢查
                  pass

              # 3. 建議調整實例大小
              instances = ec2.describe_instances()
              recommendations = []

              for reservation in instances['Reservations']:
                  for instance in reservation['Instances']:
                      if instance['State']['Name'] == 'running':
                          # 分析 CloudWatch 指標建議新的實例類型
                          recommendations.append({
                              'InstanceId': instance['InstanceId'],
                              'CurrentType': instance['InstanceType'],
                              'RecommendedType': 'calculated-recommendation'
                          })

              return {
                  'statusCode': 200,
                  'body': json.dumps({
                      'unused_volumes': unused_volumes,
                      'idle_db_instances': idle_instances,
                      'resize_recommendations': recommendations
                  })
              }

  # 成本優化排程
  CostOptimizationSchedule:
    Type: AWS::Events::Rule
    Properties:
      Description: Weekly cost optimization analysis
      ScheduleExpression: cron(0 10 * * SUN)  # 每週日上午 10 點
      State: ENABLED
      Targets:
        - Arn: !GetAtt CostOptimizationLambda.Arn
          Id: CostOptimizationTarget

  # 成本警報
  BillingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: HighBillingAlarm
      AlarmDescription: Alert when estimated charges exceed threshold
      MetricName: EstimatedCharges
      Namespace: AWS/Billing
      Statistic: Maximum
      Period: 86400                      # 24 小時
      EvaluationPeriods: 1
      Threshold: 1000                    # USD 1000
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: Currency
          Value: USD
      AlarmActions:
        - !Ref CostAlarmTopic

今日總結

今天我們完成了全面的 AWS 雲端效能優化實作:

  1. CloudFront CDN:Lambda@Edge 智能優化、壓縮協商、地理路由
  2. Auto Scaling:預測性擴展、時間排程、自訂指標追蹤
  3. RDS 最佳化:參數優化、讀取副本、RDS Proxy 連線池
  4. ElastiCache:Redis 叢集配置、記憶體策略、效能監控
  5. 成本優化:Savings Plans、Spot 實例、自動化分析

參考資源


上一篇
Day 17: 30天部署SaaS產品到AWS-WebSocket 協作服務實作
下一篇
Day 19: 30天部署SaaS產品到AWS-雲端安全架構設計
系列文
30 天將工作室 SaaS 產品部署起來19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言