亚马逊AWS官方博客

Amazon CloudFront 跨账号异常监控实践

背景

Amazon CloudFront 是一个全球内容分发网络(CDN)服务,它可以帮助您通过全球分布式的服务器网络更快地向用户提供内容。它的主要特性包括:

  1. 低延迟和高可用性:CloudFront 利用 AWS 遍布全球的边缘节点,可以将内容快速传输到用户附近,减少延迟,提高可用性。
  2. 安全性:CloudFront 支持 HTTPS 协议,可以保护传输中的内容不被窃取或篡改。同时它还提供多种安全功能,如防 DDoS 攻击等。
  3. 静态请求加速:CloudFront 会自动缓存您的静态内容,如图片、视频、CSS 和 JavaScript 文件等,减少源站的负载压力,提高响应速度。
  4. 动态请求加速:CloudFront 可以轻松集成您的动态内容,如 API、Web 应用程序等,为您提供全面的内容分发解决方案。

当用户受到 DDoS 攻击的时候,如果没有事先配置 Amanzon WAF 和 Amazon Shield Advanced 保护,往往在短时间内会产生大量的 CloudFront 请求或者流量,从而产生大量的 CDN 费用。站在 payer 账号的角度,payer 底下有很多 linked 账号在使用 CloudFront,我们希望可以集中和智能地监控所有 linked account 的 CDN 使用情况,在异常情况下发出告警。CloudFront 账单数据有一两天的延迟,所以我们决定结合 Amazon CloudWatch 的 cross account anomaly detection 功能来实现这一目标。

Amazon CloudWatch cross account anomaly detection 介绍

从 2024 年 4 月起,Amazon CloudWatch 支持对账户间共享的指标进行异常检测。CloudWatch 异常检测功能允许您在单个监控账户中通过 CloudWatch 跨账户可观测性跟踪多个账户之间的指标行为的异常变化(需要源账号授权)。

CloudWatch 的异常检测功能将机器学习算法应用于指标的历史数据,自动创建预期值模型。这个模型会评估指标的趋势,并捕捉每小时、每日和每周的模式变化。算法会训练最近两周的指标数据,但即使没有完整的两周数据,您仍然可以为指标启用异常检测。

与传统的静态阈值方式不同,CloudWatch 异常检测可以自动发现异常行为,避免了由于阈值设置不当而漏报或误报的情况。及时发现系统异常,有助于快速定位和解决潜在问题,提高系统稳定性。

在配置异常检测时,您需要设置一个异常检测阈值 ANOMALY_DETECTION_BAND,我们会在下文详细说明。CloudWatch 将使用这个阈值和模型来确定指标值的”正常”范围。阈值设置越高,判定为正常的值范围就越大。一旦模型创建完成,CloudWatch 会持续评估并调整模型,以保证尽可能精准。这包括重新训练模型,使其能随着指标值的变化而自我调整。还包括优化针对季节性、峰值或稀疏指标的预测器模型。

您还可以在启用异常检测后排除指标的某些时间段,这些数据将不会用于训练模型。通过排除部署或其他异常事件的影响,可以确保创建最准确的模型。同时,也可以设置异常发生时自动执行操作,如发送通知或触发自动化工作流,实现自动化运维。

总的来说,CloudWatch 异常检测通过机器学习自动发现异常,能够提高系统可观测性和可靠性,同时简化了异常检测的实施和管理。无需搭建和维护独立的异常检测系统,直接使用 CloudWatch 的托管服务,降低运维成本,中国区和海外区都已经上线。

接下来我们使用一个集中账号连接另外一个源账号实现异常监控。

配置集中账号

要实现跨账号监控,先得在集中账号开通 CloudWatch 跨账号监控功能,由于我们监控的目标服务是 CloudFront,所以必须选择美东 1 区域进行配置。打开 CloudWatch 服务,点击 settings,并在 Monitoring account configuration 处点击 Configure。

对于 CloudFront 的监控,我们只需要启用 Metrics 就可以,Logs 和 Metrics 的共享在功能上是免费的,其他数据共享会产生额外的功能费。

在 source accounts 里面把需要收集数据的源账号 ID 填上。

保存配置并退出,然后点击“Resources to link accounts”来获取源账号链接信息。

如果源账号和集中账号之间是已经使用 AWS Organization 来管理,则可以使用下图左侧的连接方式。由于实验环境是独立的账号,所以我们选择右侧的“Any Account”, 并点击“Copy URL”来保存源账号的连接配置 URL 地址。

配置源账号

首先使用新的浏览器登录源账号,跳转到美东 1 区域,并打开 Link 配置的链接地址,确认配置信息无误后点解“Link”。

验证集中账号能否看到源账号的 CW 指标

配置完后大概等待 5 分钟,在集中账号的 CloudWatch 选择 CloudFront 就能看到同步过来的 CDN metrics 信息。

配置 SNS 用于接受告警信息

创建一个 topic 叫 test-alert

创建 email 订阅,登陆邮箱通过订阅服务,并记下 topic 的 ARN 地址串 arn:aws:sns:us-east-1:xxxxxxxx:test-alert,后面配置 CloudFront 监控告警需要使用到。

部署代码

1. 打开监控账号的 cloudshell,然后从 git 上拉取代码

git clone https://github.com/Rayment915/aws-cdn-anamoly-detecor.git

跨账号监控只需要用到这两个文件:

get_cross_account_distributionId.py
create_alert_from_mon_account.py 
# get_cross_account_distributionId.py
# use to list CloudFront distribution ID in source account and create alert in monitoring account

import boto3
from create_alert_from_mon_account import create_cloudwatch_alarm

def get_distribution_list(OwningAccount,next_token = None):
    cloudwatch = boto3.client('cloudwatch')
    metrics = []

    while True:
        # Call the list_metrics API with the appropriate parameters
        if next_token:
            response = cloudwatch.list_metrics(
                Namespace='AWS/CloudFront',
                NextToken=next_token,
                IncludeLinkedAccounts=True,
                OwningAccount=OwningAccount
            )
        else:
            response = cloudwatch.list_metrics(
                Namespace='AWS/CloudFront',
                IncludeLinkedAccounts=True,
                OwningAccount=OwningAccount
            )

        # Append the retrieved metrics to the list
        metrics.extend(response['Metrics'])

        # Check if there are more results to retrieve
        next_token = response.get('NextToken', None)
        if not next_token:
            break

    distribution_ids= [metric['Dimensions'][1]['Value'] for metric in metrics]
    ret=set(distribution_ids)
    return ret

if __name__ == "__main__":
    OwningAccount='YOUR_SOURCE_ACOUNT_ID'
    distributions_list=get_distribution_list(OwningAccount)

    # Modify it according to your use case
    ANOMALY_DETECTION_BAND=5 

    metric_list=['Requests','BytesDownloaded']
    sns_arn='arn:aws:sns:us-east-1:YOUR_MONITORING_ACCOUNT_ID:test-alarm'
    for distribution_id in distributions_list:
        for metric in metric_list:
            create_cloudwatch_alarm(distribution_id,metric,sns_arn,ANOMALY_DETECTION_BAND,OwningAccount)
# create_alert_from_mon_account.py 

import boto3
from botocore.exceptions import ClientError


def create_cloudwatch_alarm(distribution_id,metric,sns_arn,ANOMALY_DETECTION_BAND,account_id):
    """
    创建基于 Anomaly Detection 的 CloudWatch 告警
    """
    # 创建 CloudWatch 客户端
    ALARM_NAME = f"CloudFront-{account_id}-{distribution_id}-{metric}-Anomaly"
    cloudwatch = boto3.client('cloudwatch', region_name='us-east-1')

    try:
        # 创建异常检测模型
        response = cloudwatch.put_anomaly_detector(
            MetricName=metric,
            Namespace='AWS/CloudFront',
            Dimensions=[
                {
                    'Name': 'DistributionId',
                    'Value': distribution_id
                },
            ],
            Stat='Sum',

        )
        print(f"==== created detactor model for {ALARM_NAME}==== ")

        # 创建基于异常检测的告警
        response = cloudwatch.put_metric_alarm(
            AlarmName=ALARM_NAME,
            ComparisonOperator='GreaterThanUpperThreshold',
            TreatMissingData = "notBreaching",
            ActionsEnabled = True,
            EvaluationPeriods = 5,
            DatapointsToAlarm = 3,
            Metrics = [
                {
                    "Id": f"m_cdn_{metric}_{account_id}_{distribution_id}",
                    "MetricStat": {
                        "Metric": {
                            "Namespace": "AWS/CloudFront",
                            "MetricName": metric,
                            "Dimensions": [
                                {
                                    "Name": "Region",
                                    "Value": 'Global'
                                },
                                {
                                    "Name": "DistributionId",
                                    "Value": distribution_id
                                }
                            ]
                        },
                        "Period": 60,
                        "Stat": "Sum"
                    },
                    "AccountId": account_id,
                    "ReturnData": True
                },
                {
                    "Id": f"ad_cdn_{metric}_{account_id}_{distribution_id}",
                    "Expression": f"ANOMALY_DETECTION_BAND(m_cdn_{metric}_{account_id}_{distribution_id}, {ANOMALY_DETECTION_BAND})",
                    "Label": f"{metric} (expected)",
                    "ReturnData": True
                }
            ],
            
            ThresholdMetricId=f"ad_cdn_{metric}_{account_id}_{distribution_id}",
            AlarmActions=[sns_arn]
        )

        print(f"已创建告警: {ALARM_NAME}")
    except ClientError as e:
        print(f"创建告警失败: {e}")

2. 执行代码部署上去

两个文件需要放同一个目录,并修改 get_cross_account_distributionId.py 文件的部分信息:

  • OwningAccount,源账号的 AWS account ID
  • sns_arn,修改为实际的 SNS ARN 地址

python3 get_cross_account_distributionId.py

3. ANOMALY_DETECTION_BAND 说明

在创建异常检测告警时,我们需要指定一个参数 ANOMALY_DETECTION_BAND。这个值用于定义异常检测带的厚度。您设置的数值会乘以指标的标准差。如果使用较高的值,异常检测带就会变得更宽更厚,这样一些异常值可能被忽略。但如果使用较小的数值,就可能产生大量的误报。基本上,这个值取决于您的指标数据的特征,以及您希望异常检测带有多精确。考虑到 CDN 的流量通常会产生较大的请求数和带宽的波动,您可以先将 ANOMALY_DETECTION_BAND 设置的高一些,例如 5。然后选择几个 CloudFront 分配,观察异常检测带是否能覆盖正常的 Request 和 BytesDownloaded 波形,再根据实际情况将 BAND 调大或者调小。我们的目的是监控 CDN 流量的异常增长,所以只需要观察波峰。另外,您也不需要观察所有的 CloudFront 分配,选择几个典型的分配来评估 BAND 数值即可。由于 CloudWatch 异常检测会对模型进行持续的评估和调整,所以 ANOMALY_DETECTION_BAND 主要起到种子的作用,您不需要过于担心它的精准性。

成本预估

异常检测警报会产生以下成本(集中账号):

  1. 您在 alarm 中用到的每个指标的成本。
  2. 额外的两个警报指标成本,用于计算异常检测模型生成的上下限指标。

参考官方原文如下:

Alarms are billed based on the number of metrics per alarm. For every  anomaly detection alarm, there are three standard resolution metrics per  alarm. One is the actual metric being evaluated, the second is the  upper bound of expected behavior, and the third is the lower bound of the expected behavior.

详细请见官方计价文档

举例:

假设现在有一个集中账号和一个源账号,其中源账号有 100 个分配需要跨账号监控。这时候源账号在只共享 Logs 和 Metrics 的情况下不会产生额外的费用,集中账号会产生 100*3*2 = 600 个 metric alarms 的费用,因为每个 metric 会产生 3 个 alarm-metric 的费用,然后我们的代码监控’Requests’和’BytesDownloaded’这两个源账号的指标。

总结

通过 CloudWatch 的 cross account anomaly detection 功能,我们可以实现使用集中账号来监控多个源账号的 CloudFront metrics,通过机器学习自动地和迅速地发现异常,能够提高系统可观测性和可靠性,同时简化了异常检测的实施和管理。但是对于新创建的或者预知到有突发流量(如线上推广活动)的 CloudFront 分配,异常监控可能会产生误报,在这期间可以适当暂停异常监控告警功能,转而使用 AWS Budgets 对 Link 账号进行成本控制。

参考链接

https://aws.amazon.com/about-aws/whats-new/2024/04/amazon-cloudwatch-cross-account-anomaly-detection

https://aws.amazon.com/cn/cloudwatch/pricing

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Unified-Cross-Account-Setup.html#Unified-Cross-Account-Setup-ConfigureMonitoringAccount

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Anomaly_Detection.html

本篇作者

林进风

厦门汉为软件技术有限公司 CTO,从事 IT 行业 10 余年,在进入云计算领域之前主要从事系统集成方面的工作,曾经负责制造业 SAP ECC 和 hana 项目、医疗和金融行业 oracle RAC 数据库升级改造项目;在硬件和应用容灾以及高可用方面有相关经验。2017 年进入云计算领域投身于亚马逊云并从事 SA 工作,在计算、联网、数据库方面以及 serverless 领域学习和研究。

张振威

亚马逊云科技 APN 解决方案架构师,主要负责合作伙伴架构咨询和方案设计,同时致力于亚马逊云科技云服务在国内的应用及推广,擅长数据迁移、数据库调优和数据分析。

陈程

亚马逊云科技高级边缘产品架构师,专注于 Amazon CloudFront、AWS WAF、AWS Shield、AWS Global Accelerator、Amazon Route 53 等产品和服务。

庄颖勤

亚马逊云科技解决方案架构师,负责基于亚马逊云科技的云计算方案架构设计、咨询、实施等工作。在 DevOps、CI/CD 和容器等领域拥有丰富的技术和支持经验,致力于帮助客户实现技术创新和业务发展。

柯俊雄

亚马逊云科技解决方案架构师,专注于数据分析/容器化领域。