我们希望听到您的意见!帮助我们深入了解 Ansible 生态系统现状。
参加 2024 年 Ansible 项目调查

自动化节省 AWS 账单费用的两种简单方法

自动化节省 AWS 账单费用的两种简单方法

Red Hat Ansible Automation Platform 是一款用于公共云的优秀的自动化和编排工具。在这篇文章中,我将介绍 Ansible Automation Platform 可以提供帮助的两种常见场景。我想跳出常见的公共云用例(例如资源的供应和取消供应),而是看看如何自动化常见的运维任务。

cloud engineer diagram

什么是运维任务?它只是管理员除了创建和删除云资源(例如实例、网络、密钥等)之外需要执行的任何操作,以帮助维护其公司的公共云帐户。我遇到的问题之一是实例被遗留在后台运行,从而增加了我们的公共云账单,而我们却将注意力集中在其他地方。用户越多,出现问题的可能性就越大;自动化可以帮助解决这些问题并保持对帐户的控制。这里我想解决两个常见的场景

  1. 为一次性计划手动创建的定制 AWS 实例,通常用于测试某些内容,然后忘记了这些实例并将其保持运行状态。
  2. 每次将 Pull Request (PR) 推送到我们的项目时,都会启动持续集成 (CI) 实例以以编程方式测试更改,并且有时会遇到某些情况,导致并非所有内容都正确地取消了供应(关闭)。

在这两种情况下,孤立的实例都可能长时间保持运行状态。假设您启动了几十个实例来在公共云上测试某些内容,然后您忙了起来,忘记了时间,并在下班前忘记终止这些实例。这可能是 16 小时(至少)的时间,您在此期间被收取费用,并且没有从您的公司正在资助的公共云中获得任何价值。现在将此乘以数十个用户,该账单可能会很快达到数万美元。

用例一:处理定制的孤立实例

因此,让我们解决每个问题,并使用 Ansible Automation Platform 自动化上述第一个场景的解决方案,其中实例在任何自动化防护栏之外启动(即,它们不使用任何自动化工具,包括 Ansible,来启动云资源)。我们要求团队中所有可以访问公共云帐户的人员标记其实例。他们必须创建一个键值对标签,内容为:owner: person

inventory tags screenshot

这创建了一种非常简单的方法来审计和查看谁(哪个人、组织或团队)对账单负责,这是成功的一半。我将编写一个非常简单的 Ansible Playbook 来强制执行此操作。我将使用完全受支持的 amazon.aws 集合来演示这一点。

此处社区集合和受支持集合之间的主要区别在于您 Red Hat 订阅的支持。对于作为 Red Hat 订阅一部分包含的完全受支持的 amazon.aws 集合,还进行了大量的集成测试、代码审计和 Python 3/boto3 支持。

处理未标记的实例

在我的第一个 Ansible Playbook 中,我想获取所有没有标签的实例列表。首先,让我们检索特定区域中所有正在运行的实例

- name: grab info for un-tagged instances
  amazon.aws.ec2_instance_info:
    region: "{{ ec2_region }}"
    filters:
      instance-state-name: running
  register: ec2_node_info

我正在使用 AWS 集合中找到的 ec2_instance_info 模块,它是 Amazon 命名空间的一部分。此任务检索所有实例(无论是否有标签)。我发现最简单的方法是获取所有内容,然后过滤掉空标签

- name: set the untagged to a var
  set_fact:
    untagged_instances: "{{ ec2_node_info.instances | selectattr('tags', 'equalto', {}) | map(attribute='instance_id') | list }}"

selectattr 过滤器只是匹配任何没有标签的实例,标签为 ['tags', 'equalto', {} ]

然后我可以简单地终止这些实例,因为我的同事没有遵循我制定的良好准则

- name: Terminate every un-tagged running instance in a region.
  amazon.aws.ec2:
    region: "{{ ec2_region }}"
    state: absent
    instance_ids: "{{ untagged_instances }}"
  when: untagged_instances | length  > 0

但是,由于您可能比我更宽容,因此您可以使用 state: stopped 而不是 absent,这将关闭它们而不是终止它们。

检索任何缺少标签的实例

为了扩展上述内容,我们不仅关心完全未标记的实例(表示根本没有标签),而且我们特别关注 owner 标签。我现在想检索任何缺少 owner 标签的实例。我可以使用与上面完全相同的逻辑,但改为使用 selectattr 过滤器查找未定义的值。

- name: set the missing tag to a var
  set_fact:
    missing_tag: "{{ ec2_node_info.instances | selectattr('tags.owner', 'undefined') | map(attribute='instance_id') | list }}"

我想展示上面这两个示例,以提供一条实现操作化的路径。使用 Ansible Automation Platform 实现上述操作,您的组织现在可以理解,他们需要使用标签,否则他们的实例将被关闭(或更糟!)。更进一步,组织可以使用自动化来强制执行特定的标签以分配所有权,或者将对实例采取措施。您可以使用上述一个或两个示例。

用例二:处理自动化的实例

对于我的特定用例,我有一个代码存储库,该存储库会自动进行测试。我们的代码每晚都会进行测试,并且每次将 Pull Request (PR) 推送到代码存储库时都会进行测试。CI 测试将在 AWS 上配置实例,配置实例,运行自动化测试,然后取消其配置。有时,取消配置步骤将无法成功完成,从而导致孤立的主机。我注意到的一件事是,这些实例通常会被发现部分关闭,它们的标签完全丢失(已删除),但实例实际上并未关闭,因此我们仍在被收取费用。前面的示例中的上述 Ansible Playbook 可以捕获这种情况。

但是,另一个很棒的测试是使用新的正常运行时间参数。

- name: grab info
  amazon.aws.ec2_instance_info:
    region: "{{ ec2_region }}"
    uptime: 121
    filters:
      instance-state-name: [ "running" ]
      "tag:ansible-workshops": true
  register: ec2_node_info

在此任务中,我想重点介绍两个参数。第一个是正常运行时间参数(在 amazon.aws 的 1.4.0 中添加),它只返回运行时间超过该整数(以分钟为单位)的实例。对于此示例,它必须运行超过 121 分钟,或超过两个小时。我知道我的 CI 测试永远不应该花费超过两个小时。要么实例卡住了,要么我的自动化测试坏了,要么取消配置没有成功。

此处另一个标签只是一个过滤器,以便我只返回属于我的自动化测试(相对于其他计划)的实例。在此示例中,它必须是 workshop。现在,您应该明白为什么我需要在开头使用“未标记”示例!如果根本没有标签,则整个运维任务将失败。因此,由于标签在公共云基础设施中的重要性,因此“无标签”用例与每个其他用例都重叠。

自动化自动化

因此,这种自动化非常棒,但是手动运行 playbook 只能节省您少量时间。我继续使用 Ansible 工作流 功能一次性访问多个区域,然后对其进行计划,以便我的自动化作业每小时运行一次。

ansible workflow diagrams

右侧的每个矩形代表一个自动化 作业。同一列中的每个作业都在我的 Ansible Automation Platform 集群上并行运行。每个作业模板都设置为不同的区域。我还使用了 调查功能 使得从 Web UI 中轻松配置。

screenshot

在我的特定场景中,我在四个 AWS 区域(us-east-1、ap-northeast-1、ap-southeast-1 和 eu-central-1)中运行自动化测试。现在我的工作流已完成,安排我的工作流每小时运行一次非常简单。

screenshot

瞧!现在我在后台进行了自动化测试,以确保没有孤立的实例正在运行。对于我的特定用例,这将节省大量资金,并强制在公共云使用方面形成一种问责制文化,以便团队成员之间能够清楚透明地了解成本。







如何迁移您的 Ansible Playbook 以支持 AWS boto3

如何迁移您的 Ansible Playbook 以支持 AWS boto3

Red Hat Ansible Automation Platform 以自动化 Linux、Windows 和网络基础设施而闻名。虽然 Ansible 的社区版本和我们的企业产品 Red Hat Ansible Automation Platform 主要以配置管理而闻名,但这只是您使用 Ansible 自动化真正能够实现的一小部分。Ansible Automation Platform 非常擅长自动化的其他许多用例,例如您的 AWS、Azure 或 Google 公共云

diagram of Ansible on public clouds

Ansible Automation Platform 可以自动化您的公共云的部署、迁移和运维任务。这非常强大,因为您可以编排整个基础设施 工作流,从云部署到实例配置再到停用,而无需为每个单独的用例使用一个点工具。这也允许 IT 管理员专注于自动化业务成果,而不是各个技术孤岛。

具体来说,对于此博客,我想介绍如何将您的 Ansible Playbook 从不受支持的 ec2 模块转换为完全受支持的 ec2_instance 模块,以便在 AWS 上配置实例。亚马逊已弃用其软件开发工具包 (SDK) Boto,转而使用较新的完全受支持的 SDK Boto3。Alina Buzachis 在 2021 年 10 月宣布了“新增功能:Ansible AWS 集合 2.0 版本”,其中包括我们 Red Hat Ansible 认证内容集合中对 amazon.aws.ec2_instance 模块的完全支持,该模块使用 Python 3 和 Boto3。

受支持的 ec2_instance 模块已经存在一段时间了,但我尚未将其用于我的用例,因为我们需要最后一个功能才能与旧的 ec2 模块保持一致。具体来说,对于演示和研讨会,我需要 exact_count 参数。这允许我启动与指定数量相同的实例。例如,如果我指定 exact_count: 50,它将启动 50 个相同的 Red Hat Enterprise Linux 8 实例。

使用 exact_count 可以节省数小时的时间,而不用使用循环,而且我不需要一个庞大的声明式文件来表示我的 50 台服务器;只需调整单个参数即可创建相同的副本。 幸运的是,我们知道我们有这个参数,所以我开始将技术营销团队使用到的所有研讨会和演示都转换为 Boto3。

让我们看一下我们技术研讨会中任务文件的旧版本,以便我向您展示如何从 ec2 转换为 ec2_instance

---
- name: Create EC2 instances for RHEL8
  ec2:
    assign_public_ip: true
    key_name: "{{ ec2_name_prefix }}-key"
    group: "{{ ec2_security_group }}"
    instance_type: "{{ ec2_info[rhel8].size }}"
    image: "{{ node_ami_rhel.image_id }}"
    region: "{{ ec2_region }}"
    exact_count: "{{ student_total }}"
    count_tag:
      Workshop_node1": "{{ ec2_name_prefix }}-node1"
    instance_tags:
      Workshop_node1": "{{ ec2_name_prefix }}-node1"
      Workshop: "{{ ec2_name_prefix }}"
      Workshop_type: "{{ workshop_type }}"
    wait: "{{ ec2_wait }}"
    vpc_subnet_id: "{{ ec2_vpc_subnet_id }}"
    volumes:
      - device_name: /dev/sda1
        volume_type: gp2
        volume_size: "{{ ec2_info[control_type].disk_space }}"
        delete_on_termination: true
  register: control_output

要将实例启动到 AWS,只需要六个必需的参数。您需要指定一个密钥(即访问镜像的 SSH 密钥)、安全组(ec2 实例的虚拟防火墙)、instance_type(例如 t2.medium)、区域(例如 us-east-1)、镜像(例如 RHEL8 的 AMI)和网络接口或 VPC 子网 ID(vpc_subnet_id)。

上面我的任务中的其余参数用于

  • 调整实例
  • 添加公网 IP 地址,增加存储空间
  • 更改模块的行为
    • wait 指的是等待实例达到运行状态,
    • exact_count 指的是并行配置多个实例
  • 添加标签,这只是向实例添加键值标签,以便我们可以在后续自动化中对其进行筛选,或者在 AWS Web 控制台中轻松排序。

要将其转换为 ec2_instance,只需进行一些小的调整!

ec2 ec2_instance
assign_public_ip: true
network:
          assign_public_ip: true
group: "{{ ec2_security_group }}"
security_group: "{{ ec2_security_group }}"
image: "{{ node_ami_rhel.image_id }}"
image_id: "{{ node_ami_rhel.image_id }}"
count_tag:
  Workshop_node1": "{{ ec2_name_prefix }}-node1"
filters:
  "tag:Workshop_node1": "{{ ec2_name_prefix }}-node1"
instance_tags:
tags:
volumes:
  - device_name: /dev/sda1
  volume_type: gp2
  volume_size: "{{ ec2_info[control_type].disk_space }}"
  delete_on_termination: true
volumes:
- device_name: /dev/sda1
  ebs:
  volume_type: gp2
  volume_size: "{{ ec2_info[rhel].disk_space }}"
  delete_on_termination: true

修改后的完整任务如下所示

- name: Create EC2 instances for node1
  ec2_instance:
    key_name: "{{ ec2_name_prefix }}-key"
    security_group: "{{ ec2_security_group }}"
    instance_type: "{{ ec2_info[rhel].size }}"
    image_id: "{{ node_ami_rhel.image_id }}"
    region: "{{ ec2_region }}"
    exact_count: "{{ student_total }}"
    network:
      assign_public_ip: true
    filters:
      "tag:Workshop_node1": "{{ ec2_name_prefix }}-node1"
    tags:
      Workshop_node1: "{{ ec2_name_prefix }}-node1"
      Workshop: "{{ ec2_name_prefix }}"
      uuid: "{{ ec2_name_prefix }}"
      guid: "{{ ec2_name_prefix }}"
      Workshop_type: "{{ workshop_type }}"
    wait: "{{ ec2_wait }}"
    vpc_subnet_id: "{{ ec2_vpc_subnet_id }}"
    volumes:
      - device_name: /dev/sda1
        ebs:
          volume_type: gp2
          volume_size: "{{ ec2_info[rhel].disk_space }}"
          delete_on_termination: true

虽然任务看起来很长,但请注意,可选标签占用了七行……这没问题,我正在显示许多默认值。请记住,向云资源添加标签不会产生额外费用,并且它们有助于后续的自动化和筛选。我曾经听到一位同事说,标签永远不会嫌多!

查看上面的任务,您会看到任何带有标签 Workshop_node1: "-node1" 的内容都将用于验证现有实例是否匹配。它将确保存在具有标签 Workshop_node1 的 exact_count 个实例。这也可以在后续自动化中用于筛选并仅检索所需的实例。

- name: grab instance ids to tag rtr1
  ec2_instance_info:
    region: "{{ ec2_region }}"
    filters:
      "tag:Workshop_node1": "{{ ec2_name_prefix }}-node1"
  register: node1_output

这将检索所有具有其公共标签的实例。您可能还需要每个实例的唯一标签。在这种情况下,我建议使用 ec2_tag 模块,其中 循环 的时间消耗较少(与使用 ec2_instance 模块循环相比)

- name: Ensure tags are present for node1
  ec2_tag:
    region: "{{ ec2_region }}"
    resource: "{{ item.1.instance_id }}"
    state: present
    tags:
      Name: "{{ ec2_name_prefix }}-student{{ item.0 + 1 }}-node1"
      Index: "{{ item[0] }}"
      Student: "student{{ item.0 + 1 }}"
      launch_time: "{{ item.1.launch_time }}"
  with_indexed_items:
    - "{{ node1_output.instances }}"
  when: node1_output.instances|length > 0

当您需要特定云资源的唯一标签时,ec2_tag 模块非常有用。在上面的示例中,名称、索引、学生标识符和启动时间对于该资源是唯一的。同样,额外的标签不会带来时间惩罚或成本,所以您可以随意添加标签。因此,在 AWS 上配置大量实例的工作流程如下所示

  1. 批量配置 exact_count 个实例
  2. 使用 ec2_instanceec2_instance_info 将输出注册到变量
  3. 对于唯一标签,使用 ec2_tag 模块遍历实例

感谢您阅读我的博客,希望这能帮助您在 Ansible 云自动化之旅中有所收获。