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

Provisioner local-exec的always_run触发器未按预期运行问题咨询

根因解释

你配置的timestamp()触发器确实会让null_resource每次apply都重建、执行对应local-exec脚本,问题核心出在Terraform的执行阶段顺序不匹配,以及隧道脚本本身的缺陷:

  • Terraform运行分为plan和apply两个核心阶段,所有Provider的初始化(包括MySQL Provider建立数据库连接的逻辑)是在plan阶段提前完成的,而local-exec provisioner只有在apply阶段、资源触发重建时才会执行。第二次执行terraform apply时,还没等到执行建隧道的脚本,plan阶段的MySQL Provider就已经尝试连接本地3306端口,此时隧道还未建立,直接抛出连接拒绝的错误,根本不会走到apply阶段执行你的隧道脚本。
  • 即使执行顺序没问题,你当前的隧道脚本也存在缺陷:如果本地3306端口已经被之前残留的隧道进程占用,新的ssh隧道命令会直接执行失败,同样会导致连接失败。
合理实现方案

方案1:将隧道逻辑剥离到Terraform执行前的前置脚本(最推荐)

不要把隧道这种依赖外部环境的操作放到Terraform资源生命周期里管理,单独拆分执行流程即可:

  1. 编写独立的前置脚本,逻辑包含:杀残留隧道进程、新建SSH隧道、等待隧道就绪
  2. 执行terraform apply
  3. 执行完成后自动销毁SSH隧道

示例前置脚本逻辑:

# 清理旧隧道进程
pkill -f "3306:${RDS_ENDPOINT}" || true
# 新建隧道
ssh -i ${PRIVATE_KEY} -L 3306:${RDS_ENDPOINT} -fN ec2-user@${BASTION_IP}
# 等待隧道转发就绪
sleep 3

方案2:将数据库操作也改为local-exec实现

如果你一定要把所有逻辑放到Terraform配置里,可以放弃官方MySQL Provider,改用本地执行mysql命令的方式完成库操作,这样所有逻辑都在apply阶段按依赖顺序执行,不会出现顺序错位:

# 创建SSH隧道
resource "null_resource" "ssh_tunnel" {
  provisioner "local-exec" {
    command = <<EOT
    pkill -f "3306:${var.rds_endpoint}" || true
    ssh -i ${var.private_key} -L 3306:${var.rds_endpoint} -fN ec2-user@${var.bastion_ip} -v >./stdout.log 2>./stderr.log
    sleep 3
    EOT
  }
  triggers = {
    always_run = timestamp()
  }
}

# 通过mysql客户端创建数据库
resource "null_resource" "create_db" {
  provisioner "local-exec" {
    command = "mysql -h 127.0.0.1 -u ${var.db_user} -p${var.db_password} -e 'CREATE DATABASE IF NOT EXISTS ${var.db_name};'"
  }
  depends_on = [null_resource.ssh_tunnel]
  triggers = {
    always_run = timestamp()
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 07:24:01