You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Terraform创建AWS EC2实例时上传脚本失败,请求协助

解决Terraform创建EC2时上传脚本与配置Cronjob的问题

我完全理解你作为Terraform新手遇到这种卡壳的情况有多闹心——刚入门时我也在文件上传和初始化脚本上踩过不少坑!咱们一步步拆解问题,给你几个靠谱的解决方案:

方案1:修复File Provisioner的配置(最直接的文件上传方式)

你之前用File Provisioner失败,大概率是缺少SSH连接配置——这个Provisioner需要通过SSH连接到EC2实例才能传输文件,默认不会自动帮你建立连接。下面是完整的配置示例:

resource "aws_instance" "my_ec2" {
  ami           = "ami-xxxxxx" # 替换成你所在区域的Amazon Linux 2 AMI
  instance_type = "t2.micro"
  key_name      = "your-key-pair-name" # 替换成你的密钥对名称

  # 安全组必须开放SSH端口(22),否则无法连接
  vpc_security_group_ids = [aws_security_group.allow_ssh.id]

  # File Provisioner:上传本地脚本到EC2实例
  provisioner "file" {
    source      = "abc.sh" # 本地脚本路径
    destination = "/home/ec2-user/basic2.sh" # 实例上的目标路径

    # 关键:添加SSH连接配置
    connection {
      type        = "ssh"
      user        = "ec2-user" # Amazon Linux 2的默认用户名,Ubuntu是ubuntu
      private_key = file("~/.ssh/your-private-key.pem") # 本地私钥路径
      host        = self.public_ip # 自动获取实例的公网IP
    }
  }

  # 可选:用remote-exec给脚本加执行权限
  provisioner "remote-exec" {
    inline = [
      "chmod +x /home/ec2-user/basic2.sh"
    ]

    connection {
      type        = "ssh"
      user        = "ec2-user"
      private_key = file("~/.ssh/your-private-key.pem")
      host        = self.public_ip
    }
  }
}

# 配套的安全组:允许SSH访问
resource "aws_security_group" "allow_ssh" {
  name        = "allow-ssh"
  description = "Allow SSH inbound traffic"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # 生产环境建议限制成你的IP,比如"192.168.1.1/32"
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

注意事项:

  • 确保你的私钥文件权限正确:chmod 400 ~/.ssh/your-private-key.pem,否则SSH会拒绝连接
  • 如果实例在私有子网,需要通过堡垒机或者SSM Session Manager连接,这时可以把connection换成SSM类型(需要实例有SSM权限)

方案2:用User Data + Templatefile(更可靠的初始化方式)

User Data是EC2实例启动时自动执行的脚本,不需要额外的SSH连接,适合做初始化操作。你之前用的template_file是旧的数据源,现在Terraform推荐用内置的templatefile函数,更简洁:

步骤1:创建本地脚本模板(比如user_data.tpl

#!/bin/bash

# 创建目录并写入脚本内容
mkdir -p /home/ec2-user/files2
cat > /home/ec2-user/files2/basic2.sh << 'EOF'
# 这里复制你的abc.sh的内容,比如:
echo "脚本执行时间:$(date)" >> /home/ec2-user/script_run.log
EOF

# 给脚本加执行权限
chmod +x /home/ec2-user/files2/basic2.sh

# 配置Cronjob:每天凌晨1点执行脚本,输出日志到系统日志
echo "0 1 * * * ec2-user /home/ec2-user/files2/basic2.sh >> /var/log/basic_script.log 2>&1" | sudo tee /etc/cron.d/basic_cron

步骤2:在Terraform中引用这个模板

resource "aws_instance" "my_ec2" {
  ami           = "ami-xxxxxx"
  instance_type = "t2.micro"
  key_name      = "your-key-pair-name"
  vpc_security_group_ids = [aws_security_group.basic.id]

  # 使用templatefile加载用户数据模板
  user_data = templatefile("${path.module}/user_data.tpl", {})
}

# 配套安全组:允许必要的出站流量(比如下载依赖)
resource "aws_security_group" "basic" {
  name        = "basic-security-group"
  description = "Allow outbound traffic"

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

这种方法的好处是:实例启动时自动完成所有配置,不需要等待SSH连接,也不会因为网络问题导致上传失败。

方案3:用SSM Parameter Store存储脚本(适合敏感或复杂脚本)

如果你的脚本包含敏感信息,或者需要在多个实例复用,可以把脚本存在SSM Parameter Store里,然后在User Data中拉取:

#!/bin/bash
# 从SSM拉取脚本内容并写入文件
aws ssm get-parameter --name "/my/scripts/basic.sh" --with-decryption --query "Parameter.Value" --output text > /home/ec2-user/basic2.sh
chmod +x /home/ec2-user/basic2.sh

注意:实例需要有ssm:GetParameter的IAM权限,所以要给实例附加对应的IAM角色。

常见问题排查

  • File Provisioner连接失败:检查安全组是否开22端口,私钥路径和权限是否正确,实例公网IP是否能访问
  • User Data没执行:查看实例的/var/log/cloud-init-output.log日志,里面会有执行细节和错误信息
  • Cronjob不生效:检查/var/log/cron日志,确保Cron服务在运行(sudo systemctl status crond),脚本路径和权限正确

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

火山引擎 最新活动