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

新建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访问策略配置正确)。

请问:

  1. 为什么aws s3 cp命令在userdata执行期间无法工作,实例创建完成后却可以?
  2. 是否S3访问策略要等EC2实例完全创建(userdata执行完毕)后才会生效?
  3. 正确的解决方法是什么?
提供的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:45:33