AWS Thai Blog

จัดการค่าใช้จ่ายของ Kubernetes compute ของคุณให้เหมาะสมด้วย Karpenter consolidation

บทนำ

Karpenter ถูกสร้างมาเพื่อช่วยแก้ปัญหาเกี่ยวกับการเลือก node ที่เหมาะสมที่สุดใน Kubernetes รูปแบบของ Karpenter คือ คุณต้องการอะไร เมื่อไหร่ที่คุณต้องการ (what-you-need-when-you-need-it) ช่วยทำให้กระบรวนการจัดการทรัพยากร compute ใน Kubernetes ทำได้ง่ายขึ้น โดยการเพิ่ม Compute capacity ไปที่ Cluster ของคุณตามความต้องการของ Pod จากการออก release ล่าสุดของการรวบรวม  workload (workload consolidation) ตอนนี้ Karpenter สามารถเปิดใช้งานการมอนิเตอร์อย่างต่อเนื่องและการกำหนดการสร้าง Pod ได้อย่างเหมาะสม เพื่อที่จะใช้ทรัพยากรได้อย่างเหมาะสมและลดค่าใช้จ่ายของ compute

บทความนี้แปลมาจาก Optimizing your Kubernetes compute costs with Karpenter consolidation โดยคุณ Lukonde Mwila

ในบทความนี้จะบรรยายถึงความสามารถของ Karpenter consolidation และรายละเอียดของ และจัดการค่าใช้จ่ายของ Data plane ใน Kubernetes ให้เหมาะสมและตัวอย่างการใช้งาน

Karpenter’s workload consolidation

ในเวอร์ชันก่อนหน้านี้ Karpenter ทำการลบการติดตั้ง (de-provision) Worker node ที่ไม่มี Pod ที่ไม่ใช่ Daemonset เท่านั้น เมื่อเปิดใช้งานและ  Workload ถูก reschedule ไปเรื่อยๆ ทำให้บาง Worker node  อาจใช้งานได้ไม่เต็มประสิทธิภาพ ทำให้ Workload consolidation มีเป้าหมายที่จะปรับปรุง vision ของ Karpenter ให้มีประสิทธิภาพดีขึ้น และการทำ Auto scaling ให้มีค่าใช้จ่ายน้อยที่สุดโดยการรวม workload ให้มีจำนวนน้อยที่สุด ค่าใช้จ่ายต่อ instance น้อยที่สุด ในขณะที่ยังคงยึดตามทรัพยากรของ Pod และข้อจำกัดของการ schedule ต่างๆ Workload consolidation สามารถเปิดใช้งานโดยใช้ Provisioner ซึ่งเป็น Custom Resource Definition (CRD) ของ Karpenter หน้าที่ของ Provisioner คือการจัดการออกคำสั่งใน lifecycle ของ Karpenter ที่ควบคุม Node ใน Cluster ทำให้เราสามารถที่จะกำหนดข้อจำกัดของ capacity รวมถึง พฤติกรรม (เช่น expiration และ consolidation) ของ Node ที่ได้ทำการสร้าง

เมื่อเปิดใช้งานใน Provisioner Karpenter จะทำการมอนิเตอร์ workload ของ cluster ของคุณอย่างต่อเนื่อง เพื่อมองหาโอกาสที่จะรวบรวม compute capacity เพื่อที่จะ utilize node ให้ดีขึ้นรวมถึงค่าใช้จ่ายด้วย Karpenter ยังคงคำนึงถึงข้อจำกัดของการทำ scheduling อื่นๆ (any scheduling constraints) ที่คุณได้ระบุไว้ เช่น pod affinity rules หรือ topology spread constraints เป็นต้น) เนื่องจาก Karpenter ทำการสร้าง node จากความต้องการของ Workload ซึ่งมีความสำคัญที่จะต้องระบุสิ่งเหล่านี้ไว้ ในการทำสิ่งนี้ คุณควรระบุทั้งความต้องการใช้งานของ central processing unit (CPU) และ memory ของ pod ของคุณ ซึ่งจะช่วยป้องกันไม่ให้ทรัพยากรขาดแคลน (starvation) หรือใช้งานมากจนเกินไป (hogging) โดยเฉพาะอย่างยิ่งเมื่อมีการใช้งานหลาย ๆ Workload ใน Cluster นอกจากนี้มันคือข้อกำหนดที่สำคัญที่จะทำให้ฟีเจอร์ Workload consolidation ของ Karpenter ทำงานได้อย่างมีประสิทธิภาพ

สิ่งที่ต้องเตรียมก่อนเริ่มต้น (Prerequisites)

ในการที่จะทำตามบทความนี้ คุณจะต้องทำการติดตั้งสิ่งต่างๆเหล่านี้:

ในการที่จะติดตั้งได้อย่างอัตโนมัติ คุณสามารถใช้ Amazon EKS blueprints สำหรับ Terraform ซึ่งมีตัวอย่างในการติดตั้ง Karpenter เป็น add-on สำหรับ Cluster ของคุณ คุณไม่จำเป็นต้องปรับเปลี่ยนซอร์สโค้ด ของ Terraform ในการที่จะทำตามตัวอย่างของบทความนี้

Provisioners

Karpenter ควบคุม Node จาก Provisioner Custom Resource Definition (CRD) ซึ่ง Provisioner คือไฟล์ configuration ที่มีหน้าที่ในการตัดสินใจในเรื่องต่างๆไม่ว่าจะเป็นชนิดของ Computing capacity, ชนิดของ Instance, Configuration เพิ่มเติมของ Kubelet, พารามิเตอร์ของ Resource, และการระบุ lifecycle ต่างๆของ Node คุณสามารถติดตั้ง Provisioner CRDs หลายๆตัวที่ Cluster ของคุณขึ้นอยู่กับการใช้งานของแต่ล่ะ User ตราบเท่าที่ยังไม่เกิดการทับซ้อนกัน

Amazon EKS blueprints มีตัวอย่างของ Provisioners อยู่ในโฟลเดอร์ /karpenter/provisioners ในตัวอย่างเราจะใช้ 1 Provisioner สำหรับ Node.js แอปพลิเคชันชื่อว่า express-test โดย Pod ของแอปพลิเคชันจะไม่ถูก schedule สร้างขึ้นมานอกจาก Provisioner จะถูกติดตั้งแยกไปที่ cluster

ตัวอย่างของไฟล์ของ Provisioner:

Provisioner

apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: express-test
spec:
  # Enables consolidation which attempts to reduce cluster cost by both removing un-needed nodes and down-sizing those
  # that can't be removed.  Mutually exclusive with the ttlSecondsAfterEmpty parameter.
  consolidation:
    enabled: true
  requirements:
    - key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand
      operator: In
      values: ["spot", "on-demand"]
    - key: "karpenter.k8s.aws/instance-cpu"
      operator: In 
      values: ["c", "m", "r"]
  provider:
    instanceProfile: KarpenterNodeInstanceProfile-alpha
    subnetSelector:
      karpenter.sh/discovery: 'alpha'
    securityGroupSelector:
      karpenter.sh/discovery/alpha: 'alpha'
  labels:
    managedBy: carpenter

คุณสามารถบันทึกไฟล์นี้และติดตั้งลงใน Cluster ของคุณโดยใช้คำสั่งนี้

kubectl apply -f provisioner.yaml

อธิบายโดยละเอียด

ตัวอย่างการทำ Workload consolidation

ในหัวข้อนี้ เราจะติดตั้งแอปพลิเคชัน express-test ด้วยหลายๆ replicas ซึ่งในแต่ละ pod จะมี 1 CPU core  และมีข้อจำกัดให้กระจายไปแต่ล่ะ Zone (zonal topology spread constraint) ในการทำสิ่งเหล่านี้ workload manifest จะกำหนดเงื่อนไขในการเลือก node (node selector) สำหรับ Pod ที่จะทำการ schedule ตัว Compute ที่ถูกจัดการโดย Provisioner ที่เราสร้างขึ้นมาในขั้นตอนก่อนหน้านี้ หลังจากที่ตรวจดูการทำงานของ Karpenter provision ในการเริ่มต้นสร้างกลุ่มของ node เราจะทำการปรับเปลี่ยน Deployment โดยจะเปลี่ยนจำนวนของ replica และตรวจดูการทำงานของ Karpenter consolidation ที่จะตอบสนองการเปลี่ยนแปลงในครั้งนี้

manifest ของ แอปพลิเคชันถูกระบุตามตัวอย่างโค้ดด้านล่าง

Manifest ของแอปพลิเคชัน

apiVersion: apps/v1
kind: Deployment
metadata:
  name: express-test
spec:
  replicas: 20
  selector:
    matchLabels:
      app: express-test
  template:
    metadata:
      labels:
        app: express-test
    spec:
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/zone"
        whenUnsatisfiable: ScheduleAnyway
        labelSelector:
          matchLabels:
            app: express-test
      nodeSelector:
        karpenter.sh/provisioner-name: express-test
      containers:
        - name: express-test
          image: lukondefmwila/express-test:1.1.4
          resources:
            requests:
              cpu: "1"
              memory: "64Mi"

คุณสามารถบันทึกไฟล์นี้และติดตั้ง Deployment ลงใน Cluster ของคุณโดยใช้คำสั่งนี้

kubectl apply -f deployment.yaml

หลังจากนั้น คุณสามารถดูการใช้งาน Compute และค่าใช้จ่ายของ Karpenter node โดยใช้ Amazon EKS Node Viewer

Karpenter ได้ทำการเพิ่ม 3 nodes (2 x t3.2xlarge instances และ 1 x c6a.2xlarge) ไปที่ cluster ของเรา เพื่อเติมเต็มตามความต้องการที่ระบุใน Manifest ซึ่งจะบริหารจัดการทั้งความต้องการของ Compute และทำตาม Schedule constrains Spot instance เหล่านี้มี 8 CPU cores และแต่ละ Node ถูกสร้างโดยกระจายไปแต่ละ availability zone (AZ) ทั้ง eu-west-1a, eu-west-1b, และ eu-west-1c Karpenter สร้าง Node ที่ต้องการ 1 CPU core สำหรับ 20 replicas (กระจายระหว่าง AZs) ควบคู่ไปกับการใช้งาน CPU ของ Daemonset และทรัพยากรที่ถูกจองโดย Kubelet

ตามที่เราเห็นในรูปภาพด้านบน เครื่องมือ CLI ของ eks-node-viewer แสดงค่าใช้จ่ายต่อเดือนสำหรับการติดตั้ง Node ในแบบปัจจุบัน

ในขั้นตอนต่อไปจะเป็นการเปลี่ยน Manifest ของ Deployment เริ่มต้น ตามที่แสดงด้านล่าง เราลดจำนวน Replica ของ Pod ลงจาก 20 เป็น 10 ซึ่งทำให้การขอใช้ทรัพยากรลงน้อยลงตามไปด้วย  

Manifest ของแอปพลิเคชัน

apiVersion: apps/v1
kind: Deployment
metadata:
  name: express-test
spec:
  replicas: 10
  ...

คุณสามารถอัพเดตการเปลี่ยนแปลงนี้ลงใน Cluster ของคุณโดยใช้คำสั่ง kubectl apply -f deployment.yaml อีกครั้ง.

ในขณะนี้ Node ต่าง ๆ ของเราใช้งานได้ไม่เต็มประสิทธิภาพ (underutilized) เพราะก่อนหน้านี้ Karpenter ทำการเพิ่ม 3 nodes โดยการประมาณ 8 CPUs ในแต่ละ Node  เพื่อให้เหลือพร้อมใช้งานสำหรับ 20 replicas ในตอนนี้วิธีการเดียวที่จะลดค่าใช้จ่ายของ Cluster คือการลด Node ที่ใช้งานไม่เต็มประสิทธิภาพออก ในขั้นแรก Pod จะถูกให้ทำการ isolate และ drain ถ้าคุณทำการมอนิเตอร์ Node ต่อไปเรื่อย ๆ คุณจะเห็นว่ามีเหตุการณ์เหล่านี้เกิดขึ้นและในท้ายที่สุด Pod ที่ถูก schedule ไปสร้างที่ 2 Spot instances ที่เหลืออยู่ ซึ่งผลลัพทธ์ที่ได้ในการเปลี่ยนแปลงเหล่านี้จะเป็นไปตามรูปภาพที่ปรากฎทางด้านล่าง

ตามที่ผลลัพธ์แสดงถึงการเปิดใช้งาน Consolidation ทำให้ Karpenter สามารถปรับการใช้งาน Compute ของ Cluster ให้เหมาะสม โดยการลดจำนวน Instance ลง 1 จาก 3 Instance ในขณะที่ยังคงสามารถรองรับความต้องการของทรัพยากรได้และทำตามข้อจำกัดในการกระจายสำหรับรองรับ Workload ของแอปพลิเคชันของเราได้ ค่าใช้จ่ายของ Data plane สำหรับ Workload นี้ลดลงตามที่แสดงในรูปภาพด้านบน.

สำหรับใน Production environment นั้น เราแนะนำให้ใช้เครื่องมือ เช่น kubecost ควบคู่ไปกับ Karpenter เพื่อมอนิเตอร์และจัดการค่าใช้จ่ายของ Kubernetes คุณสามารถทำตามวิธีการนี้ในการติดตั้ง kubecost สำหรับการมอนิเตอร์ค่าใช้จ่ายของ Amazon EKS

Cleanup

เพื่อหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติม กรุณาอย่าลืมลบ Infrastructure ที่คุณสร้างในตัวอย่างนี้ ขั้นตอนแรก คุณต้องลบ Node ที่ถูกสร้างโดย Karpenter ซึ่ง Node เหล่านี้ถูกจัดการโดย Provisioner CRD ซึ่งคุณสามารถลบทรัพยากรนี้จาก Kubernetes cluster ของคุณ หลังจากนั้น คุณสามารถลบ infrastructure ที่เหลือโดยใช้ Terraform กรุณาตรวจสอบอีกครั้งว่าคุณอยู่ในโฟลเดอร์ที่ถูกต้องก่อนที่จะใช้คำสั่ง terraform destroy คุณสามารถทำตามวิธีการ clean up สำหรับ Amazon EKS blueprints

บทสรุป

ในบทความนี้ เราได้แสดงให้คุณเห็นถึงวิธีการในการรวมฟีเจอร์ใหม่ของ Karpenter คือ Workload consolidation และทำตาม practices ที่ดีในการใช้งานทรัพยากรตามความต้องการที่จะช่วยให้คุณลดค่าใช้จ่ายของ Data plane ใน Kubernetes ของคุณ

นี้คือตัวอย่างเพิ่มเติมที่เกี่ยวกับหัวข้อนี้:

Karpenter สำหรับ Kubernetes | Karpenter vs Cluster Autoscaler

Karpenter consolidation ใน Kubernetes

เรียนรู้เพิ่มเติมเกี่ยวกับ Karpenter คุณสามารถอ่านบทความและเข้าร่วมกลุ่ม community ของ Kubernetes slack channel ได้ที่ #karpenter.