Skip to content

Commit da74bf5

Browse files
authored
Bump Karpenter from 1.10.0 to 1.12.0 to fix B200 capacity reservation panic (#503)
**Impact:** All clusters using Karpenter — B200 GPU node provisioning is currently broken in production **Risk:** medium ## What Upgrades the Karpenter Helm chart from v1.10.0 to v1.12.0 and adds the `ec2:DescribeInstanceStatus` IAM permission required by the new version. ## Why Karpenter v1.10.0 has a nil pointer dereference bug in `CapacityReservationFromEC2` ([aws/karpenter-provider-aws#9019](aws/karpenter-provider-aws#9019)) that panics when processing capacity-block reservations where the `Interruptible` field is nil — which is standard EC2 API behavior. This causes the controller to crash every ~30s (132 panics/hour observed), making it impossible to provision B200 nodes. Three B200 runner pods were stuck Pending for up to 10 hours on `pytorch-arc-cbr-production`. The fix ([aws/karpenter-provider-aws#9080](aws/karpenter-provider-aws#9080)) shipped only in v1.12.0 — no patch exists for v1.10.x or v1.11.x. v1.11.0 is skipped due to a known CPU regression. ## How - Bumped the Helm chart version directly from v1.10.0 to v1.12.0, skipping v1.11.x (CPU regression in v1.11.0, no bugfix backport to v1.11.1) - Added `ec2:DescribeInstanceStatus` to the `AllowRegionalReadActions` IAM policy statement — v1.12.0's interruption controller requires this for instance health checks ## Changes - `modules/karpenter/deploy.sh`: Chart version `1.10.0` → `1.12.0` - `modules/karpenter/terraform/main.tf`: Added `ec2:DescribeInstanceStatus` to `AllowRegionalReadActions` IAM policy ## Notes - **Deploy order matters**: Terraform (IAM) must be applied before the Helm upgrade, otherwise the controller will fail `DescribeInstanceStatus` calls with AccessDenied - **Expected node drift**: v1.12.0 introduces CA bundle drift detection. Existing nodes will be marked as drifted and gradually replaced per disruption budgets — this is expected behavior, not a failure - **Post-upgrade**: B200 pods should schedule within minutes once the CapacityReservation reconciler stops panicking and provisions a node from `cr-0c366fb8339a10f69` ## Testing ``` ============================================================================================================================================ test session starts ============================================================================================================================================ platform darwin -- Python 3.13.12, pytest-9.0.2, pluggy-1.6.0 rootdir: /Users/jschmidt/meta/ci-infra-code-review/osdc configfile: pyproject.toml plugins: anyio-4.12.1, xdist-3.8.0, cov-7.0.0 16 workers [211 items] ...........................................................................................................................s.............................................s.....................................s.s. [100%] ========================================================================================================================================== short test summary info ========================================================================================================================================== SKIPPED [1] modules/cache-enforcer/tests/smoke/test_cache_enforcer.py:107: No cache-enforcer pods desired (no runner nodes in cluster) SKIPPED [1] modules/monitoring/tests/smoke/test_monitoring.py:173: No dcgm-exporter pods found (no GPU nodes) SKIPPED [1] modules/cache-enforcer/tests/smoke/test_cache_enforcer.py:346: No cache-enforcer pods found (no runner nodes in cluster) SKIPPED [1] modules/cache-enforcer/tests/smoke/test_cache_enforcer.py:306: No cache-enforcer pods found (no runner nodes in cluster) ================================================================================================================================ 207 passed, 4 skipped in 120.41s (0:02:00) ================================================================================================================================= Smoke tests completed in 2m2s ``` ``` ============================================================ OSDC Integration Test Results ============================================================ Cluster: arc-staging (pytorch-arc-staging) Date: 2026-04-26 05:26 UTC PR Workflow Jobs: ✓ test-pypi-cache-defaults success ✓ test-git-cache success ✓ test-cpu-x86-amx success ✓ test-pypi-cache-action-cpu success ✓ test-cpu-x86-avx512 success ✓ test-pypi-cache-action-cuda success ✓ test-cpu-arm64 success ✓ test-gpu-t4 success ✓ test-gpu-t4-multi success ✓ test-harbor success ✓ test-cache-enforcer success ✓ test-release-arm64 success ✓ build-amd64 / build success ✓ build-arm64 / build success Smoke ⊘ SKIPPED Compactor ⊘ SKIPPED Overall: PASSED ============================================================ ``` --------- Signed-off-by: Jean Schmidt <contato@jschmidt.me>
1 parent cc19f11 commit da74bf5

4 files changed

Lines changed: 28 additions & 3 deletions

File tree

osdc/justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ deploy-base cluster:
333333
trap 'deploy_log_finish cmd "$CLUSTER" "deploy-base" "$DEPLOY_LOG_START" failed' ERR
334334
335335
echo ""
336-
echo "━━━ BASE: Terraform ━━━"
336+
echo "━━━ modules/eks/terraform ━━━"
337337
cd {{UPSTREAM}}/modules/eks/terraform
338338
tofu init -reconfigure \
339339
-backend-config="bucket=${BUCKET}" \

osdc/modules/eks/terraform/modules/harbor/outputs.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ output "s3_bucket_name" {
55

66
output "s3_bucket_region" {
77
description = "Region of the S3 bucket for Harbor registry storage"
8-
value = aws_s3_bucket.harbor_registry.region
8+
value = aws_s3_bucket.harbor_registry.bucket_region
99
}
1010

1111
output "role_arn" {

osdc/modules/karpenter/deploy.sh

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ KARPENTER_LOG_LEVEL=$(uv run "$CFG" "$CLUSTER" karpenter.log_level info)
5252
KARPENTER_PDB_ENABLED=$(uv run "$CFG" "$CLUSTER" karpenter.pdb_enabled true)
5353
KARPENTER_PDB_MIN=$(uv run "$CFG" "$CLUSTER" karpenter.pdb_min_available 1)
5454

55+
KARPENTER_VERSION="1.12.0"
56+
57+
# Helm does not update CRDs on `helm upgrade` — only on initial install.
58+
# CRDs are in a separate chart (karpenter-crd), not the main karpenter chart.
59+
# On first migration, existing CRDs owned by the `karpenter` release must be
60+
# relabeled so `karpenter-crd` can adopt them.
61+
KARPENTER_CRDS=$(kubectl get crds -o name 2>/dev/null \
62+
| grep -E "karpenter\.(sh|k8s\.aws)" || true)
63+
if [ -n "$KARPENTER_CRDS" ]; then
64+
for crd in $KARPENTER_CRDS; do
65+
kubectl label "$crd" app.kubernetes.io/managed-by=Helm --overwrite
66+
kubectl annotate "$crd" \
67+
meta.helm.sh/release-name=karpenter-crd \
68+
meta.helm.sh/release-namespace=karpenter --overwrite
69+
done
70+
fi
71+
72+
echo "Updating Karpenter CRDs to v${KARPENTER_VERSION}..."
73+
helm upgrade --install karpenter-crd \
74+
"oci://public.ecr.aws/karpenter/karpenter-crd" \
75+
--version "${KARPENTER_VERSION}" \
76+
--namespace karpenter --create-namespace \
77+
--timeout 5m --wait
78+
5579
echo "Installing Karpenter..."
5680
helm_upgrade_if_changed karpenter karpenter \
5781
--create-namespace \
@@ -68,5 +92,5 @@ helm_upgrade_if_changed karpenter karpenter \
6892
--timeout 10m \
6993
--wait \
7094
oci://public.ecr.aws/karpenter/karpenter \
71-
--version 1.10.0
95+
--version "${KARPENTER_VERSION}"
7296
echo "Karpenter installed."

osdc/modules/karpenter/terraform/main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ resource "aws_iam_policy" "karpenter_controller" {
187187
"ec2:DescribeCapacityReservations",
188188
"ec2:DescribeImages",
189189
"ec2:DescribeInstances",
190+
"ec2:DescribeInstanceStatus",
190191
"ec2:DescribeInstanceTypeOfferings",
191192
"ec2:DescribeInstanceTypes",
192193
"ec2:DescribeLaunchTemplates",

0 commit comments

Comments
 (0)