Terraform部署AKS添加NSG安全规则首次执行报404异常求助
我之前也碰到过一模一样的情况——AKS自动生成的MC资源组和NSG总是跟不上Terraform的节奏,第一次terraform apply必报404,第二次又能成功。你加的sleep其实只是“赌运气”,根本解决不了本质的竞态问题,因为AKS创建NSG的时机和helm部署ingress的时间没有强关联,就算睡60秒,也可能刚好NSG还没创建完成。
问题根源
你现在用external数据源去查询NSG,但这个查询的时机完全没和AKS的创建进度绑定。哪怕你让它依赖helm_release.ingress,helm部署完成不代表AKS的底层网络资源(比如NSG)已经就绪。AKS创建MC资源组和NSG是在集群初始化的后期阶段,很可能helm都部署完了,NSG还在后台创建中,这时候external数据源查不到NSG,自然就报错了。
最可靠的解决方案:直接从AKS资源获取NSG信息
Terraform的azurerm_kubernetes_cluster资源本身就暴露了节点池的NSG ID,完全不需要用外部脚本去查。我们可以通过这个ID提取NSG的名称和所属资源组,同时让NSG规则直接依赖AKS资源,确保Terraform等待AKS完全创建完成后再去创建规则。
修改你的配置如下:
# 你的AKS集群定义保持不变 resource "azurerm_kubernetes_cluster" "aks" { name = "terraform-aks" location = "westeurope" resource_group_name = "terraform-aks-rg" dns_prefix = "terraform-aks" default_node_pool { name = "agentpool" node_count = 3 vm_size = "Standard_D2s_v3" } identity { type = "SystemAssigned" } tags = { Environment = "Production" } } # 从AKS资源中提取NSG的名称和资源组 locals { # 获取节点池的NSG ID node_nsg_id = azurerm_kubernetes_cluster.aks.default_node_pool[0].node_network_security_group_id # 从ID中拆分出NSG名称(Azure资源ID的第9段,索引从0开始是第8位) node_nsg_name = element(split("/", local.node_nsg_id), 8) # 拆分出NSG所属的资源组(ID的第5段,索引4) node_nsg_resource_group = element(split("/", local.node_nsg_id), 4) } # 创建NSG规则,直接使用上面的local变量,自动依赖AKS集群 resource "azurerm_network_security_rule" "https" { name = "myRule" priority = 100 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "443" source_address_prefix = "*" destination_address_prefix = "*" resource_group_name = local.node_nsg_resource_group network_security_group_name = local.node_nsg_name }
为什么这个方案能解决问题?
- Terraform的资源依赖是基于变量引用的:因为
azurerm_network_security_rule引用了azurerm_kubernetes_cluster的属性,Terraform会自动等待AKS集群完全创建完成(包括MC资源组和NSG)之后,才会执行NSG规则的创建操作,从根本上避免了竞态条件。 - 不需要依赖外部脚本和sleep,完全利用Terraform自身的资源依赖机制,比手动加延迟可靠得多。
如果你一定要保留外部查询的方式(不推荐)
如果因为某些原因必须用external数据源查询NSG,那至少要把external数据源的依赖改成直接依赖azurerm_kubernetes_cluster.aks,而不是helm release:
data "external" "aks_nsg" { program = ["bash", "./get-aks-nsg.sh"] # 确保AKS集群完全创建后再执行查询 depends_on = [azurerm_kubernetes_cluster.aks] } # 然后你的NSG规则依赖这个external数据源 resource "azurerm_network_security_rule" "https" { # ... 其他配置 ... network_security_group_name = data.external.aks_nsg.result.output resource_group_name = "MC_terraform-aks-rg_terraform-aks_westeurope" depends_on = [data.external.aks_nsg] }
同时,把你的查询脚本改得更精确,避免匹配到其他NSG:
#!/bin/bash # 精确匹配AKS节点池的NSG,名称通常是aks-agentpool-xxxx-nsg OUTPUT=$(az network nsg list --resource-group MC_terraform-aks-rg_terraform-aks_westeurope --query "[?contains(name, 'aks-agentpool')].name" -o tsv | head -n 1) jq -n --arg output "$OUTPUT" '{"output":$output}'
但还是那句话,这种方式不如直接用AKS资源的属性可靠,因为外部查询还是可能因为Azure API的延迟出现问题。
内容的提问来源于stack exchange,提问作者Mario Jacobo

