aws-samples / aws-samples/sample-eks-enterprise-quickstart

首次 apply 时的两个 race condition:CA Pod Identity 传播延迟 + ALB Controller webhook 时序

Open
#2 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Shell
Stars
5
Forks
3
Avg merge
34m
Merged PRs (30d)
2

Description

## 现象

在全新账号上跑 `terraform apply` 时遇到两个 race condition,都发生在 terraform 资源创建完成 ↔ K8s 控制器真正 ready 这个边界。

### 1. Cluster Autoscaler ↔ Pod Identity Association

**症状**:首次 apply 看似成功完成,但 `cluster-autoscaler` pod 一直 CrashLoopBackOff。

**完整 pod 日志**(部署后约 12 分钟):

```
I0531 12:27:03.374052 1 aws_manager.go:81] AWS SDK Version: 1.40.1
I0531 12:27:03.374111 1 auto_scaling_groups.go:429] Regenerating instance to ASG map for ASG names: []
I0531 12:27:03.374118 1 auto_scaling_groups.go:436] Regenerating instance to ASG map for ASG tags: map[k8s.io/cluster-autoscaler/: k8s.io/cluster-autoscaler/enabled:]
E0531 12:27:03.400875 1 aws_manager.go:133] Failed to regenerate ASG cache: operation error Auto Scaling: DescribeAutoScalingGroups, https response error StatusCode: 403, RequestID: , api error AccessDenied: User: arn:aws:sts:::assumed-role/EKSNodeRole-/ is not authorized to perform: autoscaling:DescribeAutoScalingGroups because no identity-based policy allows the autoscaling:DescribeAutoScalingGroups action
F0531 12:27:03.400899 1 aws_cloud_provider.go:469] Failed to create AWS Manager: ...
```

注意 caller 是 `assumed-role/EKSNodeRole-` —— **节点 instance role**,不是 Pod Identity 关联的 role。说明 SDK 没拿到 Pod Identity 注入的 credentials,fallback 到了节点 IRSA,而节点 role 没 autoscaling 权限,导致 AccessDenied。

**确认 association + role 都是对的**:

```bash
$ aws eks list-pod-identity-associations --cluster-name --region us-west-2 \
--query 'associations[?serviceAccount==`cluster-autoscaler`]'
[
{
"clusterName": "",
"namespace": "kube-system",
"serviceAccount": "cluster-autoscaler",
"associationArn": "arn:aws:eks:us-west-2::podidentityassociation//",
"associationId": ""
}
]

$ aws iam get-role-policy --role-name -cluster-autoscaler \
--policy-name ClusterAutoscalerPolicy --query 'PolicyDocument.Statement[*].Action[]' --output json
[
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
...
]
```

association 有,role policy 也有 `DescribeAutoScalingGroups` —— 但 pod 没用上。

**关键证据**:CA pod env 里**没有** Pod Identity 注入应该有的 `AWS_CONTAINER_CREDENTIALS_FULL_URI` / `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE`:

```yaml
$ kubectl get pod -n kube-system -o yaml | grep -A 30 'env:'
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: AWS_REGION
value: us-west-2
```

**Workaround**:`kubectl rollout restart deployment cluster-autoscaler -n kube-system`,新 pod 立刻拿到 `AWS_CONTAINER_CREDENTIALS_FULL_URI` env,正常拉 ASG list:

```
$ kubectl rollout restart deployment cluster-autoscaler -n kube-system
deployment.apps/cluster-autoscaler restarted

$ sleep 30; kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-cluster-autoscaler
NAME READY STATUS RESTARTS AGE
cluster-autoscaler- 1/1 Running 0 37s
```

**根因**:terraform 这边 `helm_release.cluster_autoscaler` 已经有 `depends_on = [aws_eks_pod_identity_association.cluster_autoscaler]`(参见 `terraform/modules/eks-addons/main.tf` line ~270),顺序是对的。但 EKS 控制面 association 创建调用**返回成功** ≠ Pod Identity Agent 的 admission webhook cache 已经识别这个 association。第一个被 webhook 处理的 CA pod 没拿到 `AWS_CONTAINER_*` env → SDK fallback 到节点 IRSA。

---

### 2. metrics-server ↔ ALB Controller webhook

**症状**:首次 apply 在 `aws_eks_addon.metrics_server` 失败:

```
module.eks_addons.aws_eks_addon.coredns: Creation complete after 24s [id=:coredns]
module.eks_addons.aws_eks_addon.metrics_server: Still creating... [00m30s elapsed]


│ Error: waiting for EKS Add-On (:metrics-server) create: unexpected state 'CREATE_FAILED', wanted target 'ACTIVE'. last error: : AdmissionRequestDenied: Internal error occurred: failed calling webhook "mservice.elbv2.k8s.aws": failed to call webhook: Post "https://aws-load-balancer-webhook-service.kube-system.svc:443/mutate-v1-service?timeout=10s": no endpoints available for service "aws-load-balancer-webhook-service"

│ with module.eks_addons.aws_eks_addon.metrics_server,
│ on modules/eks-addons/main.tf line 44, in resource "aws_eks_addon" "metrics_server":
│ 44: resource "aws_eks_addon" "metrics_server" {
```

注意:`module.eks_addons.helm_release.alb_controller: Creation complete after 19s` —— helm release 已经"成功",但实际 ALB Controller pod / webhook endpoint 还没真正 ready。

**Workaround**:直接重跑 `terraform apply`(idempotent,这次能过,因为 ALB Controller 已经完全起来了)。

**根因**:
1. `aws_eks_addon.metrics_server` 没有显式 `depends_on = [helm_release.alb_controller]`(参见 `terraform/modules/eks-addons/main.tf` line 44),terraform 调度上可能在 ALB 之前就开始创建。
2. 即便有 depends_on,helm provider 默认 `wait=true` 是等 Deployment Available 就返回,但 webhook 的 Service endpoint 注册有几秒滞后。

ALB Controller 装好后会注册一个 admission webhook,拦截集群里**所有 Service 的 mutation**。metrics-server addon 创建过程里要建一个 Service → 触发 webhook → 此时 webhook Service 还没 endpoint → AdmissionRequestDenied。

---

## 可能的修法

这些都有 caveat,留给 maintainer 决定:

**Option A:加 `time_sleep` 强制等传播**

```hcl
resource "time_sleep" "wait_for_pod_identity_propagation" {
create_duration = "30s"
depends_on = [aws_eks_pod_identity_association.cluster_autoscaler]
}

resource "helm_release" "cluster_autoscaler" {
...
depends_on = [time_sleep.wait_for_pod_identity_propagation]
}
```

需要在 `versions.tf` 加 `time` provider。

优点:简单。
缺点:30s 是经验值,EKS 服务端响应变慢时还是可能 race。同样的模式要在 metrics-server / Karpenter / 其他 helm release 各处复制一次。

**Option B:用 `null_resource` provisioner 显式 `kubectl wait`**

等 ALB webhook 的 `endpoints` 真正 populate 出来再继续 metrics-server:

```hcl
resource "null_resource" "wait_alb_webhook_ready" {
provisioner "local-exec" {
command = "kubectl wait --for=jsonpath='{.subsets[*].addresses[0].ip}' endpoints/aws-load-balancer-webhook-service -n kube-system --timeout=120s"
}
depends_on = [helm_release.alb_controller]
}

resource "aws_eks_addon" "metrics_server" {
depends_on = [null_resource.wait_alb_webhook_ready]
...
}
```

优点:确定性。
缺点:apply runner 必须装 kubectl,而且要配好 kubeconfig(用 `local-exec` 时这通常 OK,但跨平台兼容性要注意)。

**Option C:不改代码,只在 README 说明**

README 加一段:"如果首次 apply 在 metrics-server 失败,重跑 apply 即可;如果 CA pod CrashLoopBackOff,跑 `kubectl rollout restart deployment cluster-autoscaler -n kube-system`"。

优点:零代码改动。
缺点:对 `aws-samples` 这种面向新用户的仓库 first-impression 不好。

我个人倾向 **Option C** —— 在 terraform 代码里背这些 EKS 服务端传播延迟的 workaround 维护成本高,而且 30s sleep 这种经验值难证明在所有 region / 所有时段都够。但这是你的选择。

如果你倾向 A 或 B,我可以提对应的 PR。

---

## 复现环境

- AWS 账号:全新(此前没用过 EKS,无任何 EKS / EKS-NG / AutoScaling / Spot / ELB SLR 预存)
- Region: us-west-2
- 仓库 commit: `110da9c` (main HEAD at time of repro)
- terraform: 1.10.x
- AWS provider: 6.x
- helm provider: 2.17.x
- kubernetes provider: 2.33.x
- EKS K8s 版本: 1.35
- 跑的 stack: 默认 on 部分(VPC Endpoints + EKS + System NG + Addons + EBS CSI + Cluster Autoscaler + ALB Controller),不带 Karpenter / 不带 GPU NG。

第一次 `terraform apply` 同时复现两个问题。重跑 apply 后 metrics-server 能过,CA 仍然需要手动 `kubectl rollout restart`。

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.