使用Terraform+GitHub Actions创建独立Azure容器实例遇资源替换问题
我正尝试基于GitHub Actions实现自助流水线,通过Terraform部署Azure容器实例(ACI)等基础设施,目标为每个feature/**分支触发部署同名容器组,且所有资源需共存直至手动销毁。
目前遇到的问题是:当分支名称(对应资源名称)变更时,Terraform会销毁原有容器组并创建新资源,而非新增独立资源。我未使用Terraform Cloud,想到的临时方案是通过bash脚本向backend.tf注入变量,为每个分支创建独立Azure远程后端存储状态,但Terraform本身不支持在backend配置中使用内置变量,不确定是否有更优方案。
当前GitHub Actions工作流如下:
permissions: id-token: write # required to use OIDC authentication contents: read # required to checkout the code from the repo name: feature-deploy on: push: branches: - "feature/**" jobs: build: name: build-feature # ... 构建Docker镜像并上传到Azure容器注册表的工作流 deploy-aci: name: deploy-aci runs-on: ubuntu-latest env: BRANCH_NAME: ${{ github.ref_name }} ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} ARM_USE_OIDC: true defaults: run: working-directory: aci/terraform/azure-container-instances needs: build steps: - name: Checkout aci terraform repo uses: actions/checkout@v3 with: repository: MyOrg/azure-feature-environments path: aci token: ${{ secrets.ACI_REPOSITORY_ACCESS_TOKEN }} - name: Setup terraform ENV variables run: echo "TF_VAR_branch_name=${{ env.BRANCH_NAME }}" >> $GITHUB_ENV - name: Setup terraform uses: hashicorp/setup-terraform@v2 - name: Terraform Init id: init run: terraform init - name: Terraform Validate id: validate run: terraform validate -no-color - name: Terraform Plan id: plan run: terraform plan -no-color -input=false - name: Terraform Apply run: terraform apply -auto-approve -input=false
已尝试操作:假设Terraform已部署名为branch1的资源,推送branch2分支触发工作流,传入分支名称作为环境变量。
预期结果:创建名为branch2的新容器组及配套资源,不销毁branch1资源。
实际结果:Terraform销毁branch1资源,替换为branch2资源。
问题的核心是所有分支共用同一个Terraform状态文件。Terraform通过状态文件跟踪已部署的资源,当你切换分支运行apply时,Terraform会对比当前代码定义的资源(对应新分支)和状态文件中的资源(对应旧分支),认为旧分支的资源已被移除,因此会销毁旧资源并创建新资源。
要实现分支间资源共存,必须让每个分支拥有独立的Terraform状态文件,确保各分支的资源状态互不干扰。
以下是两种无需Terraform Cloud的可行方案,均能实现分支独立部署:
方案1:使用Terraform Workspaces
Terraform Workspaces允许在同一个配置下维护多个独立状态,每个Workspace对应一个分支。Azure Blob Storage作为后端时,会自动为每个Workspace创建独立的状态文件路径。
修改步骤:
更新GitHub Actions工作流,在
Terraform Init步骤前添加Workspace选择/创建逻辑:- name: Select or Create Terraform Workspace run: | terraform workspace select ${{ env.BRANCH_NAME }} || terraform workspace new ${{ env.BRANCH_NAME }}此命令会尝试切换到与分支同名的Workspace,若不存在则自动创建。
确保Terraform资源命名唯一,在代码中使用
var.branch_name作为资源名称的前缀或组成部分,避免跨Workspace资源冲突:variable "branch_name" { type = string description = "Name of the feature branch, used to unique resource names" } resource "azurerm_container_group" "aci" { name = "${var.branch_name}-container-group" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_address_type = "Public" dns_name_label = "${var.branch_name}-aci-dns" # 需确保全局唯一 os_type = "Linux" container { name = "app-container" image = "myacr.azurecr.io/myapp:${var.branch_name}" cpu = "1.0" memory = "1.5" ports { port = 80 protocol = "TCP" } } }
方案2:动态指定Backend状态文件路径
通过terraform init的-backend-config参数,为每个分支指定独立的状态文件存储路径,避免共用同一个状态文件。
修改步骤:
更新GitHub Actions的Terraform Init步骤,动态传入状态文件的
key路径:- name: Terraform Init id: init run: terraform init -backend-config="key=aci/${{ env.BRANCH_NAME }}/terraform.tfstate"此配置会将每个分支的状态文件存储到Azure Blob容器的
aci/[分支名]/terraform.tfstate路径下,实现状态隔离。同方案1,确保Terraform资源名称包含
var.branch_name,避免资源命名冲突。
分支名称特殊字符处理:Azure资源名称不允许包含
/、@等特殊字符,需在GitHub Actions中预处理分支名:env: BRANCH_NAME: ${{ replace(github.ref_name, '/', '-') }}将分支名中的
/替换为-,符合Azure命名规则。权限控制:确保GitHub Actions使用的OIDC身份对Azure存储账户拥有
Storage Blob Data Contributor权限,能读写对应的状态文件路径。资源清理:如需销毁某个分支的资源,可在对应分支运行
terraform destroy,或直接删除该分支对应的Workspace/状态文件后手动清理Azure资源。
内容的提问来源于stack exchange,提问作者alig227

