添加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.")
排查原因
- 单区域限制:脚本硬编码了
region_name = 'us-west-2',若目标EC2实例分布在多个AWS区域,仅能获取该区域内的实例。 - IAM角色权限不足:扮演的角色可能缺少跨区域EC2资源的描述权限,或
ec2:DescribeInstances权限被限制在特定资源范围内。 - 计数逻辑错误:脚本用
len(instances_data)统计Reservation数量而非实际实例数量,导致误判获取到的实例总数(一个Reservation可包含多个实例)。 - 冗余过滤与参数错误:
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
相关产品推荐
相关产品推荐

