一、Datadog与主流云服务集成的整体思路

做云原生开发或者运维的同学,大概率都遇到过这样的场景:服务器突然CPU飙高,数据库连接池打满,或者某个API接口延迟突然上去了。这时候如果只能登录到各个云厂商的控制台里一个个看,效率极低,而且很难把分散在AWS、阿里云、腾讯云等不同平台上的资源关联起来分析。Datadog作为一个第三方可观测性平台,核心价值就在于把不同云服务的指标、日志、链路追踪统一汇聚到同一个仪表盘里,让你在一个界面上就能掌握全局。

Datadog支持的云服务商非常广泛,其中AWS和阿里云是国内开发者最常用的两大公有云平台。集成方式主要有两种:一种是基于代理模式,在目标机器上部署Datadog Agent,通过代理采集本地指标并发送到Datadog后端;另一种是基于API模式,直接通过云厂商的API拉取资源信息并推送到Datadog。两种方式可以单独使用,也可以组合使用,具体取决于你的业务场景和对实时性的要求。


二、AWS云服务集成配置与适配

2.1 AWS CloudTrail集成

AWS CloudTrail是记录所有API调用操作的服务,把它接到Datadog里之后,你可以直接在Datadog的事件流里看到谁在什么时间对哪个资源做了什么操作。这对于安全审计和变更追踪非常有用。

技术栈:Python 3.x + boto3 SDK + Datadog API

# ============================================================
# 技术栈:Python 3.x + boto3 + datadog-api-client
# 功能:从AWS CloudTrail拉取事件并转发到Datadog事件流
# ============================================================

import boto3
import json
from datetime import datetime, timedelta
from datadog_api_client.v2.api import events_api
from datadog_api_client.v2.datadog import EventsApi, EventPayloadRequest
from datadog_api_client.configuration import Configuration
from datadog_api_client.exceptions import ApiException

def get_s3_trail_log_bucket(trail_name: str) -> str:
    """
    获取CloudTrail关联的S3日志桶名称
    参数:
        trail_name: CloudTrail跟踪名称
    返回:
        S3桶名称字符串
    """
    # 创建CloudTrail客户端
    ct_client = boto3.client('cloudtrail', region_name='us-east-1')
    trails = ct_client.describe_trails(trailNamesList=[trail_name])

    for trail in trails['trailList']:
        if trail['TrailARN']:
            # 从Trail的S3BucketName字段获取桶名
            return trail.get('S3BucketName')

    return None


def fetch_trail_events(bucket_name: str, key_prefix: str) -> list:
    """
    从S3桶中读取CloudTrail日志文件并解析为事件列表
    参数:
        bucket_name: S3桶名称
        key_prefix: S3对象键前缀(通常包含日期路径)
    返回:
        解析后的事件列表
    """
    s3_client = boto3.client('s3', region_name='us-east-1')
    events = []

    # 列出指定前缀下的所有日志文件
    paginator = s3_client.get_paginator('list_objects_v2')
    for page in paginator.paginate(Bucket=bucket_name, Prefix=key_prefix):
        for obj in page.get('Contents', []):
            key = obj['Key']
            # 只处理JSON格式的日志文件
            if not key.endswith('.json'):
                continue

            # 读取文件内容并解析
            response = s3_client.get_object(Bucket=bucket_name, Key=key)
            content = response['Body'].read().decode('utf-8')
            log_data = json.loads(content)

            # 提取每条事件的详细信息
            for record in log_data.get('Records', []):
                event = {
                    'eventTime': record.get('eventTime'),
                    'eventSource': record.get('eventSource'),
                    'eventName': record.get('eventName'),
                    'userIdentity': record.get('userIdentity', {}).get('arn', 'unknown'),
                    'sourceIPAddress': record.get('sourceIPAddress'),
                    'requestParameters': record.get('requestParameters', {}),
                    'responseElements': record.get('responseElements', {}),
                    'key': key
                }
                events.append(event)

    return events


def send_event_to_datadog(event: dict) -> bool:
    """
    将单条CloudTrail事件发送到Datadog事件流
    参数:
        event: 事件字典
    返回:
        是否发送成功
    """
    # 配置Datadog API认证信息
    config = Configuration()
    config.set_default_header('DD-APPLICATION-KEY', 'YOUR_DD_APP_KEY')
    config.set_default_header('DD-API-KEY', 'YOUR_DD_API_KEY')

    api_instance = EventsApi(events_api.ApiClient(config))

    # 构建事件标题
    title = f"[{event['eventSource']}] {event['eventName']}"

    # 构建事件正文,包含关键信息
    text = (
        f"操作者: {event['userIdentity']}\n"
        f"源IP: {event['sourceIPAddress']}\n"
        f"时间: {event['eventTime']}\n"
        f"请求参数: {json.dumps(event['requestParameters'], indent=2, ensure_ascii=False)}"
    )

    # 根据事件来源设置优先级
    priority = 'normal'
    if 'Deny' in event['eventName'] or 'Delete' in event['eventName']:
        priority = 'high'

    # 构建Datadog事件请求体
    payload = EventPayloadRequest(
        title=title,
        text=text,
        priority=priority,
        source_type='aws',
        tags=[
            f"cloudtrail:source:{event['eventSource']}",
            f"cloudtrail:event:{event['eventName']}",
            "service:aws-infrastructure"
        ]
    )

    try:
        # 调用API发送事件
        api_instance.create_event(payload)
        print(f"事件发送成功: {title}")
        return True
    except ApiException as e:
        print(f"事件发送失败: {e}")
        return False


def process_cloudtrail_batch(bucket_name: str, key_prefix: str):
    """
    批量处理CloudTrail事件
    这是一个完整的处理流程示例
    """
    print(f"开始处理CloudTrail日志: {bucket_name}/{key_prefix}")

    # 第一步:拉取事件
    events = fetch_trail_events(bucket_name, key_prefix)
    print(f"共获取到 {len(events)} 条事件")

    # 第二步:逐条发送
    success_count = 0
    for event in events:
        if send_event_to_datadog(event):
            success_count += 1

    print(f"处理完成: 成功 {success_count} 条, 失败 {len(events) - success_count} 条")


if __name__ == '__main__':
    # 使用前请替换为实际的桶名和路径前缀
    BUCKET = 'my-audit-cloudtrail-bucket'
    PREFIX = 'AWSLogs/123456789012/CloudTrail/us-east-1/2024/01/15/'

    process_cloudtrail_batch(BUCKET, PREFIX)

2.2 AWS EC2实例监控

EC2实例的监控是大多数团队最先要接入的部分。通过Datadog Agent部署在EC2实例上,可以实时采集CPU利用率、内存使用量、磁盘IO、网络流量等核心指标。

# ============================================================
# 技术栈:Bash Shell
# 功能:在AWS EC2实例上一键部署Datadog Agent
# ============================================================

#!/bin/bash

# 定义Agent版本,建议锁定到稳定版本
AGENT_VERSION="7.51.0"

# 定义Datadog站点端点,中国区域使用datadoghq.com.cn
DD_SITE="datadoghq.com"

echo "========== 开始部署 Datadog Agent v${AGENT_VERSION} =========="

# 第一步:检查操作系统类型,这里以Ubuntu为例
OS_TYPE=$(cat /etc/os-release | grep -i "NAME" | awk -F= '{print $2}' | tr -d '"')
echo "检测到操作系统: ${OS_TYPE}"

if [[ "${OS_TYPE}" != *"Ubuntu"* ]]; then
    echo "警告: 此脚本仅适配Ubuntu系统,其他系统请调整安装步骤"
fi

# 第二步:更新系统包索引
echo "更新系统包索引..."
sudo apt-get update -y

# 第三步:安装前置依赖
echo "安装依赖: curl, jq..."
sudo apt-get install -y curl jq

# 第四步:创建系统用户(Datadog Agent运行用户)
echo "创建datadog用户..."
id -u dd-agent &>/dev/null || sudo useradd --system --no-create-home --shell /bin/false dd-agent

# 第五步:从Datadog官方仓库下载安装脚本
echo "下载Datadog Agent安装脚本..."
curl -sL https://s3.amazonaws.com/dd-agent-installer/datadog-agent-installer-ubuntu.sh | sudo /bin/bash

# 第六步:安装指定版本的Agent
echo "安装Datadog Agent v${AGENT_VERSION}..."
sudo apt-get install -y "datadog-agent=${AGENT_VERSION}*"

# 第七步:配置Agent主配置文件
echo "配置Agent主配置文件..."
sudo tee /etc/datadog-agent/datadog.yaml > /dev/null <<EOF
# Datadog Agent 主配置文件
site: ${DD_SITE}
api_key: YOUR_DD_API_KEY_HERE

# 日志配置
logs_enabled: true
log_level: INFO

# 网络代理配置(如有企业代理需要配置)
# proxy:
#   http: http://proxy.example.com:8080
#   https: http://proxy.example.com:8080

# 主机名设置,建议设置为EC2实例的私有IP或自定义名称
hostname: $(curl -s http://169.254.169.254/latest/meta-data/private-ipv4)

# 标签配置,用于区分不同环境的实例
tags:
  - "env:production"
  - "team:backend"
  - "region:us-east-1"
EOF

# 第八步:配置EC2专用的集成
echo "配置AWS EC2集成..."
sudo mkdir -p /etc/datadog-agent/conf.d/aws.ec2
sudo tee /etc/datadog-agent/conf.d/aws.ec2/databuilder.yaml > /dev/null <<EOF
# EC2实例集成配置
# 启用EC2实例元数据收集
instances:
  - api_version: v2        # 使用IMDSv2获取实例元数据
    collection_interval: 60  # 每60秒采集一次
    tags:
      - instance_id:#instance_id   # 实例ID标签
      - instance_type:#instance_type  # 实例规格标签
      - availability_zone:#availability_zone  # 可用区标签
    collect_ec2_instance_stats: true  # 采集实例级指标
    collect_ec2_instance_network_stats: true  # 采集网络指标
EOF

# 第九步:重启Agent服务
echo "重启Datadog Agent..."
sudo systemctl restart datadog-agent
sudo systemctl enable datadog-agent

# 第十步:验证Agent状态
echo "验证Agent运行状态..."
sleep 5
sudo systemctl status datadog-agent --no-pager | head -20

echo ""
echo "========== 部署完成! =========="
echo "Agent配置文件位于: /etc/datadog-agent/datadog.yaml"
echo "集成配置位于: /etc/datadog-agent/conf.d/"
echo "建议执行 'sudo datadog-agent status' 查看完整状态"

2.3 AWS RDS数据库监控

数据库监控是很多团队最容易忽视但又最关键的环节。AWS RDS支持将性能指标导出到CloudWatch,而Datadog可以直接从CloudWatch拉取这些指标,无需在数据库服务器上部署额外的软件。

# ============================================================
# 技术栈:Python 3.x + boto3 + datadog-api-client
# 功能:从AWS CloudWatch拉取RDS指标并推送到Datadog
# ============================================================

import boto3
import json
from datetime import datetime, timezone, timedelta
from datadog_api_client.v2.api import metrics_api
from datadog_api_client.v2.datadog import (
    MetricsApi,
    MetricPayload,
    MetricPointPayload,
    MetricUnit,
    MetricType
)
from datadog_api_client.configuration import Configuration

# CloudWatch中RDS相关的核心指标名称
RDS_METRICS = [
    {
        'metric_name': 'CPUUtilization',           # CPU使用率
        'statistic': 'Average',                      # 统计方式
        'datadog_name': 'aws.rds.cpu_utilization',  # Datadog中的指标名
        'unit': 'percent',                           # 单位
        'display_name': 'RDS CPU利用率'
    },
    {
        'metric_name': 'DatabaseConnections',       # 数据库连接数
        'statistic': 'Maximum',                      # 统计方式
        'datadog_name': 'aws.rds.database_connections',
        'unit': 'gauge',
        'display_name': 'RDS数据库连接数'
    },
    {
        'metric_name': 'ReadLatency',               # 读延迟
        'statistic': 'Average',
        'datadog_name': 'aws.rds.read_latency',
        'unit': 'millisecond',
        'display_name': 'RDS读延迟'
    },
    {
        'metric_name': 'WriteLatency',              # 写延迟
        'statistic': 'Average',
        'datadog_name': 'aws.rds.write_latency',
        'unit': 'millisecond',
        'display_name': 'RDS写延迟'
    },
    {
        'metric_name': 'FreeableMemory',            # 可用内存
        'statistic': 'Minimum',
        'datadog_name': 'aws.rds.freeable_memory',
        'unit': 'byte',
        'display_name': 'RDS可用内存'
    },
    {
        'metric_name': 'SwapUsage',                 # 交换空间使用
        'statistic': 'Maximum',
        'datadog_name': 'aws.rds.swap_usage',
        'unit': 'byte',
        'display_name': 'RDS交换空间使用'
    }
]


def get_rds_instances() -> list:
    """
    获取AWS中所有RDS实例信息
    返回:
        实例信息列表,每个实例包含标识符和标签
    """
    rds_client = boto3.client('rds', region_name='us-east-1')
    instances = []

    # 分页获取所有RDS实例
    paginator = rds_client.get_paginator('describe_db_instances')
    for page in paginator.paginate():
        for instance in page['DBInstances']:
            # 获取实例的标签信息
            tags = rds_client.list_tags_for_resource(
                ResourceName=instance['DBInstanceArn']
            )
            tag_dict = {t['Key']: t['Value'] for t in tags.get('TagList', [])}

            instances.append({
                'identifier': instance['DBInstanceIdentifier'],
                'engine': instance['Engine'],                    # 数据库引擎类型
                'engine_version': instance['EngineVersion'],     # 引擎版本
                'instance_class': instance['DBInstanceClass'],   # 实例规格
                'status': instance['DBInstanceStatus'],           # 实例状态
                'region': instance['Region'],
                'tags': tag_dict
            })

    return instances


def fetch_cloudwatch_metrics(
    db_instance_id: str,
    metric_config: dict,
    start_time: datetime,
    end_time: datetime
) -> list:
    """
    从CloudWatch获取指定RDS实例的指定指标数据
    参数:
        db_instance_id: RDS实例标识符
        metric_config: 指标配置字典
        start_time: 查询开始时间
        end_time: 查询结束时间
    返回:
        数据点列表
    """
    cw_client = boto3.client('cloudwatch', region_name='us-east-1')

    # 获取指标数据
    response = cw_client.get_metric_data(
        MetricDataQueries=[
            {
                'Id': metric_config['metric_name'],
                'MetricStat': {
                    'Metric': {
                        'Namespace': 'AWS/RDS',
                        'MetricName': metric_config['metric_name'],
                        'Dimensions': [
                            {
                                'Name': 'DBInstanceIdentifier',
                                'Value': db_instance_id
                            }
                        ]
                    },
                    'Stat': metric_config['statistic'],
                    'Period': 300  # 5分钟聚合窗口
                },
                'ReturnData': True
            }
        ],
        StartTime=start_time,
        EndTime=end_time,
        ScanBy='TimestampAscending'
    )

    # 提取时间序列数据
    data_points = []
    for result in response.get('MetricDataResults', []):
        for i, timestamp in enumerate(result.get('Timestamps', [])):
            value = result.get('Values', [])[i]
            data_points.append({
                'timestamp': int(timestamp.timestamp()),
                'value': value
            })

    return data_points


def push_metrics_to_datadog(
    metric_name: str,
    unit: str,
    data_points: list,
    tags: list
):
    """
    将指标数据推送到Datadog
    参数:
        metric_name: Datadog指标名称
        unit: 指标单位
        data_points: 数据点列表
        tags: 标签列表
    """
    # 配置Datadog API
    config = Configuration()
    config.set_default_header('DD-APPLICATION-KEY', 'YOUR_DD_APP_KEY')
    config.set_default_header('DD-API-KEY', 'YOUR_DD_API_KEY')

    api_instance = MetricsApi(metrics_api.ApiClient(config))

    # 构建指标负载
    payload = MetricPayload(
        metric=metric_name,
        points=[
            MetricPointPayload(
                host='aws-rds-monitor',    # 虚拟主机名
                host_metadata={
                    'hostname': 'aws-rds-monitor'
                },
                metric=metric_name,
                type=MetricType.GAUGE,
                tags=tags,
                unit=unit,
                datapoints=[
                    {'timestamp': dp['timestamp'], 'value': dp['value']}
                    for dp in data_points
                ]
            )
        ]
    )

    try:
        api_instance.submit_metric(payload)
        print(f"  -> 成功推送 {len(data_points)} 个数据点到 {metric_name}")
    except Exception as e:
        print(f"  -> 推送失败: {e}")


def sync_rds_metrics_to_datadog(
    time_window_minutes: int = 15,
    region: str = 'us-east-1'
):
    """
    主同步函数:将所有RDS实例的指标同步到Datadog
    参数:
        time_window_minutes: 同步的时间窗口(分钟)
        region: AWS区域
    """
    print(f"开始同步RDS指标,时间窗口: {time_window_minutes}分钟")

    # 获取RDS实例列表
    instances = get_rds_instances()
    print(f"发现 {len(instances)} 个RDS实例")

    # 计算时间范围
    end_time = datetime.now(timezone.utc)
    start_time = end_time - timedelta(minutes=time_window_minutes)

    for instance in instances:
        inst_id = instance['identifier']
        print(f"\n处理实例: {inst_id} ({instance['engine']} {instance['engine_version']})")

        # 为每个实例构建标签
        base_tags = [
            f"db_instance:{inst_id}",
            f"engine:{instance['engine']}",
            f"instance_class:{instance['instance_class']}",
            f"region:{instance['region']}"
        ]

        # 添加实例级别的标签
        for key, value in instance['tags'].items():
            base_tags.append(f"{key}:{value}")

        # 遍历每个指标进行拉取和推送
        for metric_config in RDS_METRICS:
            print(f"  采集指标: {metric_config['display_name']}")

            # 从CloudWatch拉取数据
            data_points = fetch_cloudwatch_metrics(
                db_instance_id=inst_id,
                metric_config=metric_config,
                start_time=start_time,
                end_time=end_time
            )

            if data_points:
                # 推送到Datadog
                push_metrics_to_datadog(
                    metric_name=metric_config['datadog_name'],
                    unit=metric_config['unit'],
                    data_points=data_points,
                    tags=base_tags
                )

    print("\n========== RDS指标同步完成! ==========")


if __name__ == '__main__':
    # 执行同步,每次同步过去15分钟的数据
    sync_rds_metrics_to_datadog(time_window_minutes=15, region='us-east-1')

三、阿里云云服务集成配置与适配

3.1 阿里云ECS实例监控

阿里云ECS的监控接入和AWS EC2类似,但配置方式有所不同。阿里云提供了Datadog专用的集成方案,同时也可以通过自定义脚本实现灵活的数据采集。

# ============================================================
# 技术栈:Python 3.x + aliyunsdkcore + datadog-api-client
# 功能:采集阿里云ECS实例监控指标并推送到Datadog
# ============================================================

import json
import time
from datetime import datetime, timezone
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from datadog_api_client.v2.api import metrics_api
from datadog_api_client.v2.datadog import (
    MetricsApi,
    MetricPayload,
    MetricPointPayload,
    MetricType
)
from datadog_api_client.configuration import Configuration

# 阿里云ECS核心监控指标定义
ECS_METRICS = [
    {
        'metric_name': 'CPUUtilization',          # CPU使用率(百分比)
        'datadog_name': 'aliyun.ecs.cpu_utilization',
        'unit': 'percent',
        'display_name': 'ECS CPU使用率',
        'period': 60  # 采集周期(秒)
    },
    {
        'metric_name': 'memory_usedutilization',  # 内存使用率(百分比)
        'datadog_name': 'aliyun.ecs.memory_utilization',
        'unit': 'percent',
        'display_name': 'ECS内存使用率',
        'period': 60
    },
    {
        'metric_name': 'diskusage_utilization',   # 磁盘使用率(百分比)
        'datadog_name': 'aliyun.ecs.disk_utilization',
        'unit': 'percent',
        'display_name': 'ECS磁盘使用率',
        'period': 60
    },
    {
        'metric_name': 'network_in_rate',         # 网络入流量(字节/秒)
        'datadog_name': 'aliyun.ecs.network_in_rate',
        'unit': 'byte',
        'display_name': 'ECS网络入流量',
        'period': 60
    },
    {
        'metric_name': 'network_out_rate',        # 网络出流量(字节/秒)
        'datadog_name': 'aliyun.ecs.network_out_rate',
        'unit': 'byte',
        'display_name': 'ECS网络出流量',
        'period': 60
    },
    {
        'metric_name': 'load_1m',                 # 1分钟负载
        'datadog_name': 'aliyun.ecs.load_1m',
        'unit': 'gauge',
        'display_name': 'ECS 1分钟负载',
        'period': 60
    }
]


def create_alibaba_client(region_id: str) -> AcsClient:
    """
    创建阿里云API客户端
    参数:
        region_id: 阿里云区域ID,如cn-hangzhou
    返回:
        AcsClient实例
    """
    # 请替换为实际创建的AccessKey
    ACCESS_KEY_ID = 'YOUR_ALIYUN_ACCESS_KEY_ID'
    ACCESS_KEY_SECRET = 'YOUR_ALIYUN_ACCESS_KEY_SECRET'

    client = AcsClient(
        ak=ACCESS_KEY_ID,
        secret=ACCESS_KEY_SECRET,
        region_id=region_id
    )
    return client


def get_ecs_instances(client: AcsClient, region_id: str) -> list:
    """
    获取指定区域下所有ECS实例信息
    参数:
        client: 阿里云API客户端
        region_id: 区域ID
    返回:
        ECS实例信息列表
    """
    request = CommonRequest()
    request.set_domain(f'ecs.{region_id}.aliyuncs.com')
    request.set_version('2014-05-26')
    request.set_action_name('DescribeInstances')
    request.set_accept_format('json')

    # 分页获取实例,每页100条
    instances = []
    page_number = 1
    page_size = 100

    while True:
        request.set_query_params({
            'PageNumber': str(page_number),
            'PageSize': str(page_size)
        })

        response = client.do_action_with_exception(request)
        result = json.loads(response)

        # 解析实例列表
        instance_list = result.get('Instances', {}).get('Instance', [])
        for inst in instance_list:
            # 获取实例标签
            tags = {}
            for tag in inst.get('Tags', {}).get('Tag', []):
                tags[tag['TagKey']] = tag['TagValue']

            instances.append({
                'instance_id': inst['InstanceId'],
                'instance_name': inst['InstanceName'],
                'instance_type': inst['InstanceType'],          # 实例规格
                'status': inst['Status'],                       # 实例状态
                'private_ip': inst['VpcAttributes']['PrivateIpAddress']['Ip'],
                'public_ip': inst['PublicIpAddress']['Ip'],
                'zone_id': inst['ZoneId'],                      # 可用区
                'tags': tags
            })

        # 检查是否有下一页
        total_count = result.get('TotalCount', 0)
        if page_number * page_size >= total_count:
            break
        page_number += 1

    return instances


def query_cloud_monitor_metrics(
    client: AcsClient,
    region_id: str,
    instance_id: str,
    metric_name: str,
    start_time: str,
    end_time: str,
    period: int = 60
) -> list:
    """
    从阿里云云监控查询指定实例的指标数据
    参数:
        client: 阿里云API客户端
        region_id: 区域ID
        instance_id: ECS实例ID
        metric_name: 指标名称
        start_time: 开始时间(ISO格式)
        end_time: 结束时间(ISO格式)
        period: 数据聚合周期(秒)
    返回:
        数据点列表
    """
    request = CommonRequest()
    request.set_domain(f'metrics.{region_id}.aliyuncs.com')
    request.set_version('2019-01-01')
    request.set_action_name('DescribeMetricData')
    request.set_accept_format('json')

    # 设置查询参数
    request.set_query_params({
        'Namespace': 'acs_ecs_dashboard',      # 产品命名空间
        'MetricName': metric_name,
        'Dimensions': json.dumps({
            'instanceId': instance_id
        }),
        'StartTime': start_time,
        'EndTime': end_time,
        'Period': str(period),
        'Length': '1440'  # 最大返回数据点数量
    })

    response = client.do_action_with_exception(request)
    result = json.loads(response)

    # 解析数据点
    data_points = []
    for dp in result.get('Datapoints', []):
        # 阿里云返回的时间戳是毫秒级的
        timestamp = dp.get('timestamp')
        if timestamp:
            # 转换为秒级时间戳
            ts_seconds = int(timestamp) // 1000 if timestamp > 9999999999 else int(timestamp)

            # 解析不同指标的数值字段
            value = None
            for val_key in ['Average', 'Maximum', 'Minimum', 'Value']:
                if val_key in dp:
                    value = dp[val_key]
                    break

            if value is not None:
                data_points.append({
                    'timestamp': ts_seconds,
                    'value': float(value)
                })

    return data_points


def build_datadog_tags(instance: dict) -> list:
    """
    为ECS实例构建Datadog标签
    标签用于在Datadog中进行分组、过滤和聚合查询
    参数:
        instance: 实例信息字典
    返回:
        标签字符串列表
    """
    tags = [
        f"instance_id:{instance['instance_id']}",
        f"instance_name:{instance['instance_name']}",
        f"instance_type:{instance['instance_type']}",
        f"zone:{instance['zone_id']}",
        f"env:production"
    ]

    # 添加实例级别的自定义标签
    for key, value in instance['tags'].items():
        # 标签键名中的特殊字符需要替换
        safe_key = key.replace(' ', '_').replace('-', '_')
        tags.append(f"{safe_key}:{value}")

    return tags


def push_ecs_metrics_to_datadog(
    metric_name: str,
    unit: str,
    data_points: list,
    tags: list,
    instance_name: str
):
    """
    将ECS指标数据推送到Datadog
    参数:
        metric_name: Datadog指标名
        unit: 单位
        data_points: 数据点列表
        tags: 标签列表
        instance_name: 实例名称(用于日志显示)
    """
    config = Configuration()
    config.set_default_header('DD-APPLICATION-KEY', 'YOUR_DD_APP_KEY')
    config.set_default_header('DD-API-KEY', 'YOUR_DD_API_KEY')

    api_instance = MetricsApi(metrics_api.ApiClient(config))

    # 构建指标负载,每个数据点都包含完整的标签信息
    payload = MetricPayload(
        metric=metric_name,
        points=[
            MetricPointPayload(
                host=f'aliyun-ecs-{instance_name}',
                host_metadata={
                    'hostname': f'aliyun-ecs-{instance_name}'
                },
                metric=metric_name,
                type=MetricType.GAUGE,
                tags=tags,
                unit=unit,
                datapoints=[
                    {'timestamp': dp['timestamp'], 'value': dp['value']}
                    for dp in data_points
                ]
            )
        ]
    )

    try:
        api_instance.submit_metric(payload)
        print(f"    ✓ 推送 {len(data_points)} 个数据点 → {metric_name}")
    except Exception as e:
        print(f"    ✗ 推送失败: {e}")


def sync_alibaba_ecs_metrics(region_id: str = 'cn-hangzhou', window_minutes: int = 30):
    """
    主同步函数:同步阿里云ECS监控指标到Datadog
    参数:
        region_id: 阿里云区域
        window_minutes: 同步时间窗口(分钟)
    """
    print(f"╔══════════════════════════════════════════╗")
    print(f"║  阿里云ECS指标同步 → Datadog           ║")
    print(f"╚══════════════════════════════════════════╝")
    print(f"区域: {region_id}, 时间窗口: {window_minutes}分钟\n")

    # 创建API客户端
    client = create_alibaba_client(region_id)

    # 计算时间范围
    end_dt = datetime.now(timezone.utc)
    start_dt = end_dt - timedelta(minutes=window_minutes)
    start_str = start_dt.strftime('%Y-%m-%dT%H:%M:%SZ')
    end_str = end_dt.strftime('%Y-%m-%dT%H:%M:%SZ')

    # 获取所有ECS实例
    instances = get_ecs_instances(client, region_id)
    print(f"发现 {len(instances)} 个ECS实例")

    # 遍历每个实例的每个指标进行同步
    for instance in instances:
        inst_id = instance['instance_id']
        inst_name = instance['instance_name']
        print(f"\n▶ 处理实例: {inst_name} ({inst_id})")

        # 构建该实例的标签
        tags = build_datadog_tags(instance)

        # 遍历所有指标进行采集
        for metric_config in ECS_METRICS:
            data_points = query_cloud_monitor_metrics(
                client=client,
                region_id=region_id,
                instance_id=inst_id,
                metric_name=metric_config['metric_name'],
                start_time=start_str,
                end_time=end_str,
                period=metric_config['period']
            )

            if data_points:
                push_ecs_metrics_to_datadog(
                    metric_name=metric_config['datadog_name'],
                    unit=metric_config['unit'],
                    data_points=data_points,
                    tags=tags,
                    instance_name=inst_name
                )

    print("\n========== 同步完成! ==========")


if __name__ == '__main__':
    from datetime import timedelta

    # 同步过去30分钟的ECS指标数据
    sync_alibaba_ecs_metrics(region_id='cn-hangzhou', window_minutes=30)

3.2 阿里云RDS数据库监控

阿里云RDS提供了多种数据库引擎(MySQL、PostgreSQL、SQL Server等),监控方式与AWS RDS类似但API接口有所不同。

# ============================================================
# 技术栈:Python 3.x + aliyunsdkcore + datadog-api-client
# 功能:同步阿里云RDS数据库指标到Datadog
# ============================================================

import json
from datetime import datetime, timezone, timedelta
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from datadog_api_client.v2.api import metrics_api
from datadog_api_client.v2.datadog import (
    MetricsApi, MetricPayload, MetricPointPayload, MetricType
)
from datadog_api_client.configuration import Configuration

# RDS各引擎对应的云监控命名空间
RDS_NAMESPACE_MAP = {
    'MySQL': 'acs_rds_dashboard',
    'PostgreSQL': 'acs_rds_dashboard',
    'SQLServer': 'acs_rds_dashboard',
    'MariaDB': 'acs_rds_dashboard'
}

# RDS核心监控指标
RDS_METRIC_LIST = [
    {
        'metric_name': 'CPUUsage',                  # CPU使用率
        'datadog_name': 'aliyun.rds.cpu_usage',
        'unit': 'percent'
    },
    {
        'metric_name': 'MemoryUsage',               # 内存使用率
        'datadog_name': 'aliyun.rds.memory_usage',
        'unit': 'percent'
    },
    {
        'metric_name': 'DiskUsage',                 # 磁盘使用率
        'datadog_name': 'aliyun.rds.disk_usage',
        'unit': 'percent'
    },
    {
        'metric_name': 'IOPSUsage',                 # IOPS使用率
        'datadog_name': 'aliyun.rds.iops_usage',
        'unit': 'percent'
    },
    {
        'metric_name': 'ConnectionUsage',           # 连接数使用率
        'datadog_name': 'aliyun.rds.connection_usage',
        'unit': 'percent'
    },
    {
        'metric_name': 'ReadThroughput',            # 读吞吐量
        'datadog_name': 'aliyun.rds.read_throughput',
        'unit': 'byte'
    },
    {
        'metric_name': 'WriteThroughput',           # 写吞吐量
        'datadog_name': 'aliyun.rds.write_throughput',
        'unit': 'byte'
    },
    {
        'metric_name': 'ActiveSessions',            # 活跃会话数
        'datadog_name': 'aliyun.rds.active_sessions',
        'unit': 'gauge'
    }
]


def get_rds_instances(client: AcsClient, region_id: str) -> list:
    """
    获取指定区域下所有RDS实例
    """
    request = CommonRequest()
    request.set_domain(f'rds.{region_id}.aliyuncs.com')
    request.set_version('2014-08-15')
    request.set_action_name('DescribeDBInstances')
    request.set_accept_format('json')

    instances = []
    page_number = 1

    while True:
        request.set_query_params({
            'PageNumber': str(page_number),
            'PageSize': '100'
        })

        response = client.do_action_with_exception(request)
        result = json.loads(response)

        for inst in result.get('Items', {}).get('DBInstance', []):
            instances.append({
                'instance_id': inst['DBInstanceId'],
                'instance_name': inst.get('DBInstanceDescription', inst['DBInstanceId']),
                'engine': inst['Engine'],
                'engine_version': inst['EngineVersion'],
                'instance_type': inst['DBInstanceType'],
                'status': inst['DBInstanceStatus'],
                'region': region_id,
                'zone_id': inst.get('ZoneId', '')
            })

        total = result.get('TotalRecordCount', 0)
        if page_number * 100 >= total:
            break
        page_number += 1

    return instances


def fetch_rds_metric(
    client: AcsClient,
    region_id: str,
    instance_id: str,
    metric_name: str,
    start_time: str,
    end_time: str
) -> list:
    """
    查询RDS指定指标数据
    """
    request = CommonRequest()
    request.set_domain(f'metrics.{region_id}.aliyuncs.com')
    request.set_version('2019-01-01')
    request.set_action_name('DescribeMetricData')
    request.set_accept_format('json')

    request.set_query_params({
        'Namespace': 'acs_rds_dashboard',
        'MetricName': metric_name,
        'Dimensions': json.dumps({'instanceId': instance_id}),
        'StartTime': start_time,
        'EndTime': end_time,
        'Period': '60',
        'Length': '1440'
    })

    response = client.do_action_with_exception(request)
    result = json.loads(response)

    data_points = []
    for dp in result.get('Datapoints', []):
        ts = int(dp.get('timestamp', 0))
        # 判断是毫秒还是秒级时间戳
        ts_seconds = ts // 1000 if ts > 9999999999 else ts

        value = None
        for val_key in ['Average', 'Maximum', 'Minimum', 'Value']:
            if val_key in dp:
                value = float(dp[val_key])
                break

        if value is not None:
            data_points.append({'timestamp': ts_seconds, 'value': value})

    return data_points


def push_to_datadog(metric_name: str, unit: str, data_points: list, tags: list, host: str):
    """推送指标到Datadog"""
    config = Configuration()
    config.set_default_header('DD-APPLICATION-KEY', 'YOUR_DD_APP_KEY')
    config.set_default_header('DD-API-KEY', 'YOUR_DD_API_KEY')

    api = MetricsApi(metrics_api.ApiClient(config))
    payload = MetricPayload(
        metric=metric_name,
        points=[MetricPointPayload(
            host=host,
            host_metadata={'hostname': host},
            metric=metric_name,
            type=MetricType.GAUGE,
            tags=tags,
            unit=unit,
            datapoints=[{'timestamp': dp['timestamp'], 'value': dp['value']} for dp in data_points]
        )]
    )

    try:
        api.submit_metric(payload)
    except Exception as e:
        print(f"  推送失败: {e}")


def sync_rds(region_id: str = 'cn-hangzhou', window_minutes: int = 30):
    """同步阿里云RDS指标到Datadog"""
    ACCESS_KEY_ID = 'YOUR_ALIYUN_ACCESS_KEY_ID'
    ACCESS_KEY_SECRET = 'YOUR_ALIYUN_ACCESS_KEY_SECRET'

    client = AcsClient(ak=ACCESS_KEY_ID, secret=ACCESS_KEY_SECRET, region_id=region_id)

    end_dt = datetime.now(timezone.utc)
    start_dt = end_dt - timedelta(minutes=window_minutes)
    start_str = start_dt.strftime('%Y-%m-%dT%H:%M:%SZ')
    end_str = end_dt.strftime('%Y-%m-%dT%H:%M:%SZ')

    instances = get_rds_instances(client, region_id)
    print(f"发现 {len(instances)} 个RDS实例\n")

    for inst in instances:
        inst_id = inst['instance_id']
        tags = [
            f"db_instance:{inst_id}",
            f"engine:{inst['engine']}",
            f"engine_version:{inst['engine_version']}",
            f"region:{region_id}"
        ]
        host = f"aliyun-rds-{inst['instance_name']}"

        print(f"▶ {inst['instance_name']} ({inst['engine']} {inst['engine_version']})")

        for metric in RDS_METRIC_LIST:
            data_points = fetch_rds_metric(
                client, region_id, inst_id,
                metric['metric_name'], start_str, end_str
            )
            if data_points:
                push_to_datadog(
                    metric['datadog_name'], metric['unit'],
                    data_points, tags, host
                )
                print(f"  ✓ {metric['metric_name']}: {len(data_points)} 个数据点")

    print("\n========== RDS同步完成! ==========")


if __name__ == '__main__':
    sync_rds(region_id='cn-hangzhou', window_minutes=30)

3.3 阿里云OSS对象存储监控

OSS对象存储在CDN回源、数据归档等场景中扮演着重要角色。监控OSS的存储容量和请求量可以帮助及时发现异常访问模式。

# ============================================================
# 技术栈:Python 3.x + aliyunsdkcore + datadog-api-client
# 功能:采集阿里云OSS存储桶监控指标
# ============================================================

import json
from datetime import datetime, timezone, timedelta
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from datadog_api_client.v2.api import metrics_api
from datadog_api_client.v2.datadog import (
    MetricsApi, MetricPayload, MetricPointPayload, MetricType
)
from datadog_api_client.configuration import Configuration

# OSS监控指标定义
OSS_METRICS = [
    {
        'metric_name': 'StorageUtilization',        # 存储容量
        'datadog_name': 'aliyun.oss.storage_utilization',
        'unit': 'byte',
        'display': '存储容量'
    },
    {
        'metric_name': 'GetObjectCount',            # GET请求数
        'datadog_name': 'aliyun.oss.get_object_count',
        'unit': 'count',
        'display': 'GET请求数'
    },
    {
        'metric_name': 'PutObjectCount',            # PUT请求数
        'datadog_name': 'aliyun.oss.put_object_count',
        'unit': 'count',
        'display': 'PUT请求数'
    },
    {
        'metric_name': 'InternetSend',              # 公网流出流量
        'datadog_name': 'aliyun.oss.internet_send',
        'unit': 'byte',
        'display': '公网流出流量'
    },
    {
        'metric_name': 'InternetRecv',              # 公网流入流量
        'datadog_name': 'aliyun.oss.internet_recv',
        'unit': 'byte',
        'display': '公网流入流量'
    }
]


def get_oss_buckets(client: AcsClient, region_id: str) -> list:
    """获取指定区域下所有OSS存储桶"""
    request = CommonRequest()
    request.set_domain(f'oss-{region_id}.aliyuncs.com')
    request.set_version('2019-05-17')
    request.set_action_name('ListBuckets')
    request.set_accept_format('json')

    response = client.do_action_with_exception(request)
    result = json.loads(response)

    buckets = []
    for bucket in result.get('Buckets', {}).get('Bucket', []):
        buckets.append({
            'name': bucket['Name'],
            'region': bucket['Region'],
            'creation_date': bucket['CreationDate']
        })

    return buckets


def fetch_oss_metrics(
    client: AcsClient,
    region_id: str,
    bucket_name: str,
    metric_name: str,
    start_str: str,
    end_str: str
) -> list:
    """查询OSS指定存储桶的指标数据"""
    request = CommonRequest()
    request.set_domain(f'metrics.{region_id}.aliyuncs.com')
    request.set_version('2019-01-01')
    request.set_action_name('DescribeMetricData')
    request.set_accept_format('json')

    request.set_query_params({
        'Namespace': 'acs_oss_dashboard',
        'MetricName': metric_name,
        'Dimensions': json.dumps({'BucketName': bucket_name}),
        'StartTime': start_str,
        'EndTime': end_str,
        'Period': '60',
        'Length': '1440'
    })

    response = client.do_action_with_exception(request)
    result = json.loads(response)

    data_points = []
    for dp in result.get('Datapoints', []):
        ts = int(dp.get('timestamp', 0))
        ts_seconds = ts // 1000 if ts > 9999999999 else ts

        value = None
        for k in ['Average', 'Maximum', 'Minimum', 'Value']:
            if k in dp:
                value = float(dp[k])
                break

        if value is not None:
            data_points.append({'timestamp': ts_seconds, 'value': value})

    return data_points


def push_to_datadog(metric_name: str, unit: str, data_points: list, tags: list, host: str):
    """推送指标到Datadog"""
    config = Configuration()
    config.set_default_header('DD-APPLICATION-KEY', 'YOUR_DD_APP_KEY')
    config.set_default_header('DD-API-KEY', 'YOUR_DD_API_KEY')

    api = MetricsApi(metrics_api.ApiClient(config))
    payload = MetricPayload(
        metric=metric_name,
        points=[MetricPointPayload(
            host=host,
            host_metadata={'hostname': host},
            metric=metric_name,
            type=MetricType.GAUGE,
            tags=tags,
            unit=unit,
            datapoints=[{'timestamp': dp['timestamp'], 'value': dp['value']} for dp in data_points]
        )]
    )

    try:
        api.submit_metric(payload)
    except Exception as e:
        print(f"  推送失败: {e}")


def sync_oss_metrics(region_id: str = 'cn-hangzhou', window_minutes: int = 30):
    """同步OSS指标到Datadog"""
    ACCESS_KEY_ID = 'YOUR_ALIYUN_ACCESS_KEY_ID'
    ACCESS_KEY_SECRET = 'YOUR_ALIYUN_ACCESS_KEY_SECRET'

    client = AcsClient(ak=ACCESS_KEY_ID, secret=ACCESS_KEY_SECRET, region_id=region_id)

    end_dt = datetime.now(timezone.utc)
    start_dt = end_dt - timedelta(minutes=window_minutes)
    start_str = start_dt.strftime('%Y-%m-%dT%H:%M:%SZ')
    end_str = end_dt.strftime('%Y-%m-%dT%H:%M:%SZ')

    buckets = get_oss_buckets(client, region_id)
    print(f"发现 {len(buckets)} 个OSS存储桶\n")

    for bucket in buckets:
        bucket_name = bucket['name']
        tags = [
            f"bucket:{bucket_name}",
            f"region:{bucket['region']}",
            "service:object-storage"
        ]
        host = f"aliyun-oss-{bucket_name}"

        print(f"▶ {bucket_name}")
        for metric in OSS_METRICS:
            data_points = fetch_oss_metrics(
                client, region_id, bucket_name,
                metric['metric_name'], start_str, end_str
            )
            if data_points:
                push_to_datadog(
                    metric['datadog_name'], metric['unit'],
                    data_points, tags, host
                )
                print(f"  ✓ {metric['display']}: {len(data_points)} 个数据点")

    print("\n========== OSS同步完成! ==========")


if __name__ == '__main__':
    sync_oss_metrics(region_id='cn-hangzhou', window_minutes=30)

四、Datadog自定义指标与告警配置

4.1 自定义指标上报

当云服务提供的标准指标无法满足需求时,可以通过自定义指标的方式把业务相关的数值(比如订单处理耗时、队列积压数等)推送到Datadog,这样就能在统一的监控平台上进行关联分析。

# ============================================================
# 技术栈:Python 3.x + DogStatsD
# 功能:使用DogStatsD协议上报自定义业务指标到Datadog
# ============================================================

from dogstatsd import DogStatsd
import time
import random

# 初始化DogStatsD客户端
# DD_HOST 为Datadog Agent所在地址,默认同机部署用localhost
# DD_PORT 为Agent监听的DogStatsD端口,默认8125
STATSD = DogStatsd(host='localhost', port=8125, namespace='custom', prefix='myapp')

# 定义业务相关的指标采集配置
BUSINESS_METRICS = [
    {
        'name': 'order_processing_time',       # 订单处理耗时
        'type': 'distribution',                 # 分布类型,支持percentile计算
        'unit': 'millisecond',
        'description': '订单从接收到完成的总耗时'
    },
    {
        'name': 'queue_backlog',               # 队列积压数量
        'type': 'gauge',
        'unit': 'count',
        'description': '当前待处理任务数量'
    },
    {
        'name': 'api_error_count',             # API错误计数
        'type': 'counter',
        'unit': 'count',
        'description': 'API调用失败次数'
    }
]


def collect_order_processing_time() -> float:
    """
    模拟采集订单处理耗时
    在实际项目中应替换为真实的业务逻辑耗时统计
    返回:
        耗时(毫秒)
    """
    # 模拟:基础耗时50ms + 随机抖动0~200ms
    base_time = 50
    jitter = random.uniform(0, 200)
    return base_time + jitter


def collect_queue_backlog() -> int:
    """
    模拟采集队列积压数量
    返回:
        当前积压任务数
    """
    # 模拟:正常0~50之间波动,偶尔出现高峰
    normal_range = random.randint(0, 50)
    if random.random() < 0.05:  # 5%概率出现积压高峰
        return normal_range + random.randint(100, 500)
    return normal_range


def collect_api_error_count() -> int:
    """
    模拟采集API错误数量
    返回:
        当前周期内的错误计数
    """
    # 模拟:大部分时间0~5个错误,偶发批量错误
    if random.random() < 0.1:
        return random.randint(10, 50)
    return random.randint(0, 5)


def report_custom_metrics(
    tags: dict = None,
    constant_tags: dict = None
):
    """
    上报所有自定义业务指标
    参数:
        tags: 本次上报的临时标签(如请求ID等)
        constant_tags: 每次上报都包含的固定标签
    """
    # 合并标签
    all_tags = {}
    if constant_tags:
        all_tags.update(constant_tags)
    if tags:
        all_tags.update(tags)

    tag_string = ';'.join([f"{k}:{v}" for k, v in all_tags.items()])

    print(f"[指标上报] 标签: {tag_string}")
    print(f"[指标上报] 时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")

    # 1. 订单处理耗时 - 使用distribution类型,Datadog可以计算P50/P95/P99
    order_time = collect_order_processing_time()
    STATSD.distribution(
        'order_processing_time',
        value=order_time,
        tags=tag_string.split(';') if tag_string else None
    )
    print(f"  订单处理耗时: {order_time:.1f}ms")

    # 2. 队列积压数量 - 使用gauge类型,表示瞬时值
    backlog = collect_queue_backlog()
    STATSD.gauge(
        'queue_backlog',
        value=backlog,
        tags=tag_string.split(';') if tag_string else None
    )
    print(f"  队列积压: {backlog} 条")

    # 3. API错误计数 - 使用counter类型,Datadog会做速率计算
    error_count = collect_api_error_count()
    STATSD.count(
        'api_error_count',
        value=error_count,
        tags=tag_string.split(';') if tag_string else None
    )
    print(f"  API错误数: {error_count} 次")

    # 4. 额外上报一个直方图指标:请求响应码分布
    # 模拟不同状态码的出现次数
    status_codes = {200: 90, 400: 5, 500: 3}
    for code, count in status_codes.items():
        STATSD.increment(
            'api.response_code',
            value=count,
            tags=tag_string.split(';') + [f'status:{code}'] if tag_string else [f'status:{code}']
        )
    print(f"  HTTP状态码分布: {status_codes}")

    print()


if __name__ == '__main__':
    # 定义固定标签,每次上报都会携带
    CONSTANT_TAGS = {
        'env': 'production',
        'service': 'order-service',
        'version': '2.1.0'
    }

    print("========== 开始自定义指标上报演示 ==========\n")

    # 模拟多次上报,观察指标变化趋势
    for i in range(5):
        # 每次上报带一个临时的请求批次标签
        temp_tags = {'batch': str(i + 1)}
        report_custom_metrics(tags=temp_tags, constant_tags=CONSTANT_TAGS)

        # 每1秒上报一次,模拟真实生产环境的采集频率
        time.sleep(1)

    print("========== 上报演示完成 ==========")
    print("提示: 在Datadog控制台中搜索 'myapp.custom.' 前缀查看所有自定义指标")

4.2 智能告警规则配置

告警是监控体系的最后一道防线。配置合理的告警规则可以避免噪声干扰,同时确保关键问题不被遗漏。

{
  "_comment_1": "=============================================",
  "_comment_2": "技术栈:Datadog Monitor API (JSON配置)",
  "_comment_3": "功能:定义Datadog智能告警规则配置模板",
  "_comment_4": "=============================================",

  "monitors": [
    {
      "type": "metric",
      "name": "AWS RDS CPU使用率过高告警",
      "message": [
        "@slack-devops 检测到RDS实例 **{{host}}** 的CPU使用率持续超过阈值。",
        "",
        "**当前值**: {{value}}%",
        "**阈值**: >85% 持续5分钟",
        "**影响范围**: 数据库服务可能响应变慢",
        "",
        "建议操作:",
        "1. 检查是否有慢查询或未优化的SQL",
        "2. 查看连接数是否异常升高",
        "3. 如为突发流量,考虑临时升级实例规格",
        "4. 使用 Datadog SQL Explorer 分析查询性能"
      ],
      "priority": "high",
      "tags": [
        "team:backend",
        "service:rds",
        "cloud:aws",
        "env:production"
      ],
      "query": "avg(last_5m):avg:aws.rds.cpu_utilization{cloud:aws} by {db_instance} > 85",
      "message_tags": [
        "db_instance",
        "region"
      ],
      "notify_audit": true,
      "no_data_timeframe": 30,
      "renotify_interval": 3600,
      "evaluation_delay": 30,
      "new_group_delay": 300,
      "notify_no_data": false,
      "no_data_timeframe": 30,
      "groupby_simple_monitor": false,
      "include_tags": false,
      "multi_alert_type": "alert"
    },
    {
      "type": "metric",
      "name": "阿里云ECS内存使用率告警",
      "message": [
        "阿里云ECS实例 **{{host}}** 内存使用率达到 **{{value}}%**。",
        "",
        "超过80%持续10分钟触发告警。",
        "",
        "**排查步骤**:",
        "1. `top` 命令查看内存占用最大的进程",
        "2. 检查是否有内存泄漏",
        "3. 查看Datadog的 `processes` 面板获取进程级详情",
        "4. 必要时执行内存清理或重启服务"
      ],
      "priority": "normal",
      "tags": [
        "team:infra",
        "service:ecs",
        "cloud:aliyun",
        "env:production"
      ],
      "query": "avg(last_10m):avg:aliyun.ecs.memory_utilization{cloud:aliyun} by {instance_id} > 80",
      "notify_audit": true,
      "no_data_timeframe": 30,
      "renotify_interval": 1800,
      "evaluation_delay": 60,
      "new_group_delay": 300
    },
    {
      "type": "metric",
      "name": "API错误率突增告警",
      "message": [
        "⚠️ **API错误率异常**",
        "",
        "**错误率**: {{value}}%",
        "**阈值**: >5% 持续3分钟",
        "**服务**: order-service",
        "",
        "**建议**:",
        "1. 查看最近的部署记录,确认是否有新版本上线",
        "2. 检查Datadog APM中的异常链路",
        "3. 查看依赖服务(数据库/缓存)的健康状态",
        "4. 如确认是bug,立即回滚到上一版本"
      ],
      "priority": "urgent",
      "tags": [
        "team:backend",
        "service:api-gateway",
        "env:production"
      ],
      "query": "avg(last_3m):rate:myapp.custom.api_error_count{service:order-service}[1m] / avg(last_3m):rate:myapp.custom.api_total_count{service:order-service}[1m] > 0.05",
      "notify_audit": true,
      "no_data_timeframe": 5,
      "renotify_interval": 1800,
      "evaluation_delay": 30
    },
    {
      "type": "composite",
      "name": "多指标复合告警 - 数据库全面异常",
      "message": [
        "🚨 **数据库异常 - 复合告警触发**",
        "",
        "以下多个指标同时异常,可能是数据库发生了严重问题:",
        "",
        "- CPU使用率超过90%",
        "- 活跃连接数超过最大值80%",
        "- 读延迟超过500ms",
        "",
        "**紧急操作**:",
        "1. 立即执行数据库主从切换",
        "2. 通知DBA团队介入",
        "3. 暂停非关键业务对数据库的写入操作",
        "4. 保留现场用于事后分析"
      ],
      "priority": "urgent",
      "tags": [
        "team:dba",
        "env:production",
        "severity:critical"
      ],
      "query": "avg(last_5m):avg:aws.rds.cpu_utilization > 90 AND avg(last_5m):max:aws.rds.active_sessions > 200 AND avg(last_5m):avg:aws.rds.read_latency > 500",
      "evaluate": "avg(last_5m):avg:aws.rds.cpu_utilization > 90 AND avg(last_5m):max:aws.rds.active_sessions > 200 AND avg(last_5m):avg:aws.rds.read_latency > 500",
      "renotify_interval": 3600
    }
  ]
}

五、应用场景分析

5.1 跨云统一管理场景

在大型企业中,很多团队同时使用多个云平台。比如核心业务部署在AWS上,数据分析和AI训练在阿里云上,而灾备系统在腾讯云上。这时候最头疼的问题就是:没有一个统一的界面能看到全局健康状况。把三个云平台的指标都接入Datadog之后,可以在一个Dashboard里同时监控所有资源,配置一套统一的告警规则,不需要分别登录三个控制台。

举个例子,假设电商大促期间订单处理链路的数据库在阿里云RDS上,缓存层在AWS ElastiCache上,应用层在阿里云ECS上。当数据库CPU飙升时,通过Datadog的全链路追踪可以迅速定位到是哪个API导致的慢查询,而不用在两个云厂商的控制台之间来回切换。

5.2 合规审计场景

金融、医疗等强监管行业要求所有资源变更必须留痕。通过CloudTrail和阿里云操作审计的集成,所有API调用操作都会被记录并推送到Datadog事件流。审计人员可以直接在Datadog中按时间、操作者、资源类型进行筛选查询,生成合规报告。

5.3 成本优化场景

通过Datadog的Cost Analysis功能,可以将AWS和阿里云的资源用量与账单数据关联。比如发现某个ECS实例70%的时间CPU使用率低于5%,就可以考虑降配。同样,发现某个RDS实例的IOPS使用率长期低于10%,也意味着存在优化空间。

5.4 自动化运维场景

结合Datadog的Webhook告警和自动化平台(如Jenkins、AWS Lambda),可以实现告警触发后自动执行运维操作。比如RDS连接数超过阈值时,自动触发连接池扩容;ECS磁盘使用率超过90%时,自动创建快照并发送通知。


六、技术优缺点分析

6.1 优势

统一的监控视角:Datadog最大的价值在于把分散在不同云平台的指标汇聚到同一个地方。不管你的资源是AWS EC2还是阿里云ECS,只要接入了Datadog,就能在同一个Dashboard里对比查看,这比分别登录各个云厂商控制台要高效得多。

丰富的可视化能力:Datadog支持多种图表类型,包括时序图、仪表盘、热力图、拓扑图等。开发者可以根据自己的需求自由组合Widget,搭建出适合团队监控需求的Dashboard。同时支持Notebook式的分析,可以结合查询语句写文档式的说明,方便知识沉淀。

智能告警降噪:Datadog提供了基于机器学习的异常检测能力。传统的告警阈值是固定值,而智能阈值可以根据历史数据自动调整。比如某指标的流量有明显的日内波动规律,传统阈值要么设得太高导致漏报,要么设得太低导致误报。Datadog的Forecast功能可以预测未来一段时间的正常范围,只有真正异常的数据才会触发告警。

完善的权限管理:Datadog支持RBAC(基于角色的访问控制),可以按团队、项目、环境粒度设置访问权限。不同角色的用户能看到的数据范围不同,比如开发团队只能看到自己负责的服务指标,而运维团队可以看到全部资源。

6.2 劣势

成本较高:Datadog采用按主机+按用量计费的模式。对于大规模集群(比如上千台ECS实例),费用会相当可观。特别是日志和APM(应用性能监控)功能,日志量大的场景下费用增长非常迅速。很多团队在使用半年后发现账单远超预期,不得不精简监控范围。

数据延迟:通过API拉取模式采集的指标存在一定的延迟,通常是5到15分钟。对于需要秒级监控的场景,需要依赖Agent推送模式。而Agent模式又需要在每台机器上部署软件,增加了运维复杂度。

学习曲线:Datadog的功能非常丰富,这也意味着上手门槛相对较高。特别是复杂查询语句的编写、Dashboard的定制化搭建、告警规则的精调等,都需要一定的学习和实践经验。对于刚接触可观测性领域的开发者来说,可能会感到 overwhelmed。

供应商锁定:一旦深度依赖Datadog的生态(比如大量使用Datadog特有的功能如Forecast、Watchdog等),迁移到其他监控平台的成本会非常高。Dashboard配置、告警规则、APM数据格式等都需要重新适配。

6.3 与竞品对比

对比维度 Datadog 阿里云ARMS AWS CloudWatch
跨云支持 极强,支持所有主流云 仅深度支持阿里云 仅深度支持AWS
可视化能力 非常丰富,高度可定制 中等,模板化为主 中等,偏基础
APM能力 业界领先 较完善 基础
告警智能化 机器学习驱动 规则+阈值为主 阈值告警
成本 高 中等 中等
学习门槛 中高 低 低

七、注意事项

7.1 API限流问题

不管是AWS API还是阿里云API,都有调用频率限制。在批量同步指标时,需要注意控制请求速率。建议采用指数退避策略,当遇到429状态码时自动降低频率并重试。

# ============================================================
# 技术栈:Python 3.x
# 功能:实现带指数退避的API调用封装
# ============================================================

import time
import random
from functools import wraps

def retry_with_backoff(max_retries: int = 5, base_delay: float = 1.0, max_delay: float = 60.0):
    """
    指数退避重试装饰器
    当API调用失败或遇到限流时,自动按指数退避策略重试

    参数:
        max_retries: 最大重试次数
        base_delay: 初始等待时间(秒)
        max_delay: 最大等待时间(秒)
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e

                    # 判断是否是限流错误
                    is_rate_limited = (
                        'Throttling' in str(e) or
                        'RateLimitExceeded' in str(e) or
                        'TooManyRequests' in str(e) or
                        '429' in str(e)
                    )

                    if is_rate_limited:
                        # 指数退避:等待时间 = base_delay * 2^attempt + 随机抖动
                        delay = min(
                            base_delay * (2 ** attempt),
                            max_delay
                        )
                        # 添加随机抖动,避免多个客户端同时重试
                        jitter = random.uniform(0, delay * 0.1)
                        total_delay = delay + jitter

                        print(
                            f"[重试] 第 {attempt + 1} 次失败(限流),"
                            f"等待 {total_delay:.1f}s 后重试..."
                        )
                        time.sleep(total_delay)
                    else:
                        # 非限流错误,短暂等待后重试
                        time.sleep(base_delay)

            # 所有重试都失败,抛出最后一次异常
            print(f"[错误] 重试 {max_retries} 次后仍然失败: {last_exception}")
            raise last_exception

        return wrapper
    return decorator


# 使用示例:为API调用函数添加重试能力
@retry_with_backoff(max_retries=5, base_delay=2.0)
def call_cloudwatch_api(client, params):
    """带重试机制的CloudWatch API调用"""
    return client.get_metric_data(**params)


# 在实际使用中,对批量操作也建议添加速率限制
def rate_limited_batch_call(calls: list, max_per_second: int = 10):
    """
    速率限制的批量调用
    控制每秒不超过max_per_second次API调用
    """
    interval = 1.0 / max_per_second  # 每次调用间隔

    results = []
    for i, call_params in enumerate(calls):
        # 每次调用前等待固定间隔
        if i > 0:
            time.sleep(interval)

        # 每100次调用打印进度
        if i % 100 == 0:
            print(f"进度: {i}/{len(calls)} ({i * 100 // len(calls)}%)")

        # 执行调用(带重试)
        try:
            result = call_cloudwatch_api(**call_params)
            results.append(result)
        except Exception as e:
            print(f"调用失败(索引{i}): {e}")
            results.append(None)

    return results

7.2 数据安全与权限管理

接入云服务API时需要使用AccessKey。出于安全考虑,强烈建议使用最小权限原则,只授予必要的API访问权限。对于生产环境的AccessKey,建议通过密钥管理服务(如AWS Secrets Manager或阿里云KMS)进行存储,而不是硬编码在配置文件中。

# ============================================================
# 技术栈:Python 3.x + AWS Secrets Manager
# 功能:从密钥管理服务安全获取API凭证
# ============================================================

import boto3
import json
from functools import lru_cache

@lru_cache(maxsize=1)
def get_secrets_from_manager(secret_name: str, region: str = 'us-east-1') -> dict:
    """
    从AWS Secrets Manager安全获取密钥
    使用lru_cache缓存结果,避免重复调用

    参数:
        secret_name: 密钥名称
        region: AWS区域
    返回:
        解析后的密钥字典
    """
    # 创建Secrets Manager客户端
    client = boto3.client('secretsmanager', region_name=region)

    # 获取密钥值
    response = client.get_secret_value(SecretId=secret_name)

    # 解析JSON格式的密钥
    secrets = json.loads(response['SecretString'])

    # 验证必要字段是否存在
    required_keys = ['DD_API_KEY', 'DD_APP_KEY', 'AWS_ACCESS_KEY', 'AWS_SECRET_KEY']
    missing = [k for k in required_keys if k not in secrets]

    if missing:
        raise ValueError(f"密钥中缺少必要字段: {missing}")

    return secrets


def initialize_clients() -> tuple:
    """
    初始化所有需要的客户端
    从密钥管理服务获取凭证,避免硬编码
    """
    # 从Secrets Manager获取凭证
    secrets = get_secrets_from_manager('myapp/production-secrets')

    # 初始化Datadog配置
    dd_config = Configuration()
    dd_config.set_default_header('DD-API-KEY', secrets['DD_API_KEY'])
    dd_config.set_default_header('DD-APPLICATION-KEY', secrets['DD_APP_KEY'])

    # 初始化AWS客户端(使用环境变量或默认凭证链)
    import boto3
    aws_session = boto3.Session(
        aws_access_key_id=secrets['AWS_ACCESS_KEY'],
        aws_secret_access_key=secrets['AWS_SECRET_KEY'],
        region_name='us-east-1'
    )

    print("所有客户端初始化完成(凭证已从Secrets Manager加载)")
    return dd_config, aws_session

7.3 标签规范化管理

标签是Datadog中数据组织的基础。如果标签不规范,后期查询和分组会非常混乱。建议团队建立统一的标签规范,比如:

  • env: 环境标识(production/staging/development)
  • team: 负责团队(backend/frontend/infra)
  • service: 服务名称
  • cloud: 云平台标识(aws/aliyun)
  • region: 区域标识
# ============================================================
# 技术栈:Python 3.x
# 功能:标签规范化管理工具
# ============================================================

import re

class TagValidator:
    """标签验证和管理器"""

    # 允许的标签前缀
    ALLOWED_PREFIXES = {
        'env', 'team', 'service', 'cloud', 'region',
        'instance_id', 'instance_name', 'instance_type',
        'db_instance', 'engine', 'engine_version',
        'bucket', 'source', 'destination',
        'version', 'batch'
    }

    # 标签键的正则校验规则
    KEY_PATTERN = re.compile(r'^[a-zA-Z0-9_]+$')
    # 标签值的正则校验规则(允许冒号用于key:value格式)
    VALUE_PATTERN = re.compile(r'^[a-zA-Z0-9_:.\-]+$')

    @staticmethod
    def validate_tag(tag: str) -> bool:
        """
        验证单个标签的格式是否合规
        标签格式要求:key:value 或 key
        """
        if ':' in tag:
            key, value = tag.split(':', 1)
        else:
            key, value = tag, None

        # 检查标签键
        if not TagValidator.KEY_PATTERN.match(key):
            print(f"  [警告] 标签键格式不合规: {key}")
            return False

        if key not in TagValidator.ALLOWED_PREFIXES:
            print(f"  [警告] 非标准标签键: {key}(建议从允许列表中选择)")
            return False

        # 检查标签值
        if value is not None and not TagValidator.VALUE_PATTERN.match(value):
            print(f"  [警告] 标签值格式不合规: {value}")
            return False

        return True

    @staticmethod
    def sanitize_tags(tags: list) -> list:
        """
        清洗标签列表,过滤掉不合规的标签
        """
        clean_tags = []
        for tag in tags:
            if TagValidator.validate_tag(tag):
                clean_tags.append(tag)

        return clean_tags


# 使用示例
if __name__ == '__main__':
    # 待验证的标签列表
    raw_tags = [
        'env:production',           # ✓ 合规
        'team:backend',             # ✓ 合规
        'service:order-service',    # ✓ 合规
        'cloud:aws',                # ✓ 合规
        'region:us-east-1',         # ✓ 合规
        'invalid-key:value',        # ✗ 非标准前缀
        'env:prod',                 # ✓ 合规
        'env:测试环境',              # ✗ 中文字符不合规
    ]

    print("标签验证结果:")
    print("-" * 40)
    clean_tags = TagValidator.sanitize_tags(raw_tags)
    print(f"\n清洗后的标签: {clean_tags}")

7.4 性能与资源消耗

在大规模部署场景下,Agent本身也会消耗一定的CPU和内存资源。默认的Agent采集频率是60秒一次,对于大部分场景已经够用。如果不需要秒级粒度的监控,可以适当降低采集频率以减少对生产环境的影响。


八、文章总结

Datadog作为一个成熟的第三方可观测性平台,在多云集成方面提供了非常完善的解决方案。通过本文介绍的配置方法,你可以将AWS和阿里云的核心资源指标统一汇聚到Datadog中,实现跨云的统一监控和管理。

对于刚开始接触Datadog的团队,建议遵循以下实施路径:

第一步,先从最简单也最有价值的资源开始接入,比如核心业务的EC2/ECS实例和RDS数据库。这些资源出问题对业务影响最大,接入后收益也最明显。

第二步,配置基础告警规则。不要一上来就配置复杂的智能阈值,先用简单的固定阈值跑起来,等积累了一些历史数据之后,再逐步切换到智能告警模式。

第三步,统一标签规范。在接入初期就要建立标签标准,否则后期数据量上来之后,标签混乱带来的查询困难会非常痛苦。

第四步,逐步扩展覆盖范围。从核心资源扩展到全量资源,从指标监控扩展到日志收集和APM链路追踪,形成一个完整的可观测性体系。

最后提醒一点:监控系统的价值不在于"看到所有数据",而在于"在需要的时候能快速找到问题根因"。所以配置Dashboard和告警时,要始终围绕"遇到问题怎么最快定位"这个核心目标来设计,而不是追求指标数量上的全面覆盖。