Terraform模块中资源间竞态条件的正确解决方法
你在Terraform模块中定义了安装Lambda依赖、打包源码的逻辑,配置代码如下:
resource "null_resource" "install_dependencies" { provisioner "local-exec" { command = "pip install -r requirements.txt -t . ; exit" } triggers = { dependencies_versions = filemd5(".${var.package_requirements_path}") } } data "archive_file" "lambda_source_package" { type = "zip" source_dir = ".${var.source_directory}" output_path = var.zipped_lambda }
实际运行时出现两个问题:
- 打包逻辑早于pip依赖安装执行,导致包内容不全
- 手动给
archive_file加depends_on后依然报错,提示找不到生成的zip包:│ Error: unable to load "../outputs/xx.zip": open ../outputs/xx.zip: no such file or directory
官方文档提到的「用表达式引用隐式声明依赖替代depends_on」,本质是Terraform的自动依赖解析规则:
只要某个资源/数据源的配置参数中,直接引用了另一个对象的输出属性,Terraform就会自动判定两者的执行先后顺序,不需要手动声明依赖。
这种依赖关系是精准绑定到具体属性的:只有被引用的属性值发生变化时,才会触发后续资源的更新,不会出现depends_on带来的无意义全量替换问题,因此更适合模块开发场景。
你之前加depends_on依然失效的核心原因不是依赖声明没用,是Terraform对数据源(data块)的执行时机规则:如果一个数据源的所有配置参数在plan阶段就能拿到确定值,Terraform会直接在计划生成阶段就执行数据源逻辑,不会等到apply阶段——哪怕你写了depends_on,只要参数值全已知,计划阶段就会提前跑打包,这时候null_resource还没开始执行,pip依赖没装、甚至输出目录都没生成,自然会报找不到zip包的错误。
另外你原配置还有个路径bug:pip安装命令把依赖装到了当前工作目录(-t .),但打包的源目录是var.source_directory指向的路径,依赖文件根本不在打包范围内,哪怕执行顺序对,打出来的包也会缺依赖。
按以下步骤调整配置即可,核心依赖关系不需要手动写depends_on:
- 把
archive_file从数据源(data块)改成资源(resource块),资源的默认执行时机是apply阶段,不会提前到plan阶段跑 - 修正pip安装的目标路径,直接把依赖装到打包源码目录下
- 在
archive_file的配置中引用null_resource的输出属性,建立隐式依赖,保证Terraform能识别执行顺序,这里选不影响打包结果的参数引用即可,比如用永远匹配不到文件的排除规则绑定依赖 - 提前创建zip包的输出目录,避免路径不存在报错
调整后的完整配置如下:
# 提前创建输出目录,避免找不到路径报错 resource "null_resource" "create_output_dir" { provisioner "local-exec" { command = "mkdir -p ${dirname(var.zipped_lambda)}" } } resource "null_resource" "install_dependencies" { provisioner "local-exec" { # 修正安装路径,直接把依赖装到源码目录 command = "pip install -r ${var.package_requirements_path} -t ${var.source_directory}" } triggers = { dependencies_versions = filemd5(var.package_requirements_path) source_code_hash = filemd5("${var.source_directory}/index.py") # 可根据你的入口文件调整,源码变更自动触发重装 } } resource "archive_file" "lambda_source_package" { type = "zip" source_dir = var.source_directory output_path = var.zipped_lambda # 隐式依赖:引用null_resource的id属性,强制等依赖安装完再打包 # 排除规则匹配永远不存在的文件,完全不影响打包结果 excludes = [".tf_never_match_${null_resource.install_dependencies.id}"] # 依赖输出目录创建完成 depends_on = [null_resource.create_output_dir] }
后续你在Lambda函数资源中引用archive_file.lambda_source_package.output_path时,Terraform会自动按「创建输出目录→安装依赖→打包zip→部署Lambda」的顺序执行,不会出现提前打包的问题,而且只有当依赖版本、源码内容变化时才会重新打包、更新Lambda,不会产生无意义的资源替换。
内容的提问来源于stack exchange,提问作者theSekyi

