在传统的 EKS 架构中,我们总要先买几台 EC2 作为底座。但今天,我们将挑战最高难度的"真空启动":在一个没有任何节点的 EKS-Dev-SGB-Demo-02 集群中,直接注入 Karpenter,让它成为唯一的算力造物主。
第一阶段:打通环境"路标" (Tagging)
Karpenter 不会自动乱买机器,我们需要给它指定"地盘"。根据你截图中的网络信息,我们需要给指定的子网(Subnet)和安全组(Security Group)打上特定的标签。
-
给子网打标签 :找到你截图里的
subnet-070c55a...、subnet-09ca880...等子网,给它们加上标签:karpenter.sh/discovery:EKS-Dev-SGB-Demo-02
-
给安全组打标签 :找到你截图里的集群安全组
sg-0907dbb657ed29a5c,加上同样的标签:karpenter.sh/discovery:EKS-Dev-SGB-Demo-02
原理:Karpenter 启动后,会自动扫描带这个标签的网络资源,以后买的新机器都会塞进这些子网里。
第二阶段:打破死锁,拉起 Karpenter 的"大脑"
既然没有 EC2,我们就用 AWS Fargate(Serverless 容器)来跑 Karpenter 控制器。
1. 创建 Fargate Profile
我们需要告诉 EKS:"只要是部署在 karpenter 命名空间里的 Pod,统统用 Fargate 跑,不要找我要物理机。"
通过命令行执行(使用 eksctl):
eksctl create fargateprofile \
--cluster EKS-Dev-SGB-Demo-02 \
--name karpenter-profile \
--namespace karpenter
2. 准备 IAM 权限 (IRSA)
Karpenter 需要买机器的权限。我们需要建立 OIDC 关联,并为它创建一个 IAM 角色(这里省略复杂的 JSON 策略,直接用 eksctl 一键搞定):
eksctl create iamserviceaccount \
--cluster EKS-Dev-SGB-Demo-02 --name karpenter --namespace karpenter \
--role-name "KarpenterControllerRole-SGB-Demo" \
--attach-policy-arn "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeProvisionerPolicy" \
--role-only \
--approve
3. Helm 安装 Karpenter
现在环境就绪,直接使用 Helm 把 Karpenter 部署到我们刚刚划定的 Fargate 空间里:
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version 1.0.0 \
--namespace karpenter --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="arn:aws:iam::YOUR_ACCOUNT_ID:role/KarpenterControllerRole-SGB-Demo" \
--set settings.clusterName=EKS-Dev-SGB-Demo-02 \
--wait
此时执行 kubectl get pods -n karpenter,你会看到 Karpenter 正在 Fargate 的加持下成功启动!死锁打破!
第三阶段:下发"造机法则" (NodePool & EC2NodeClass)
Karpenter 脑子虽然启动了,但还不知道你要买什么样的机器。在最新的 Karpenter API 中,我们需要下发两个核心配置。
创建一个 karpenter-rules.yaml 文件:
---
# 1. 定义 AWS 相关的云资源参数 (EC2NodeClass)
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2023 # 使用最新的 Amazon Linux 2023
role: "KarpenterNodeRole-SGB-Demo" # 新机器用的 IAM 角色
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "EKS-Dev-SGB-Demo-02" # 认准我们刚才打标签的子网
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "EKS-Dev-SGB-Demo-02" # 认准我们刚才打标签的安全组
---
# 2. 定义机器规格和缩容逻辑 (NodePool)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"] # 优先买 Spot 实例,省钱!
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"] # 支持按需买 x86 或 Graviton 芯片
# 极其激进的缩容策略:机器空闲 30 秒立刻关机退款
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 30s
执行 kubectl apply -f karpenter-rules.yaml。
第四阶段:见证奇迹的时刻
现在你的集群依然是 0 个 EC2 节点。让我们随便扔一个 Nginx 上去:
kubectl create deployment nginx --image=nginx --replicas=5
发生了什么?
-
EKS 发现有 5 个 Nginx Pod 需要运行,但没有机器,状态变为
Pending。 -
跑在 Fargate 里的 Karpenter 瞬间捕捉到这个求救信号。
-
它计算出 5 个 Nginx 只需要极小的资源,于是直接调用 AWS API。
-
不到 45 秒钟 ,一台全新的、最便宜的 EC2(比如
t3.medium)被造了出来,加入集群,5 个 Nginx 瞬间变为Running。 -
当你执行
kubectl scale deployment nginx --replicas=0时,Karpenter 发现机器空了,30 秒后自动触发 Terminate,物理机关机,停止计费。
至此,你完成了一个最高形态的 EKS 架构演进:无极弹性,0 资源闲置。
现在要求起一批gpu需求的pod,而且不要让其他pod调用到这批机器上。
核心逻辑:给 GPU 机器装上"专用锁"
我们要创建一个专门针对 GPU 的 NodePool。这个配置包含两个核心要点:
-
Taints (污点/锁):给拉起来的 GPU 机器加一把锁,默认禁止任何人进入。
-
Requirements (规格):限定 Karpenter 只能去买带显卡的机器(如 G5, P4 家族)。
1. 编写 GPU 专属 NodePool (karpenter-gpu-pool.yaml)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: gpu-pool
spec:
template:
spec:
# 【关键:这就是那把锁】
# 任何被这套规则创建出来的机器,都会自带这三个污点
taints:
- key: "nvidia.com/gpu"
value: "true"
effect: NoSchedule
- key: "sku"
value: "gpu-exclusive"
effect: NoSchedule
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default # 复用之前的网络配置
requirements:
# 限定只买 GPU 机型 (G系列或P系列)
- key: "node.kubernetes.io/instance-type"
operator: In
values: ["g5.2xlarge", "g5.4xlarge", "p4d.24xlarge"]
- key: "karpenter.sh/capacity-type"
operator: In
values: ["on-demand"] # 训练任务通常建议用按需实例
2. 编写 GPU Pod 配置 (带着钥匙进来)
现在,如果你提交一个普通的 Nginx,它绝对不会落在这批机器上,因为 Nginx 没有"钥匙"。
你的 GPU 任务必须在 YAML 中声明:
-
Tolerations (容忍度/钥匙):能够忍受上面的污点。
-
NodeSelector (定向投放):明确要求去有 GPU 的机器。
GPU 任务 YAML 示例:
apiVersion: v1
kind: Pod
metadata:
name: gpu-training-task
spec:
# 【钥匙 1】:必须配置容忍度,才能在带污点的机器上运行
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
- key: "sku"
operator: "Equal"
value: "gpu-exclusive"
effect: "NoSchedule"
# 【钥匙 2】:明确要求使用显卡资源
containers:
- name: cuda-container
image: nvidia/cuda:12.0-base
resources:
limits:
nvidia.com/gpu: 1 # 申请 1 块显卡
3. 为什么这样配置能实现你的要求?
我们可以从两个视角来看这个自动化的过程:
场景 A:普通的 Java/前端 Pod 进来
-
EKS 发现这些 Pod 没有任何
Tolerations。 -
Karpenter 查看
gpu-pool,发现这批机器都有"锁"(Taint)。 -
Karpenter 判定:
gpu-pool无法服务这些普通 Pod。 -
Karpenter 会转而使用
default队列去买普通的t3.medium这种便宜机器,绝对不会浪费昂贵的 GPU 资源给普通应用。
场景 B:带 GPU 需求的训练 Pod 进来
-
EKS 发现 Pod 申请了
nvidia.com/gpu: 1。 -
Karpenter 扫描
NodePool,发现只有gpu-pool能满足 GPU 需求。 -
虽然
gpu-pool有锁,但这个 Pod 刚好带着匹配的"钥匙"(Tolerations)。 -
Karpenter 瞬间调用 AWS API,拉起一台 G5 实例。