Skip to main content

Command Palette

Search for a command to run...

Syllabus học Karpenter

Updated
13 min readView as Markdown

Tuần 0: Nền tảng

  • Ngày 0.1: Kubernetes scheduler hoạt động thế nào (predicates/filters, scoring), resource requests/limits, kubectl get events, so sánh Cluster Autoscaler (CAS) vs Karpenter về mặt kiến trúc (CAS: node group trước, pod sau; Karpenter: pod trước, chọn node sau - đây là khác biệt gốc của mọi hành vi sau này).

  • Ngày 0.2: Kiến trúc AWS cần biết: VPC/subnet, Security Group, IAM Role vs Instance Profile, EC2 Spot vs On-Demand, Spot interruption notice (2 phút cảnh báo qua IMDS/EventBridge). Đây là kiến thức Karpenter "giả định bạn đã biết" - thiếu nó, phần Tuần 3 sẽ chỉ là copy-paste YAML không hiểu gì.

Câu hỏi kiểm chứng: Giải thích được vì sao Karpenter "group-less" (không cần node group cố định) lại linh hoạt hơn CAS, bằng ví dụ cụ thể.

Tuần 1: Kiến trúc Karpenter và cài đặt

Ngày 1

  • Lý thuyết: Kiến trúc controller Karpenter (control loop, watch trên unscheduled pods), quan hệ NodePoolNodeClaimEC2NodeClass ↔ EC2 Instance. NodeClaim là gì và tại sao nó là "nguồn sự" trung gian.

  • Thực hành:

    1. Cài EKS cluster (hoặc kind/Minikube để thực hành phần logic scheduling, nhưng lưu ý: không thể test EC2NodeClass/IAM trên kind - cần EKS cho từ Ngày 2 trở đi).

    2. Cài Karpenter qua Helm chart chính thức (oci://public.ecr.aws/karpenter/karpenter), dùng Pod Identity hoặc IRSA (không dùng access key tĩnh).

    3. Tag subnet và security group với karpenter.sh/discovery=<cluster-name> - đây là bước 90% người mới bỏ sót và bị lỗi "selector mismatch, không launch được node gì cả".

    4. Apply một EC2NodeClass + NodePool default tối giản, deploy pod pause không fit node hiện có, quan sát node mới xuất hiện bằng kubectl get nodeclaims -o wide (không chỉ kubectl get nodes).

Ngày 2

  • Lý thuyết: Cấu trúc chi tiết NodePool.spec: requirements (well-known labels: karpenter.sh/capacity-type, karpenter.k8s.aws/instance-category, node.kubernetes.io/instance-type...), limits (giới hạn tổng CPU/memory của cả pool), weight (khi có nhiều NodePool cạnh tranh).

  • Thực hành:

    1. Tạo 2 NodePool có weight khác nhau, deploy workload không chỉ định gì đặc biệt, quan sát Karpenter chọn pool nào trước.

    2. Dùng karpenter-convert CLI thử convert một Provisioner YAML mẫu (tìm trên mạng bản cũ) sang NodePool - bài tập này giúp bạn thực sự hiểu sự khác biệt giữa hai API, không chỉ đọc lý thuyết.

    3. kubectl describe nodeclaim <name> và đọc kỹ phần status.conditions - đây là nơi 80% lỗi debug sau này sẽ hiện ra.

Ngày 3

  • Lý thuyết: EC2NodeClass chi tiết: amiFamily/amiSelectorTerms (và tại sao pin AMI bằng alias family@version là bắt buộc cho production, không dùng latest), subnetSelectorTerms, securityGroupSelectorTerms, blockDeviceMappings, userData, role (Karpenter tự tạo instance profile - không cần quản lý tay như bản v1alpha cũ).

  • Thực hành:

    1. Tạo EC2NodeClass với AMI pin cụ thể (không dùng latest), tự set blockDeviceMappings để tăng dung lượng gp3 root volume.

    2. Viết userData tuỳ chỉnh (bootstrap script) và verify nó chạy đúng bằng SSM Session Manager vào node mới.

    3. Thử đổi amiSelectorTerms sang phiên bản AMI khác → quan sát Karpenter đánh dấu node cũ là drifted (kubectl get nodeclaims cột DRIFTED). Đây là khái niệm quan trọng bản cũ hoàn toàn không đề cập.

Câu hỏi kiểm chứng tuần 1: Vẽ sơ đồ luồng từ "pod pending" → "EC2 instance chạy" đi qua đúng 4 object (Pod → NodePool → NodeClaim → EC2NodeClass) mà không nhìn tài liệu.

Tuần 2: Scheduling Constraints

Ngày 4

  • Lý thuyết: Toàn bộ well-known requirement keys của Karpenter (karpenter.k8s.aws/instance-category, instance-generation, instance-memory, instance-cpu, instance-hypervisor...), operator In/NotIn/Gt/Lt/Exists.

  • Thực hành:

    1. Tạo NodePool chỉ cho phép Graviton (arm64) + loại bỏ các instance quá lớn bằng NotIn (ví dụ loại m6g.16xlarge).

    2. Deploy cùng một Deployment với nodeSelectornodeAffinity khác nhau, quan sát NodeClaim nào được tạo tương ứng.

    3. Thử pod có resources.requests vượt quá mọi instance type cho phép trong NodePool → quan sát pod pending mãi + đọc event để hiểu lỗi.

Ngày 5

  • Lý thuyết: Taints/Tolerations áp cho node do NodePool tạo ra (spec.template.spec.taints), Topology Spread Constraints, Pod (anti-)affinity - và cách các thứ này ảnh hưởng trực tiếp đến loại instance mà Karpenter sẽ launch (Karpenter mô phỏng bin-packing trước khi gọi API AWS).

  • Thực hành:

    1. Tạo NodePool riêng cho GPU (taint nvidia.com/gpu), Deployment tương ứng có toleration - verify pod GPU không bao giờ lạc sang node thường và ngược lại.

    2. Dùng topologySpreadConstraints để bắt buộc pod trải đều qua nhiều AZ, quan sát Karpenter launch node ở đúng AZ cần.

    3. Test pod anti-affinity "không đứng chung node với chính nó" ở scale 20 replicas, đo Karpenter launch bao nhiêu node.

Ngày 6

  • Lý thuyết: Multi-tenant NodePool pattern trong production: NodePool riêng cho critical/system workload (on-demand, instance family cố định) vs NodePool cho batch/flexible (Spot-first, đa dạng instance type để tăng khả năng có Spot).

  • Thực hành:

    1. Thiết kế và triển khai đúng 3 NodePool: system (on-demand, nhỏ, ổn định), general (mix on-demand+spot), spot-batch (spot only, instance family đa dạng để giảm rủi ro thiếu Spot).

    2. Gán từng team/namespace bằng nodeAffinity/label + ResourceQuota namespace tương ứng.

    3. Viết một "runbook" 1 trang: khi nào một pod sẽ rơi vào NodePool nào - tài liệu này chính là thứ bạn sẽ cần khi debug lúc 2 giờ sáng.

Câu hỏi kiểm chứng tuần 2: Cho một pod spec bất kỳ, dự đoán đúng NodePool + instance type mà Karpenter sẽ chọn, trước khi apply, rồi verify lại.

Tuần 3: Disruption, Consolidation, Drift, Spot Interruption

Ngày 7

  • Lý thuyết: disruption.consolidationPolicy (WhenEmpty vs WhenEmptyOrUnderutilized), consolidateAfter, cơ chế Karpenter mô phỏng "nếu gom pod lại thì có tiết kiệm được node không" trước khi action.

  • Thực hành:

    1. Set consolidationPolicy: WhenEmptyOrUnderutilized, consolidateAfter: 1m, scale workload lên rồi xuống, đo thời gian Karpenter consolidate node thừa.

    2. Dùng annotation karpenter.sh/do-not-disrupt: "true" trên một pod quan trọng, verify Karpenter không bao giờ terminate node chứa pod đó dù rất underutilized.

    3. Cấu hình PodDisruptionBudget và quan sát Karpenter tôn trọng nó khi consolidate.

Ngày 8

  • Lý thuyết: expireAfter (node lifetime tối đa, để tránh instance "già" tích lũy config drift) vs Drift detection (khi NodePool/EC2NodeClass đổi mà node cũ không còn khớp spec - ví dụ đổi AMI, đổi requirement).

  • Thực hành:

    1. Set expireAfter: 168h, quan sát (giả lập bằng sửa nhanh giá trị thấp trong staging) node bị rotate tự động.

    2. Sửa NodePool requirements sau khi đã có node chạy → verify kubectl get nodeclaims báo Drifted=True, và Karpenter tự thay node mới tuân theo spec mới, có kiểm soát (không kill hết cùng lúc).

    3. Cấu hình disruption.budgets để giới hạn Karpenter chỉ được disrupt tối đa X% node cùng lúc - mô phỏng production (không để cả cluster rung lắc cùng lúc).

Ngày 9

  • Lý thuyết: Spot Instance interruption thực tế: EC2 gửi 2 phút cảnh báo, Karpenter có Interruption Handling (qua SQS queue + EventBridge rule) để cordon/drain trước khi mất node, không phải để pod rơi tự do.

  • Thực hành:

    1. Cấu hình interruptionQueue cho Karpenter (SQS + EventBridge rule cho Spot interruption, rebalance recommendation, instance state-change).

    2. Giả lập rebalance recommendation (AWS Fault Injection Simulator hoặc công cụ mô phỏng interruption) và quan sát Karpenter proactively di dời pod trước khi instance thực sự bị thu hồi.

    3. So sánh log/behaviour giữa node bị Spot reclaim có interruption handling và không có - đo downtime thực tế của ứng dụng.

Câu hỏi kiểm chứng tuần 3: Giải thích chính xác sự khác nhau giữa expireAfter, drift, và consolidation - ba cơ chế khác nhau khiến node bị thay, nhiều người học xong vẫn nhầm lẫn ba cái này thành một.

Tuần 4: Chi phí, Tài nguyên, Ứng dụng trên Karpenter

Ngày 10

  • Lý thuyết: Chiến lược tối ưu chi phí: Spot diversification (nhiều instance family/size để giảm rủi ro thiếu capacity), so sánh chi phí NodePool on-demand vs spot bằng số liệu (không chỉ lý thuyết).

  • Thực hành:

    1. Deploy cùng một workload trên NodePool on-demand và NodePool spot riêng biệt, đo chi phí bằng AWS Cost Explorer filter theo tag Karpenter gắn tự động.

    2. Dùng công cụ như Kubecost hoặc OpenCost để attribute chi phí Karpenter node theo namespace/team.

    3. Tính toán "bin-packing efficiency": tỉ lệ resource request/allocatable trung bình của cluster trước và sau khi áp dụng consolidation.

Ngày 11

  • Lý thuyết: Deploy ứng dụng (không chỉ nginx demo): Deployment + Service + Ingress + HPA, và tương tác giữa HPA (scale pod) và Karpenter (scale node) - đây là combo bắt buộc trong production, HPA không tự tạo node được.

  • Thực hành:

    1. Deploy một microservice có HPA dựa trên CPU/custom metric (qua metrics-server hoặc KEDA), verify chuỗi phản ứng: traffic tăng → HPA tăng pod → pod pending → Karpenter launch node.

    2. Thử workload GPU (ví dụ inference job) trên NodePool GPU riêng đã làm ở Tuần 2, đo thời gian từ "job submit" đến "GPU node ready" (thường 60-90s, khác biệt lớn với node group tĩnh).

    3. Viết Kubernetes manifest set đúng requests/limits hợp lý - vì set sai trực tiếp làm Karpenter chọn instance type sai (quá to hoặc quá bé), đây là lỗi thực tế phổ biến nhất khi vận hành.

Câu hỏi kiểm chứng tuần 4: Cho một hoá đơn AWS Cost Explorer giả định, chỉ ra được node nào do NodePool nào tạo ra và giải thích vì sao chi phí như vậy.

Tuần 5: Observability và Debug

Ngày 12

  • Lý thuyết: Metrics Karpenter export (karpenter_nodeclaims_*, karpenter_pods_unschedulable_*, karpenter_cloudprovider_*), cấu trúc log controller (structured JSON logs, mức debug khi cần).

  • Thực hành:

    1. Cài Prometheus + Grafana, import dashboard Karpenter chính thức, verify các panel: node provisioning latency, disruption events, unschedulable pods theo thời gian.

    2. Đặt alert Prometheus: pod unschedulable quá X phút, NodeClaim ở trạng thái Launching quá lâu (dấu hiệu lỗi IAM/subnet).

    3. Cố tình gây lỗi (xoá tag karpenter.sh/discovery khỏi 1 subnet, sai role trong EC2NodeClass) rồi tự debug bằng kubectl describe nodeclaim, kubectl logs -n kube-system deploy/karpenter, không tra mạng trước - đây là kỹ năng khi on-call.

Ngày 13

  • Lý thuyết: Các lỗi thường gặp nhất trong production Karpenter: subnet/SG tag sai, IAM thiếu quyền (ec2:RunInstances, iam:PassRole...), CAS và Karpenter chạy song song gây xung đột, quota EC2 vùng bị chặn, NodeClaim kẹt ở Registering do kubelet không join được cluster.

  • Thực hành:

    1. Tạo checklist troubleshooting cá nhân dựa trên các lỗi tự gây ở Ngày 12, mở rộng thêm 3-5 lỗi khác (thiếu quota vCPU theo instance family, security group chặn outbound tới EKS API).

    2. Nếu cluster đang chạy CAS, thực hành quy trình migrate an toàn: scale CAS deployment về 0, dùng taint tạm để phân vùng trách nhiệm trước khi gỡ hẳn CAS.

Câu hỏi kiểm chứng tuần 5: Nhìn vào kubectl describe nodeclaim với một lỗi giả lập ngẫu nhiên (do người khác gây ra), chẩn đoán đúng nguyên nhân trong dưới 5 phút.

Tuần 6: Bảo mật

Ngày 14

  • Lý thuyết: IAM least-privilege cho Karpenter controller (không dùng policy mẫu AdministratorAccess), phân biệt quyền controller (control plane) vs quyền node role (data plane, cần ECR/SSM/CNI).

  • Thực hành:

    1. Viết IAM policy tối thiểu cho Karpenter controller dựa trên action cần (ec2:RunInstances, ec2:CreateTags, ec2:TerminateInstances, iam:PassRole giới hạn theo aws:RequestTag/karpenter.sh/nodepool, pricing:GetProducts...), test bằng cách thu hẹp dần và xem cái gì gãy.

    2. Cấu hình RBAC cho user/team chỉ được đọc NodePool/EC2NodeClass ở namespace mình quản lý, không sửa được toàn cục.

    3. Chạy kube-bench hoặc Trivy scan cluster, xử lý ít nhất 3 finding (không chỉ đọc report).

Ngày 15

  • Lý thuyết: Network policy giữa Karpenter controller và API server/EC2 endpoint, Pod Security Standards cho node system pods, và rủi ro nếu EC2NodeClass userData lộ secret.

  • Thực hành:

    1. Áp Pod Security Standards restricted cho namespace ứng dụng, verify Karpenter (chạy ở kube-system) không bị ảnh hưởng sai cách.

    2. Review lại userData đã viết ở Tuần 1 - đảm bảo không hard-code secret, chuyển sang lấy secret qua SSM Parameter Store/Secrets Manager tại boot time.

Câu hỏi kiểm chứng tuần 6: Trình bày được IAM policy của bạn cho một người audit bảo mật và giải thích được từng permission dùng để làm gì - không permission nào là "cho chắc".

Tuần 7: Vận hành - Upgrade, HA, DR, CI/CD

Ngày 16

  • Lý thuyết: Quy trình upgrade Karpenter an toàn (đọc changelog, kiểm tra breaking change giữa minor version, conversion webhook khi còn v1beta1 resource sót lại).

  • Thực hành:

    1. Dựng lại đúng quy trình nâng cấp: apply CRD mới → upgrade Helm release → verify NodePool/EC2NodeClass tự convert đúng qua webhook → không có node bị roll không cần thiết.

    2. Test rollback: giả lập upgrade lỗi, quay lại version cũ, kiểm tra cluster vẫn ổn định.

Ngày 17

  • Lý thuyết: HA cho Karpenter controller (multi-replica + leader election), multi-AZ NodePool, và giới hạn: Karpenter controller downtime không làm mất node đang chạy, chỉ tạm ngưng provisioning/disruption mới.

  • Thực hành:

    1. Scale Karpenter controller lên 2+ replica, giả lập crash 1 pod, verify leader election chuyển tiếp không mất event.

    2. Backup toàn bộ NodePool/EC2NodeClass CRD bằng kubectl get ... -o yaml vào GitOps repo (đây nên là nguồn, không phải backup thủ công).

Ngày 18

  • Lý thuyết: Chaos engineering có mục tiêu rõ: không phải "gây lỗi cho vui" mà đo Recovery Time Objective (RTO).

  • Thực hành:

    1. Dùng Chaos Mesh/LitmusChaos: kill node do Karpenter tạo, đo thời gian pod reschedule + node mới ready.

    2. Giả lập mất hết node trong 1 AZ, verify multi-AZ NodePool tự bù sang AZ khác, đo downtime ứng dụng (không chỉ "quan sát" mơ hồ như bản cũ).

Ngày 19

  • Lý thuyết: CI/CD tích hợp Karpenter đúng cách: NodePool/EC2NodeClass nên nằm trong GitOps (ArgoCD/Flux), không apply tay; pipeline app deploy không cần biết Karpenter tồn tại (đó là điểm mạnh của model "pod-driven").

  • Thực hành:

    1. Đưa toàn bộ NodePool/EC2NodeClass vào Git repo, dùng ArgoCD/Flux sync tự động, test thay đổi qua PR → merge → cluster tự cập nhật.

    2. Viết pipeline CI đơn giản deploy app mới, không đụng gì tới Karpenter config, verify scaling vẫn hoạt động - chứng minh sự tách biệt trách nhiệm đúng chuẩn production.

Ngày 20 - Capstone

  • Yêu cầu: Trong 1 ngày, tự dựng từ đầu (không nhìn lại bài cũ) một cluster có:

    1. 3 NodePool phân tầng (system/general/spot-batch) đúng best practice.

    2. Disruption budget + do-not-disrupt cho ít nhất 1 workload stateful giả định.

    3. Interruption handling cho Spot hoạt động.

    4. Dashboard Grafana + alert cơ bản.

    5. IAM policy tự viết, không copy nguyên mẫu.

    6. Toàn bộ config nằm trong Git, sync qua GitOps.

    7. Một trang runbook debug cho 3 lỗi phổ biến nhất bạn tự liệt kê.

Tài liệu tham khảoh

Knowledge

Part 1 of 50