iT邦幫忙

2025 iThome 鐵人賽

DAY 15
1
Cloud Native

EKS in Practice:IaC × GitOps 實戰 30 天系列 第 15

[Day 15] Karpenter 讓節點更聰明:自動供應與彈性伸縮

  • 分享至 

  • xImage
  •  

前言

在 Day 13,我們透過 App-of-Apps 解決了「多應用管理」的問題;在 Day 14,我們用 Helm Manager 處理了「同一應用需要部署在多個 cluster」的挑戰。

接下來,我們的重點要轉到基礎設施層的 Helm Charts,也就是昨天在 Helm Manager 中介紹到的 infra charts。這些元件(像是 ingress-nginx、prometheus、karpenter)屬於所有專案 cluster 都需要安裝的基礎建設,因此會用 Helm Manager 架構來部署,確保多叢集之間的一致性。

今天的主角是 Karpenter —— 它能讓我們的 EKS 叢集從「固定 Node Group」進化到「動態調度與彈性節點供應」。

為什麼需要 Karpenter?

在我們的架構裡,最一開始是透過 EKS Managed Node Group 來提供節點。它背後其實就是一個 Auto Scaling Group (ASG),所以在擴縮容和彈性上會遇到一些常見限制:

  1. Scaling 速度慢:ASG 需要透過 Auto Scaling 流程(也就是 ASG 的 lifecycle 機制)去建立或釋放 EC2,常常要等上幾分鐘。對於 workload 突然暴增的情況,例如批次運算、突發流量的反應不夠快。
  2. Launch Template 綁定:一個 Node Group 只能綁定一個 Launch Template。這代表它能用的 instance type、磁碟、開機參數等都被鎖死。雖然可以在 Launch Template 裡放一個 instance type 列表,但彈性還是有限。
  3. 調度不夠聰明:Cluster Autoscaler 的工作方式是「把 Node Group 的數量調大或調小」,不會根據 Pod 的實際需求(CPU/GPU/Memory/Zone/Spot/OnDemand)去挑選最合適的 instance type。

相較之下,Karpenter 提供了更好的解法(這些優勢也在 AWS 官方文件中被強調):

  1. Scaling 速度快:不需被 ASG lifecycle 所限制,直接透過 EC2 Fleet API 建立節點,通常幾十秒就能把新的 node 加進來。
  2. 彈性高:可以根據 Pod 的需求挑選最合適的 instance type,不再受限於單一 Launch Template。此外,也可以在 NodeClass 中指定網路元件的 selector,讓 Karpenter 根據 tag 去套用需要的網路設定,提升管理彈性
  3. 成本優化:內建 Consolidation 機制,當 workload 減少時,會自動關閉空閒節點,甚至把 Pod 搬到更便宜或更合適的 instance 上。此外,Karpenter 也能根據 pod 的實際需求,選取已列出的 instance types 中最適合的機型。

簡單來說,Node Group 適合小規模、穩定需求;而 Karpenter 則是為大規模、變動性高的叢集而生。

Karpenter 的核心概念

在 v1.3+ 的版本中,Karpenter 把資源拆成三個物件:

  • NodeClass:AWS 層級的設定範本(AMI、AZ、Instance Type、Spot/OnDemand、網路配置)。舉例如下:

    apiVersion: karpenter.k8s.aws/v1
    kind: EC2NodeClass
    metadata:
      name: system
    spec:
    	### KEY POINT: AMI 選擇設定
    	### K8s 各版本適合的 AMI 可參考:https://github.com/awslabs/amazon-eks-ami/releases
      amiFamily: AL2
      amiSelectorTerms:
        - alias: al2@v20250304
      ### KEY POINT: Instance 的 EBS 設定
      blockDeviceMappings:
        - deviceName: /dev/xvda
          ebs:
            encrypted: true
            volumeSize: 20Gi
            volumeType: gp2      
      ### KEY POINT: 我們會在 EKS module 裡面就寫好各個 infra-charts 的 IRSA terraform module,幫我們建出各個 infra charts 所需要的 IAM role,後續範例都會這樣直接填入哦
      role: Karpenter-example-internal
      securityGroupSelectorTerms:
    	  ### KEY POINT: 開啟新的 instance 時,掛載有這個 tag 的 security group
        - tags:
            karpenter.sh/discovery: example-internal
      subnetSelectorTerms:
    	  ### KEY POINT: 將新的 instance 放到含有這個 tag 的 subnet 中
        - tags:
            karpenter.sh/discovery: example-internal
      tags:
        karpenter.sh/discovery: example-internal
    
  • NodePool:Kubernetes 層級的規則,描述哪些 Pod 可以使用、taints、labels。舉例如下:

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: system
    spec:
      limits:
        cpu: '10'
      template:
        metadata:
          labels:
            usage: system
        spec:
          expireAfter: 720h
          ### KEY POINT: Reference 到我們上面建立的 NodeClass
          nodeClassRef:
            group: karpenter.k8s.aws
            kind: EC2NodeClass
            name: system
          requirements:
    	      ### KEY POINT: 可以選擇使用 on-demand 或 spot instance
    	      ### Spot instance 通常會比 on-demand 再更便宜,但也相對更容易被收走
            - key: karpenter.sh/capacity-type
              operator: In
              values:
                - on-demand
            ### KEY POINT: 列出候選的機型
            - key: node.kubernetes.io/instance-type
              operator: In
              values:
                - t4g.small
                - t4g.medium
          ### KEY POINT: NodePool level 可以設置 Node Taint 讓我們方便管理
          taints:
            - effect: NoSchedule
              key: SystemOnly
              value: 'true'
    
  • NodeClaim:Karpenter 實際建立的節點,可以看作是 NodeClass + NodePool 的具體結果。NodeClaim 並不需要手動建立,而是 Karpenter 根據 Pod 的需求,自動生成的節點物件。

  • Consolidation 概念:Karpenter 除了能自動擴容以外,還能自動縮容。我們可以在 NodePool level 設置 disruption budget 以及 consolidation policy,當某些節點變得空閒時,Karpenter 會:

    • 嘗試把 Pod 搬到更少的節點上;
    • 若有更便宜或更合適的 instance type,也會自動替換;
    • 空閒的節點會被釋放,避免浪費成本。

    Consolidation 的細節可以參考我寫過的文章:Karpenter Consolidation & Disruption Budget


Helm Manager 架構下的 Karpenter 部署

延續 Day 14 的 Helm Manager 架構,Karpenter 是以 infra chart 的形式被管理

  • 同一張 Helm Chart:我們把 Karpenter(以及它的 CRDs)包裝成一張 customized chart(CRDs 放在 templates 裡面,如下截圖),這樣 Argo CD 就能一次處理好 CRD 和 controller 的安裝順序。
  • 多 cluster 部署:同一張 chart 會被部署到多個 cluster,只是注入的 values file 不同(如截圖中的右側 panel),來對應各 cluster 的需求。
  • Git directory generator:我們使用 ApplicationSet 的 Git generator,讓它自動掃描 infra-charts/karpenter/*-internal 目錄底下的 values.yaml,只要有新增 values file,對應 cluster 就會自動生成一個 Karpenter application。

https://ithelp.ithome.com.tw/upload/images/20250915/20119667QqTQ3LoxHO.png

ApplicationSet 範例

以下是我們在 ApplicationSet 裡的設定:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: karpenter
  ### KEY POINT: 這邊的 Value 會從 Helm Manager 的 root app 注入
  namespace: {{ .Values.applicationSetNs }}
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
    - git:
        repoURL: {{ .Values.repoURL }}
        revision: main
        ### KEY POINT: 讓 ArgoCD 根據什麼路徑來長出對應的 Application
        directories:
          - path: infra-charts/karpenter/*-internal
          - path: infra-charts/karpenter/in-cluster/*-internal
  template:
    metadata:
	    ### KEY POINT: ApplicationSet 的特殊寫法
      name: '{{"{{"}} .path.basename {{"}}"}}-karpenter'
    spec:
      project: {{ .Values.project }}
      sources:
        - repoURL: {{ .Values.repoURL }}
          path: "infra-charts/karpenter/chart"
          targetRevision: main
          helm:
            valueFiles:
              - /{{"{{"}} .path.path {{"}}"}}/values.yaml
      destination:
        name: '{{"{{"}} index .path.segments 2 {{"}}"}}'
        namespace: kube-system
  syncPolicy:
    preserveResourcesOnDeletion: true

這樣,我們只需要把對應 cluster 的 values.yaml 放到正確目錄,ApplicationSet 就會在對應 cluster 裡自動建立出 Karpenter Application。

🔑 注意:ApplicationSet 使用上有些特殊語法,基本 template 細節可以參考文件。另外我們採用的是 Git Generator,這個 generator 可用的參數也可以參考這篇文件

最後來看一下 ArgoCD 上的結果

https://ithelp.ithome.com.tw/upload/images/20250915/20119667rKDoJNn4t8.jpg

可以看到這邊 general NodePool 已經有長出兩個 NodeClaim 了,也就是 Karpenter 已經幫我們開好兩台 general 機型的節點、並且成功納管到 EKS 之下。

小結

今天我們讓 Karpenter 納入 Helm Manager 架構,實現了:

  • 跨多個 cluster 的一致性部署:同一張 chart 配合不同 values file,自動在各 cluster 生出對應的 Karpenter Application。
  • 彈性的節點供應:透過 NodeClass / NodePool / NodeClaim,把節點管理從「固定 Node Group」升級為「Pod 驅動的動態節點」。
  • 成本最佳化:利用 Consolidation 機制,自動關閉閒置節點,並把 Pod 搬到更合適或更便宜的 instance。
  • GitOps 自動化治理:基礎設施元件和應用一樣,都能用 ApplicationSet + Git 目錄生成的方式來驅動。

透過這些特性,我們的叢集從節點層開始具備了更高的彈性與效率。但節點只是基礎,要讓外部流量能夠順利進入叢集,還需要 流量入口 (Ingress)

因此在 Day 16,我們會來介紹 Ingress NGINX —— 最常見的 Kubernetes Ingress Controller,負責處理 HTTP/S 流量的 routing / reverse proxy / load balancing(別擔心這些明天都會講到),這也會回扣到我們 Day 6 聊到的 ingress & service 的概念,也能夠為我們後續了解 Load Balancer Controller 及 NLB/ALB 奠定穩固的基礎。


上一篇
[Day 14] Helm manager:跨叢集管理 Helm Charts
下一篇
[Day 16] 誰來當門神?用 Ingress NGINX 打開叢集的大門
系列文
EKS in Practice:IaC × GitOps 實戰 30 天17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言