新建EC2实例执行userdata时缺失AWS凭证的问题排查与解决
问题描述
我通过Terraform脚本创建新的EC2实例,为其绑定了具备S3存储桶访问权限的IAM角色,并在userdata中加入了aws s3 cp s3://bucket-name/file-name .命令,用于从指定S3存储桶复制文件。
查看/var/log/cloud-init-output.log时发现fatal error: Unable to locate credentials错误,推测是执行该aws s3 cp命令导致的。但EC2实例创建完成后,手动执行相同命令却能正常工作(说明EC2的S3访问策略配置正确)。
请问:
- 为什么
aws s3 cp命令在userdata执行期间无法工作,实例创建完成后却可以? - 是否S3访问策略要等EC2实例完全创建(userdata执行完毕)后才会生效?
- 正确的解决方法是什么?
提供的Terraform代码
(注:已修正原代码中的语法错误,如变量引用、多余引号)
data "aws_iam_policy_document" "ec2_assume_role" { statement { effect = "Allow" actions = [ "sts:AssumeRole", ] principals { type = "Service" identifiers = [ "ec2.amazonaws.com", ] } } } resource "aws_iam_role" "broker" { name = "${var.env}-broker-role" assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json force_detach_policies = true } resource "aws_iam_instance_profile" "broker_instance_profile" { name = "${var.env}-broker-instance-profile" role = aws_iam_role.broker.name } resource "aws_iam_role_policy" "rabbitmq_ec2_access_to_s3_distro" { name = "${var.env}-rabbitmq_ec2_access_to_s3_distro" role = aws_iam_role.broker.id policy = data.aws_iam_policy_document.rabbitmq_ec2_access_to_s3_distro.json } data "aws_iam_policy_document" "rabbitmq_ec2_access_to_s3_distro" { statement { effect = "Allow" actions = [ "s3:GetObject", "s3:GetObjectVersion" ] resources = ["arn:aws:s3:::${var.distro_bucket}", "arn:aws:s3:::${var.distro_bucket}/*"] } } resource "aws_instance" "rabbitmq_instance" { iam_instance_profile = aws_iam_instance_profile.broker_instance_profile.name # 其他实例配置... }
原因分析
- 并非S3访问策略生效延迟,而是**EC2实例启动初期,Instance Metadata Service(IMDS)还未完成IAM角色临时凭证的下发。
- userdata属于cloud-init的早期执行阶段(通常在实例启动后立即运行),此时IMDS可能还未准备好提供IAM角色的临时凭证,导致aws cli无法获取到有效凭证,从而抛出
Unable to locate credentials错误。 - 实例创建完成后手动执行时,IMDS已经完成凭证下发,aws cli可以正常通过IMDS获取临时凭证,因此命令能正常执行。
解决方法
方法1:在userdata中添加重试逻辑,等待IMDS就绪
在userdata脚本中加入循环,等待IMDS返回IAM角色的临时凭证后,再执行S3复制命令:
#!/bin/bash # 等待IMDS返回指定IAM角色的凭证信息 until curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ | grep -q "${var.env}-broker-role"; do echo "等待IAM凭证就绪..." sleep 5 done # 执行S3复制命令 aws s3 cp s3://bucket-name/file-name .
方法2:直接重试aws s3 cp命令
如果不需要精确等待IMDS状态,可以直接对S3复制命令进行重试,直到成功:
#!/bin/bash MAX_RETRIES=5 RETRY_COUNT=0 until aws s3 cp s3://bucket-name/file-name .; do RETRY_COUNT=$((RETRY_COUNT+1)) if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then echo "重试次数耗尽,S3复制失败" exit 1 fi echo "S3复制失败,5秒后重试..." sleep 5 done
方法3:调整cloud-init执行阶段(可选)
如果使用cloud-config格式的userdata,可以将命令放到runcmd字段中,它的执行阶段比默认userdata稍晚,此时IMDS大概率已就绪:
#cloud-config runcmd: - aws s3 cp s3://bucket-name/file-name .
内容的提问来源于stack exchange,提问作者Andrey
相关产品推荐
相关产品推荐

