You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加NextToken后仍仅能获取约三分之一EC2实例?求解决方案

问题描述

无法获取全部符合条件的EC2实例,仅能获取约三分之一(实际约100个,总数约350个)。所用Python脚本用于筛选实例类型为c6g.xlarge、r6g.xlarge,密钥对为prod-or且处于运行状态的EC2实例:初始未添加NextToken时仅能获取80个实例,添加后提升至108个,但仍远少于预期总数。脚本核心函数包括:

  • assume_role_and_create_session:扮演指定AWS角色
  • get_ec2_instances:获取符合筛选条件的EC2实例
  • get_ebs_data:获取EC2实例关联的EBS卷数据
  • write_csv_file:将提取的数据写入CSV文件

脚本代码如下:

import boto3
import csv
from datetime import datetime

def assume_role_and_create_session(tmp_session, role_arn):
    sts_connection = tmp_session.client('sts')
    assume_role_object = sts_connection.assume_role(RoleArn=role_arn, RoleSessionName="GetEBSData", DurationSeconds=900)
    
    return boto3.Session(
        aws_access_key_id=assume_role_object['Credentials']['AccessKeyId'],
        aws_secret_access_key=assume_role_object['Credentials']['SecretAccessKey'],
        aws_session_token=assume_role_object['Credentials']['SessionToken']
    )

def get_ec2_instances(session, instance_types, keyName):
    ec2_client = session.client('ec2', region_name=region_name)
    instances_data = []
    next_token = None
    
    while True:
        if next_token:
            response = ec2_client.describe_instances(
                Filters=[
                    {
                        'Name': 'instance-type',
                        'Values': instance_types
                    },
                    {
                        'Name': 'key-name',
                        'Values': [keyName]
                    },
                    {
                        'Name': 'instance-state-name',
                        'Values': ['running']
                    }
                ],
                NextToken=next_token
            )
        else:
            response = ec2_client.describe_instances(
                Filters=[
                    {
                        'Name': 'instance-type',
                        'Values': instance_types
                    },
                    {
                        'Name': 'key-name',
                        'Values': [keyName]
                    },
                    {
                        'Name': 'instance-state-name',
                        'Values': ['running']
                    }
                ]
            )

        instances_data.extend(response['Reservations'])
        next_token = response.get('NextToken')

        if not next_token:
            break
        
    return instances_data

def get_ebs_data(session, instance, instance_types):
    instance_id = instance['Instances'][0]['InstanceId']
    instance_key_name = instance['Instances'][0]['KeyName']
    instance_type = instance['Instances'][0]['InstanceType']
    
    if instance_type in instance_types:
        ec2_client = session.client('ec2', region_name=region_name)
        volumes_response = ec2_client.describe_volumes(
            Filters=[
                {
                    'Name': 'attachment.instance-id',
                    'Values': [instance_id]
                }
            ]
        )
        
        volumes_data = []
        
        for volume in volumes_response['Volumes']:
            volume_id = volume['VolumeId']
            volume_type = volume['VolumeType']
            volume_size = volume['Size']
            volume_iops = volume.get('Iops', 'N/A')
            volume_snapshot_id = volume.get('SnapshotId', 'N/A')
            
            volumes_data.append([instance_id, instance_key_name, instance_type, volume_id, volume_type, volume_size, volume_iops, volume_snapshot_id])
        
        return volumes_data
    else:
        return None

def write_csv_file(data, file_path, sum_volume_size):
    with open(file_path, mode='w', newline='') as csv_file:
        csv_writer = csv.writer(csv_file)
        
        csv_writer.writerow(['InstanceId', 'Instance KeyName', 'Instance Type', 'EBS Volume Id', 'Type of EBS', 'Size of EBS', 'IOPS', 'Volume Snapshot'])
        csv_writer.writerows(data)

if __name__ == "__main__":
    region_name = 'us-west-2'

    env_roles = {
        'prod': "PROD_ARN",
        'dev': "DEV_ARN"
    }

    tmp_session = boto3.Session(
        aws_access_key_id="AWS_ACCESS_KEY_ID",  # Replace with your access key
        aws_secret_access_key="AWS_SECRET_ACCESS_KEY",  # Replace with your secret key
    )

    env = 'prod'
    role_arn = env_roles[env]
    session = assume_role_and_create_session(tmp_session, role_arn)

    instance_types = ['c6g.xlarge', 'r6g.xlarge']
    keyName = 'prod-or'
    instances_data = get_ec2_instances(session, instance_types, keyName)

    data = []

    print(f"Number of instances obtained: {len(instances_data)}")

    for instance in instances_data:
        volumes_data = get_ebs_data(session, instance, instance_types)
        if volumes_data:
            data.extend(volumes_data)

    timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    csv_file_path = f'{env}_{region_name}_{timestamp}.csv'
    write_csv_file(data, csv_file_path, sum_volume_size)
    print(f"CSV file '{csv_file_path}' created successfully.")
排查原因
  1. 单区域限制:脚本硬编码了region_name = 'us-west-2',若目标EC2实例分布在多个AWS区域,仅能获取该区域内的实例。
  2. IAM角色权限不足:扮演的角色可能缺少跨区域EC2资源的描述权限,或ec2:DescribeInstances权限被限制在特定资源范围内。
  3. 计数逻辑错误:脚本用len(instances_data)统计Reservation数量而非实际实例数量,导致误判获取到的实例总数(一个Reservation可包含多个实例)。
  4. 冗余过滤与参数错误:get_ebs_data中重复校验实例类型,可能过滤掉合法实例;write_csv_file函数接收未定义的sum_volume_size参数,存在潜在报错风险。
解决方案

1. 支持多区域查询

将单区域改为区域列表,遍历所有目标区域:

# 主函数中替换区域定义
region_names = ['us-west-2', 'us-east-1', 'eu-west-1']  # 按需添加实际区域

修改get_ec2_instances和get_ebs_data,新增region_name参数:

def get_ec2_instances(session, instance_types, keyName, region_name):
    ec2_client = session.client('ec2', region_name=region_name)
    # 其余逻辑保持不变

def get_ebs_data(session, instance, instance_types, region_name):
    # 其余逻辑保持不变
    ec2_client = session.client('ec2', region_name=region_name)
    # 其余逻辑保持不变

主函数中遍历区域收集实例:

all_instances_data = []
for region in region_names:
    instances_data = get_ec2_instances(session, instance_types, keyName, region)
    all_instances_data.extend(instances_data)
    # 打印该区域实际实例数
    region_instance_count = sum(len(res['Instances']) for res in instances_data)
    print(f"Region {region} obtained {region_instance_count} instances")

instances_data = all_instances_data

2. 修正IAM角色权限

确保扮演的IAM角色拥有全区域EC2描述权限,示例策略如下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances",
                "ec2:DescribeVolumes"
            ],
            "Resource": "*"
        }
    ]
}

3. 优化过滤与计数逻辑

  • 移除get_ebs_data中重复的实例类型校验(get_ec2_instances已完成筛选)
  • 修正实例计数逻辑,统计实际实例数量:
# 替换原打印语句
total_instances = sum(len(res['Instances']) for res in instances_data)
print(f"Number of instances obtained: {total_instances}")

4. 修复CSV写入参数错误

移除write_csv_file中未使用的sum_volume_size参数:

def write_csv_file(data, file_path):
    with open(file_path, mode='w', newline='') as csv_file:
        csv_writer = csv.writer(csv_file)
        csv_writer.writerow(['InstanceId', 'Instance KeyName', 'Instance Type', 'EBS Volume Id', 'Type of EBS', 'Size of EBS', 'IOPS', 'Volume Snapshot'])
        csv_writer.writerows(data)

# 调用时修改为
write_csv_file(data, csv_file_path)

内容的提问来源于stack exchange,提问作者userInNeed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:04:57