如何在Boto3创建EC2实例时检测UserData脚本执行完成以拉取日志销毁实例
判断EC2实例启动完成且UserData脚本执行完毕的实现方案
1. 先改造UserData执行脚本(推荐)
在你现有的UserData脚本末尾添加执行状态标记,方便外部检测结果,不需要改现有逻辑,只需要追加以下代码:
# 原有脚本逻辑保持不变,仅在末尾追加 # 写入执行状态标记文件 if [ $? -eq 0 ]; then echo "success" > /var/log/user_data_exec_done else echo "fail:$?" > /var/log/user_data_exec_done fi
如果不想改脚本,也可以直接用cloud-init自带的状态检测能力,不需要加任何标记,后面检测的时候直接调用cloud-init命令即可。
2. 本地控制脚本检测逻辑
分两步完成检测:先确认实例本身正常启动,再确认UserData脚本执行完成。
2.1 检测实例正常启动
用Boto3自带的等待器,等实例进入running状态且系统、实例两级健康检查都通过,确保实例可正常连接:
import time import paramiko # 你的createInstances逻辑执行完拿到instances对象后,逐个处理 for instance in instances: # 等待实例进入运行状态 instance.wait_until_running() # 刷新实例属性,获取公网IP等最新信息 instance.reload() # 等待实例健康检查通过 status_waiter = self.ec2_client.get_waiter('instance_status_ok') status_waiter.wait(InstanceIds=[instance.id])
2.2 检测UserData脚本执行完成
通过SSH连接实例,轮询状态标记或者cloud-init执行状态:
# 初始化SSH客户端 ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 最长等待时间,根据你的脚本实际执行时长调整,这里设为30分钟 max_wait_seconds = 1800 start_time = time.time() exec_result = False while time.time() - start_time < max_wait_seconds: try: # 连接实例,注意替换为你的AMI对应默认用户名、私钥路径 ssh.connect( hostname=instance.public_ip_address, username="ec2-user", key_filename="/path/to/your/aws_key_pair.pem" ) # 方式1:如果前面加了自定义状态标记,用这个命令 stdin, stdout, stderr = ssh.exec_command("cat /var/log/user_data_exec_done 2>/dev/null") exec_status = stdout.read().decode().strip() # 方式2:如果没改UserData脚本,用cloud-init自带命令检测,替换上面的exec_command即可 # stdin, stdout, stderr = ssh.exec_command("cloud-init status --wait >/dev/null 2>&1 && echo success") # exec_status = stdout.read().decode().strip() if exec_status == "success": print(f"实例{instance.id}的UserData脚本执行成功") exec_result = True break elif exec_status.startswith("fail"): print(f"实例{instance.id}的UserData脚本执行失败,错误码:{exec_status.split(':')[-1]}") break except Exception: # 连接失败就等待10秒重试 time.sleep(10) continue # 检测完成后执行SCP拉取日志 if exec_result: # 用paramiko SFTPClient拉日志,或者调用系统scp命令 sftp = ssh.open_sftp() sftp.get("/path/to/your/logfile.log", "./local_logfile.log") sftp.close() # 最后销毁实例 instance.terminate() ssh.close()
注意事项
- 实例绑定的安全组入方向需要开放22端口,允许你运行控制脚本的机器IP访问,否则无法建立SSH连接
- 不同AMI的默认用户名不同:Amazon Linux默认是
ec2-user,Ubuntu默认是ubuntu,CentOS默认是centos,需要根据你用的镜像调整连接参数 - 如果UserData脚本执行时长超过30分钟,需要对应调大
max_wait_seconds的取值,避免超时误判
内容的提问来源于stack exchange,提问作者Arun Kumar
相关产品推荐
相关产品推荐

