ProHoster > blog > administrasi > Kubernetes: mengapa begitu penting untuk mengonfigurasi manajemen sumber daya sistem?
Kubernetes: mengapa begitu penting untuk mengonfigurasi manajemen sumber daya sistem?
Sebagai aturan, selalu ada kebutuhan untuk menyediakan kumpulan sumber daya khusus untuk aplikasi agar dapat berfungsi dengan benar dan stabil. Namun bagaimana jika beberapa aplikasi berjalan dengan daya yang sama? Bagaimana cara menyediakan sumber daya minimum yang diperlukan masing-masing dari mereka? Bagaimana cara membatasi konsumsi sumber daya? Bagaimana cara mendistribusikan beban antar node dengan benar? Bagaimana cara memastikan mekanisme penskalaan horizontal berfungsi jika beban aplikasi meningkat?
Anda harus mulai dengan jenis sumber daya utama apa yang ada dalam sistem - tentu saja, waktu prosesor dan RAM. Dalam manifes k8s, jenis sumber daya ini diukur dalam unit berikut:
CPU - dalam inti
RAM - dalam byte
Selain itu, untuk setiap sumber daya dimungkinkan untuk menetapkan dua jenis persyaratan - permintaan и batas. Permintaan - menjelaskan persyaratan minimum untuk sumber daya gratis dari sebuah node untuk menjalankan sebuah container (dan pod secara keseluruhan), sementara batas menetapkan batas tegas pada sumber daya yang tersedia untuk container.
Penting untuk dipahami bahwa manifes tidak harus secara eksplisit mendefinisikan kedua jenis tersebut, namun perilakunya adalah sebagai berikut:
Jika hanya batas sumber daya yang ditentukan secara eksplisit, maka permintaan untuk sumber daya ini secara otomatis mengambil nilai yang sama dengan batas (Anda dapat memverifikasi ini dengan memanggil entitas deskripsikan). Itu. faktanya, container akan dibatasi pada jumlah sumber daya yang sama yang dibutuhkan untuk menjalankannya.
Jika hanya permintaan yang ditentukan secara eksplisit untuk suatu sumber daya, maka tidak ada batasan atas yang ditetapkan pada sumber daya ini - mis. wadah hanya dibatasi oleh sumber daya dari node itu sendiri.
Dimungkinkan juga untuk mengonfigurasi manajemen sumber daya tidak hanya pada tingkat kontainer tertentu, tetapi juga pada tingkat namespace menggunakan entitas berikut:
Batas Jangkauan — menjelaskan kebijakan pembatasan pada level container/pod dalam ns dan diperlukan untuk menjelaskan batas default pada container/pod, serta mencegah pembuatan container/pod yang jelas-jelas gemuk (atau sebaliknya), membatasi jumlahnya dan menentukan kemungkinan perbedaan nilai batasan dan permintaan
ResourceQuota — menjelaskan kebijakan pembatasan secara umum untuk semua container di ns dan digunakan, sebagai aturan, untuk membatasi sumber daya antar lingkungan (berguna ketika lingkungan tidak dibatasi secara ketat pada tingkat node)
Berikut ini adalah contoh manifes yang menetapkan batas sumber daya:
Itu. dalam hal ini, untuk menjalankan container dengan nginx, Anda memerlukan setidaknya 1G RAM kosong dan 0.2 CPU pada node, sedangkan maksimal container dapat menggunakan 0.2 CPU dan semua RAM yang tersedia di node.
Itu. jumlah semua kontainer permintaan di ns default tidak boleh melebihi 300m untuk CPU dan 1G untuk OP, dan jumlah semua batas adalah 700m untuk CPU dan 2G untuk OP.
Itu. di namespace default untuk semua container, permintaan akan disetel ke 100m untuk CPU dan 1G untuk OP, batas - 1 CPU dan 2G. Pada saat yang sama, batas juga ditetapkan pada nilai yang mungkin dalam permintaan/batas untuk CPU (50m < x < 2) dan RAM (500M < x < 4G).
Itu. untuk setiap pod di ns default akan ada batasan 4 vCPU dan 1G.
Sekarang saya ingin memberi tahu Anda keuntungan apa yang dapat diberikan oleh pengaturan pembatasan ini kepada kita.
Mekanisme penyeimbangan beban antar node
Seperti yang Anda ketahui, komponen k8s bertanggung jawab atas distribusi pod antar node, seperti scheduler, yang bekerja berdasarkan algoritma tertentu. Algoritme ini melewati dua tahap saat memilih node optimal untuk diluncurkan:
penyaringan
mulai
Itu. sesuai dengan kebijakan yang dijelaskan, node pada awalnya dipilih yang memungkinkan untuk meluncurkan pod berdasarkan kumpulan predikat (termasuk memeriksa apakah node memiliki sumber daya yang cukup untuk menjalankan pod - PodFitsResources), dan kemudian untuk masing-masing node tersebut, menurut prioritas poin diberikan (termasuk, semakin banyak sumber daya gratis yang dimiliki sebuah node, semakin banyak poin yang diberikan - LeastResourceAllocation/LeastRequestedPriority/BalancedResourceAllocation) dan pod diluncurkan pada node dengan poin terbanyak (jika beberapa node memenuhi kondisi ini sekaligus, maka dipilih secara acak).
Pada saat yang sama, Anda perlu memahami bahwa penjadwal, ketika menilai sumber daya yang tersedia dari sebuah node, dipandu oleh data yang disimpan di etcd - mis. untuk jumlah sumber daya yang diminta/dibatasi dari setiap pod yang berjalan pada node ini, namun tidak untuk konsumsi sumber daya sebenarnya. Informasi ini dapat diperoleh dari output perintah kubectl describe node $NODE, misalnya:
Di sini kita melihat semua pod berjalan pada node tertentu, serta sumber daya yang diminta oleh setiap pod. Dan inilah tampilan log penjadwal saat pod cronjob-cron-events-1573793820-xt6q9 diluncurkan (informasi ini akan muncul di log penjadwal saat Anda menyetel level logging ke-10 di argumen perintah startup -v=10):
catatan
I1115 07:57:21.637791 1 scheduling_queue.go:908] About to try and schedule pod nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.637804 1 scheduler.go:453] Attempting to schedule pod: nxs-stage/cronjob-cron-events-1573793820-xt6q9
I1115 07:57:21.638285 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s5 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638300 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s6 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s3 is allowed, Node is running only 20 out of 110 Pods.
I1115 07:57:21.638322 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s4 is allowed, Node is running only 17 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638365 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s12 is allowed, Node is running only 9 out of 110 Pods.
I1115 07:57:21.638334 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s11 is allowed, Node is running only 11 out of 110 Pods.
I1115 07:57:21.638385 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s1 is allowed, Node is running only 19 out of 110 Pods.
I1115 07:57:21.638402 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s2 is allowed, Node is running only 21 out of 110 Pods.
I1115 07:57:21.638383 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, Node is running only 16 out of 110 Pods.
I1115 07:57:21.638335 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, Node is running only 18 out of 110 Pods.
I1115 07:57:21.638408 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s13 is allowed, Node is running only 8 out of 110 Pods.
I1115 07:57:21.638478 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s10 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638505 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s8 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638577 1 predicates.go:1369] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s9 is allowed, existing pods anti-affinity terms satisfied.
I1115 07:57:21.638583 1 predicates.go:829] Schedule Pod nxs-stage/cronjob-cron-events-1573793820-xt6q9 on Node nxs-k8s-s7 is allowed, Node is running only 25 out of 110 Pods.
I1115 07:57:21.638932 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 9
I1115 07:57:21.638946 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 2343 millicores 9640186880 memory bytes, score 8
I1115 07:57:21.638961 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: BalancedResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 9
I1115 07:57:21.638971 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: BalancedResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.638975 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: LeastResourceAllocation, capacity 39900 millicores 66620170240 memory bytes, total request 4107 millicores 11307422720 memory bytes, score 8
I1115 07:57:21.638990 1 resource_allocation.go:78] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: LeastResourceAllocation, capacity 39900 millicores 66620178432 memory bytes, total request 5847 millicores 24333637120 memory bytes, score 7
I1115 07:57:21.639022 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639030 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639034 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: TaintTolerationPriority, Score: (10)
I1115 07:57:21.639041 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639053 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639059 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: NodeAffinityPriority, Score: (0)
I1115 07:57:21.639061 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639063 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639073 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639077 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639085 1 interpod_affinity.go:237] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: InterPodAffinityPriority, Score: (0)
I1115 07:57:21.639088 1 selector_spreading.go:146] cronjob-cron-events-1573793820-xt6q9 -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639103 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s10: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639109 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s8: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639114 1 generic_scheduler.go:726] cronjob-cron-events-1573793820-xt6q9_nxs-stage -> nxs-k8s-s9: SelectorSpreadPriority, Score: (10)
I1115 07:57:21.639127 1 generic_scheduler.go:781] Host nxs-k8s-s10 => Score 100037
I1115 07:57:21.639150 1 generic_scheduler.go:781] Host nxs-k8s-s8 => Score 100034
I1115 07:57:21.639154 1 generic_scheduler.go:781] Host nxs-k8s-s9 => Score 100037
I1115 07:57:21.639267 1 scheduler_binder.go:269] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10"
I1115 07:57:21.639286 1 scheduler_binder.go:279] AssumePodVolumes for pod "nxs-stage/cronjob-cron-events-1573793820-xt6q9", node "nxs-k8s-s10": all PVCs bound and nothing to do
I1115 07:57:21.639333 1 factory.go:733] Attempting to bind cronjob-cron-events-1573793820-xt6q9 to nxs-k8s-s10
Di sini kita melihat bahwa pada awalnya penjadwal memfilter dan menghasilkan daftar 3 node yang dapat diluncurkan (nxs-k8s-s8, nxs-k8s-s9, nxs-k8s-s10). Kemudian menghitung skor berdasarkan beberapa parameter (termasuk BalancedResourceAllocation, LeastResourceAllocation) untuk masing-masing node tersebut guna menentukan node yang paling sesuai. Pada akhirnya, pod dijadwalkan pada node dengan jumlah poin terbanyak (di sini dua node sekaligus memiliki jumlah poin yang sama 100037, jadi dipilih secara acak - nxs-k8s-s10).
Keluaran: jika sebuah node menjalankan pod yang tidak ada batasannya, maka untuk k8 (dari sudut pandang konsumsi sumber daya) hal ini akan sama dengan seolah-olah tidak ada pod seperti itu pada node ini sama sekali. Oleh karena itu, jika Anda, secara kondisional, memiliki pod dengan proses yang rakus (misalnya, wowza) dan tidak ada batasan yang ditetapkan untuknya, maka situasi mungkin muncul ketika pod ini sebenarnya memakan semua sumber daya dari node tersebut, tetapi untuk k8s node ini dianggap tidak dimuat dan akan diberikan jumlah poin yang sama saat memberi peringkat (tepatnya dalam poin menilai sumber daya yang tersedia) sebagai node yang tidak memiliki pod yang berfungsi, yang pada akhirnya dapat menyebabkan distribusi beban antar node tidak merata.
Penggusuran Pod
Seperti yang Anda ketahui, setiap pod diberi salah satu dari 3 kelas QoS:
terjamin — ditetapkan ketika untuk setiap kontainer di pod, permintaan dan batas ditentukan untuk memori dan cpu, dan nilai-nilai ini harus cocok
mudah meledak — setidaknya satu container di pod memiliki permintaan dan batasan, dengan request < limit
upaya terbaik — ketika tidak ada satu pun container di dalam pod yang sumber dayanya terbatas
Pada saat yang sama, ketika sebuah node mengalami kekurangan sumber daya (disk, memori), kubelet mulai memberi peringkat dan mengeluarkan pod berdasarkan algoritma tertentu yang memperhitungkan prioritas pod dan kelas QoS-nya. Misalnya, jika kita berbicara tentang RAM, maka berdasarkan kelas QoS, poin diberikan sesuai dengan prinsip berikut:
Itu. dengan prioritas yang sama, kubelet pertama-tama akan mengeluarkan pod dengan kelas QoS upaya terbaik dari node.
Keluaran: jika Anda ingin mengurangi kemungkinan pod yang diinginkan dikeluarkan dari node jika terjadi kekurangan sumber daya, maka selain prioritasnya, Anda juga perlu mengatur permintaan/batasnya.
Mekanisme penskalaan otomatis pod aplikasi (HPA) secara horizontal
Ketika tugasnya adalah menambah dan mengurangi jumlah pod secara otomatis tergantung pada penggunaan sumber daya (sistem - CPU/RAM atau pengguna - rps), entitas k8s seperti HPA (Penskala Pod Horizontal). Algoritmanya adalah sebagai berikut:
Pembacaan saat ini dari sumber daya yang diamati ditentukan (currentMetricValue)
Nilai yang diinginkan untuk sumber daya ditentukan (desiredMetricValue), yang untuk sumber daya sistem ditetapkan menggunakan permintaan
Jumlah replika saat ini ditentukan (Replika saat ini)
Rumus berikut menghitung jumlah replika yang diinginkan (Replika yang diinginkan)
Replika yang diinginkan = [Replika saat ini * ( NilaiMetrik Saat Ini / NilaiMetrik yang diinginkan )]
Dalam hal ini, penskalaan tidak akan terjadi ketika koefisien (currentMetricValue/desiredMetricValue) mendekati 1 (dalam hal ini, kita dapat mengatur sendiri kesalahan yang diizinkan; secara default adalah 0.1).
Mari kita lihat cara kerja hpa menggunakan contoh aplikasi app-test (digambarkan sebagai Deployment), di mana jumlah replika perlu diubah tergantung pada konsumsi CPU:
Itu. kita melihat bahwa pod aplikasi pada awalnya diluncurkan dalam dua instance, yang masing-masing berisi dua container nginx dan nginx-exporter, yang masing-masing memiliki spesifikasi tertentu permintaan untuk CPU.
Itu. Kami membuat hpa yang akan memantau pengujian aplikasi Deployment dan menyesuaikan jumlah pod dengan aplikasi berdasarkan indikator cpu (kami memperkirakan pod akan menggunakan 30% CPU yang diminta), dengan jumlah replika berada di kisaran 2-10.
Sekarang, mari kita lihat mekanisme pengoperasian hpa jika kita memberikan beban pada salah satu perapian:
# kubectl top pod
NAME CPU(cores) MEMORY(bytes)
app-test-78559f8f44-pgs58 101m 243Mi
app-test-78559f8f44-cj4jz 4m 240Mi
Secara total kami memiliki yang berikut:
Nilai yang diinginkan (desiredMetricValue) - sesuai dengan pengaturan hpa, kami memiliki 30%
Nilai saat ini (currentMetricValue) - untuk penghitungan, pengontrol-manajer menghitung nilai rata-rata konsumsi sumber daya dalam %, yaitu. secara kondisional melakukan hal berikut:
Menerima nilai absolut metrik pod dari server metrik, mis. 101m dan 4m
Mendapat nilai absolut untuk konsumsi sumber daya yang diinginkan (untuk ini, permintaan semua kontainer dijumlahkan) 60m + 30m = 90m
Menghitung persentase rata-rata konsumsi CPU relatif terhadap pod permintaan, mis. 53m / 90m * 100% = 59%
Sekarang kita memiliki semua yang kita perlukan untuk menentukan apakah kita perlu mengubah jumlah replika; untuk melakukan ini, kita menghitung koefisiennya:
ratio = 59% / 30% = 1.96
Itu. jumlah replika harus ditingkatkan ~2 kali lipat dan jumlahnya menjadi [2 * 1.96] = 4.
Kesimpulan: Seperti yang Anda lihat, agar mekanisme ini berfungsi, kondisi yang diperlukan adalah adanya permintaan untuk semua container di pod yang diamati.
Mekanisme penskalaan otomatis node secara horizontal (Cluster Autoscaler)
Untuk menetralisir dampak negatif pada sistem selama lonjakan beban, memiliki hpa yang dikonfigurasi saja tidak cukup. Misalnya, menurut pengaturan di manajer pengontrol hpa, diputuskan bahwa jumlah replika perlu ditingkatkan sebanyak 2 kali lipat, namun node tidak memiliki sumber daya bebas untuk menjalankan sejumlah pod (yaitu node tidak dapat menyediakan sumber daya yang diminta ke pod permintaan) dan pod ini beralih ke status Tertunda.
Dalam hal ini, jika penyedia memiliki IaaS/PaaS yang sesuai (misalnya, GKE/GCE, AKS, EKS, dll.), alat seperti Penskala Otomatis Node. Hal ini memungkinkan Anda untuk mengatur jumlah maksimum dan minimum node dalam cluster dan secara otomatis menyesuaikan jumlah node saat ini (dengan memanggil API penyedia cloud untuk memesan/menghapus sebuah node) ketika ada kekurangan sumber daya di cluster dan pod. tidak dapat dijadwalkan (berada dalam status Tertunda).
Kesimpulan: Untuk dapat melakukan penskalaan otomatis pada node, perlu untuk mengatur permintaan dalam kontainer pod sehingga k8s dapat menilai beban pada node dengan benar dan melaporkan bahwa tidak ada sumber daya di cluster untuk meluncurkan pod berikutnya.
Kesimpulan
Perlu dicatat bahwa menetapkan batas sumber daya kontainer bukan merupakan persyaratan agar aplikasi dapat berjalan dengan sukses, namun tetap lebih baik melakukannya karena alasan berikut:
Untuk pengoperasian penjadwal yang lebih akurat dalam hal penyeimbangan beban antar node k8s
Untuk mengurangi kemungkinan terjadinya peristiwa “penggusuran pod”.
Agar penskalaan otomatis pod aplikasi (HPA) horizontal berfungsi
Untuk penskalaan otomatis node secara horizontal (Cluster Autoscaling) untuk penyedia cloud