两分钟搞清楚 RI 和 Savings Plans

主要区别

RI 涵盖了更多的服务种类:如果需要为 Amazon RDS、Amazon Redshift、Amazon OpenSearch、Amazon DynamoDB、Amazon ElastiCache 服务购买承诺折扣,RI 是唯一选择。SP 涵盖了更多的计算服务:如果需要为 Amazon Fargate、Amazon Lambda、Amazon Sagemaker 这些计算服务购买承诺折扣,SP 是唯一选择。

因此,我们真正需要考虑的是为 EC2 服务选择购买 RI 还是 SP。

折扣对比

接下来,我们来看一下 EC2 RI 和 SP 的折扣情况,不同区域和实例类型的折扣存在差异,此处仅做折扣对比。选取的参数为:

  • 区域:us-east-1
  • 实例类型:c5.large

详细折扣信息请参考以下链接:

https://aws.amazon.com/savingsplans/compute-pricing

https://aws.amazon.com/ec2/pricing/reserved-instances/pricing

灵活对比

通常在购买 Standard RI 时,需要指定平台和租户,这意味着 RI 不能在不同的平台和租户之间进行切换。

相比之下,购买 EC2 Instance SP 时无需指定平台和租户,系统可以自动进行切换。

Convertable RI  和 Compute SP

Convertible RI 允许在不同的实例类型、平台和租户类型之间切换,但需要根据利用率和覆盖率情况进行手动调整。

相比之下,Compute SP 可以自动应用于不同实例类型、平台和租户的 EC2 实例、以及 Fargate 和 Lambda 服务。

总结

综上所述,SP 相较于 RI,在提供相同折扣的同时具备更高的灵活性,是节省 AWS 计算成本更简单、灵活的方式。

最后需要补充的是,RI 的一个独有的特性是可以在 RI Marketplace 上进行买卖,但这存在一定限制,且无法保证能很快售出。

请参考链接:

https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/ri-market-general.html

玩转亚马逊云监控服务Cloudwatch之一:批量为 EC2 创建 CPU 使用率告警

引言

这是一个序列操作指南文档,本篇是第一篇,玩转亚马逊云监控服务Cloudwatch之一:批量为 EC2 创建 CPU 使用率告警。 在当今的云计算时代,企业为了追求更高效、更灵活的 IT 基础设施,常常会选择将业务系统迁移到公有云平台。某新能源领域的头部企业,出于对全球市场布局和技术创新的考量,决定将其业务系统从阿里云迁移到 AWS。然而,在迁移过程中,他们遇到了一个挑战:AWS CloudWatch 原生功能中没有直接支持批量添加 EC2 监控告警的功能。这对于拥有大量 EC2 实例的企业来说,手动为每台实例设置告警无疑是一项耗时且容易出错的任务。作为该企业的现场支持SA,我们通过编写一个 Python 脚本,成功地帮助他们实现了批量创建 EC2 CPU 使用量告警的功能。

背景与挑战

该新能源企业在其业务系统迁移至 AWS 后,面临着如何高效监控大量 EC2 实例性能的问题。特别是,他们需要为每一台 EC2 实例创建 CPU 使用率告警,以便在 CPU 使用率超过一定阈值时能够及时收到通知,从而快速响应潜在的性能问题。然而,AWS CloudWatch 作为 AWS 提供的强大的监控与可观测性服务,虽然功能丰富,但并没有直接提供批量创建 EC2 告警的原生功能。这意味着企业无法通过简单的点击操作来为所有 EC2 实例统一设置告警,而是需要针对每一台实例单独进行配置。对于拥有数十台甚至上百台 EC2 实例的企业来说,这种手动配置的方式不仅效率低下,而且容易因人为疏忽而导致配置不一致或遗漏某些实例,从而影响整体监控效果和系统稳定性。

解决方案设计

为了解决这一挑战,我们决定利用 AWS 提供的软件开发工具包(SDK)—— Boto3,编写一个 Python 脚本来实现批量创建 EC2 CPU 使用量告警的功能。Boto3 是 AWS 的 Python 库,允许开发者通过编写代码与 AWS 服务进行交互,从而实现自动化和定制化的操作。通过使用 Boto3,我们可以调用 AWS 的 API 来获取所有运行的 EC2 实例信息,并为每个实例创建自定义的 CPU 使用率告警。这样,企业就能够以自动化的方式一次性为所有目标实例配置统一的告警规则,不仅节省了大量时间,还确保了告警配置的一致性和准确性。

Python 脚本实现

以下是我们为企业编写的 Python 脚本,用于批量创建 EC2 CPU 使用量告警:

"""
author: RJ.Wang
Date: 2025-03-13
email: wangrenjun@gmail.com
Description: Batch creation of AWS EC2 CPU utilization alarms 
"""
import boto3
import logging
from botocore.exceptions import ClientError

# 配置日志记录
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)

AWS_REGION = "ap-southeast-1"  # 需要修改为您的 AWS 区域
sns_topic_arn = "arn:aws:sns:xxxxxx:xxxxxx:xxxxxx"  # 需要修改为您的 SNS 主题 ARN

ec2_client = boto3.client("ec2", region_name=AWS_REGION)
cw_client = boto3.client("cloudwatch", region_name=AWS_REGION)
sts_client = boto3.client("sts")

# 获取账号ID并缓存
account_id = sts_client.get_caller_identity()["Account"]

def get_instance_ids():
    """
    获取所有运行的EC2实例ID
    """
    paginator = ec2_client.get_paginator("describe_instances")
    instance_ids = []
    try:
        for page in paginator.paginate():
            for reservation in page.get("Reservations", []):
                for instance in reservation.get("Instances", []):
                    instance_id = instance.get("InstanceId")
                    state = instance.get("State", {}).get("Name", "unknown")
                    if state == "running":
                        instance_ids.append(instance_id)
                        logger.debug(f"Found running instance: {instance_id}")
                    else:
                        logger.debug(f"Skipping instance in state {state}: {instance_id}")
    except ClientError as e:
        logger.error(f"Error getting instance IDs: {e}")
    except Exception as e:
        logger.error(f"Unexpected error getting instances: {e}")
    return instance_ids

def delete_existing_alarm(alarm_name):
    """
    删除已存在的同名报警
    """
    try:
        response = cw_client.describe_alarms(AlarmNames=[alarm_name])
        if response.get("MetricAlarms"):
            cw_client.delete_alarms(AlarmNames=[alarm_name])
            logger.info(f"Deleted existing alarm: {alarm_name}")
        else:
            logger.info(f"No existing alarm found: {alarm_name}")
    except ClientError as e:
        logger.error(f"Error deleting alarm {alarm_name}: {e}")

def create_cpu_alarm(instance_id, created_alarms):
    """
    为指定实例创建CPU使用率报警
    """
    alarm_name = f"CPU-{instance_id}-Alarm"
    try:
        delete_existing_alarm(alarm_name)
        cw_client.put_metric_alarm(
            AlarmName=alarm_name,
            MetricName="CPUUtilization",
            Namespace="AWS/EC2",
            Statistic="Average",
            Period=60,
            EvaluationPeriods=2,
            Threshold=85.0,
            ComparisonOperator="GreaterThanThreshold",
            AlarmActions=[sns_topic_arn],
            OKActions=[sns_topic_arn],
            AlarmDescription=f"Alarm for CPUUtilization on instance {instance_id}",  # 增加报警描述
            Dimensions=[{"Name": "InstanceId", "Value": instance_id}]
        )
        logger.info(f"Created CPU alarm: {alarm_name}")

        alarm_arn = f"arn:aws:cloudwatch:{AWS_REGION}:{account_id}:alarm:{alarm_name}"
        created_alarms.append(alarm_arn)
    except ClientError as e:
        logger.error(f"Error creating alarm for instance {instance_id}: {e}")

def main():
    """
    主函数,执行整个流程
    """
    try:
        logger.info("Starting program...")
        instance_ids = get_instance_ids()
        if not instance_ids:
            logger.info("No running instances found. Exiting.")
            return

        logger.info(f"Found {len(instance_ids)} running instances: {instance_ids}")

        created_alarms = []
        for instance_id in instance_ids:
            create_cpu_alarm(instance_id, created_alarms)

        total_alarms = len(created_alarms)
        logger.info(f"\nTotal new alarms created: {total_alarms}")
        logger.info("Alarm ARNs:")
        for arn in created_alarms:
            logger.info(arn)

        logger.info("Program completed successfully.")
    except Exception as e:
        logger.error(f"An unexpected error occurred: {e}", exc_info=True)

if __name__ == "__main__":
    main()

配置 SNS 主题和订阅

为了确保告警通知能够及时发送到指定的电子邮件地址,我们需要配置 SNS 主题和订阅。以下是详细的步骤:

  1. 创建 SNS 主题

    • 登录 AWS 管理控制台,选择 SNS 服务。
    • 在左侧导航栏中,选择 主题,然后点击 创建主题
    • 输入主题名称(例如:ec2-cpu-alarms)和显示名称(可选),然后点击 创建主题
    • 记录下创建的主题 ARN,例如:arn:aws:sns:ap-southeast-1:123456789012:ec2-cpu-alarms
  2. 订阅 SNS 主题

    • 在 SNS 控制台中,选择刚才创建的主题,点击 订阅 选项卡,然后点击 创建订阅
    • 协议 下拉菜单中选择 电子邮件
    • 终端节点 中输入要接收告警通知的电子邮件地址。
    • 点击 创建订阅
  3. 确认订阅

    • AWS 会向指定的电子邮件地址发送一封确认邮件。
    • 打开邮件,点击其中的 确认订阅 链接,完成订阅过程。
    • 确认后,该电子邮件地址将能够接收来自 SNS 主题的告警通知。

iShot_2025-03-13_21.25.48

脚本解析

  1. 初始化 AWS 客户端:脚本首先初始化了 EC2、CloudWatch 和 STS 客户端,用于与相应的 AWS 服务进行交互。通过 STS 客户端获取当前账号的 ID,以便后续构造报警的 ARN。

  2. 获取运行的实例 IDget_instance_ids 函数使用 EC2 客户端的分页器获取所有运行状态的 EC2 实例 ID。它遍历每个页面的实例,筛选出状态为 “running” 的实例,并收集它们的 ID。

  3. 删除现有报警:在为每个实例创建新报警之前,delete_existing_alarm 函数会检查是否存在同名的现有报警。如果存在,则删除它,以避免冲突。

  4. 创建 CPU 报警create_cpu_alarm 函数负责创建 CPU 使用率报警。它定义了报警的名称、指标名称、命名空间、统计方式、周期、评估周期、阈值、比较运算符、报警动作、OK 动作、描述以及维度等参数。通过调用 CloudWatch 客户端的 put_metric_alarm 方法,将报警规则发送到 AWS 服务。

  5. 主函数执行流程main 函数作为程序的入口,协调整个流程。它首先获取运行的实例 ID 列表,然后为每个实例调用 create_cpu_alarm 函数创建报警,并收集创建的报警 ARN,最后输出总结信息。

实施步骤

  1. 配置 SNS 主题和订阅:按照上述步骤创建 SNS 主题,并通过电子邮件地址订阅该主题,确保订阅已确认。

  2. 更新脚本中的变量:在脚本中,将 AWS_REGION 变量的值替换为您的 AWS 区域,将 sns_topic_arn 变量的值替换为您创建的 SNS 主题 ARN。

  3. 在 AWS CloudShell 中测试脚本

    • 登录到 AWS 管理控制台,使用具有必要权限的 IAM 账户进行登录。确保该账户具有访问 AWS CloudShell 和使用相关 AWS 服务的权限。
    • 在 AWS 管理控制台中,启动 AWS CloudShell。您可以通过点击控制台右上角的 CloudShell 图标来启动它。
    • 在 CloudShell 中,克隆包含脚本的 GitHub 仓库,或者直接将脚本复制到 CloudShell 环境中。
    • 运行脚本:在 CloudShell 中运行 python create_ec2_alarms.py 命令。

iShot_2025-03-13_21.26.20

  1. 验证结果:运行脚本后,企业可以在 AWS 管理控制台的 CloudWatch 服务中查看新创建的报警。检查每个实例是否都有对应的 CPU 报警,并验证报警的配置是否符合预期。
iShot_2025-03-13_21.27.13

收到的告警 mail 通知

iShot_2025-03-13_21.27.59

结果与收益

通过这个解决方案,企业成功地为其所有运行的 EC2 实例批量创建了 CPU 使用量告警。这不仅提高了监控效率,还确保了所有实例的告警配置的一致性。企业现在可以更有效地监控其 AWS 环境中的资源使用情况,并在出现问题时及时收到通知,从而提高整体系统的可靠性和稳定性。

此外,这个案例还展示了 AWS 的灵活性和可扩展性。尽管 CloudWatch 的原生功能中没有直接支持批量添加告警,但通过使用 AWS 的 SDK 和 API,企业可以轻松地实现自定义的解决方案,满足其特定的业务需求。

希望这个案例能够为其他面临类似挑战的企业提供启示,帮助他们更好地利用 AWS 的功能提升业务价值。

WordPress如何删除页脚的“自豪地由WordPress驱动” 和添加备案信息

环境:Wordpress 5.0.2版 + 2019主题

目的:去除页脚上的-> 自豪地由WordPress驱动。

去除“自豪地由WordPress驱动。”看下图:

先根据截图找到footer.php,只要在27、28、29行各加上“ // ” 然后保存更新即可;
如果想加上备案信息,只要在31行插入下面这段:


沪公网安备 12345678号


就可以得到下图

请用你的备案号码替代12345678并保存更新即可。