Mengasuransikan sumber daya di Kuba

gambar


Manajemen sumber daya cluster selalu menjadi topik yang kompleks. Bagaimana cara menjelaskan kebutuhan untuk mengkonfigurasi sumber daya untuk pengguna yang menyebarkan aplikasi mereka ke cluster? Mungkin lebih mudah untuk mengotomatisasi ini?


Deskripsi masalah


Manajemen sumber daya adalah tugas penting dalam konteks administrasi cluster Kubernetes. Tetapi mengapa penting jika Kubernetes melakukan semua kerja keras untuk Anda? Karena tidak. Kubernetes memberi Anda alat yang mudah untuk menyelesaikan banyak masalah ... jika Anda menggunakan alat ini. Untuk setiap pod di cluster Anda, Anda dapat menentukan sumber daya yang dibutuhkan untuk kontainernya. Dan Kubernetes akan menggunakan informasi ini untuk mendistribusikan contoh aplikasi Anda di seluruh node cluster.


Hanya sedikit orang yang menganggap manajemen sumber daya di Kubernetes serius. Ini normal untuk kluster yang ringan dengan beberapa aplikasi statis. Tetapi bagaimana jika Anda memiliki kluster yang sangat dinamis? Di mana aplikasi datang dan pergi, di mana namespace dibuat dan dihapus sepanjang waktu? Cluster dengan sejumlah besar pengguna yang dapat membuat namespace mereka sendiri dan menggunakan aplikasi? Nah, dalam hal ini, alih-alih orkestrasi yang stabil dan dapat diprediksi, Anda akan memiliki banyak crash acak dalam aplikasi, dan kadang-kadang bahkan dalam komponen Kubernetes sendiri!


Berikut adalah contoh dari kluster tersebut:


gambar


Anda melihat 3 perapian di negara bagian "Terminating". Tapi ini bukan penghilangan perapian yang biasa - mereka terjebak dalam keadaan ini karena daemon yang berisi pada node mereka dihantam oleh sesuatu yang sangat haus sumber daya.


Masalah seperti itu dapat diatasi dengan menangani dengan benar kekurangan sumber daya, tetapi ini bukan topik dari artikel ini (ada artikel yang bagus), dan juga bukan peluru perak untuk menyelesaikan semua masalah dengan sumber daya.


Alasan utama untuk masalah seperti itu adalah salah atau kurangnya manajemen sumber daya di cluster. Dan jika masalah semacam ini bukan merupakan bencana bagi penyebaran, karena mereka akan dengan mudah membuat yang baru bekerja di bawah, maka untuk entitas seperti DaemonSet, atau bahkan lebih untuk StatefulSet, pembekuan seperti itu akan berakibat fatal dan memerlukan intervensi manual.


Anda dapat memiliki cluster besar dengan banyak CPU dan memori. Saat Anda menjalankan banyak aplikasi di atasnya tanpa pengaturan sumber daya yang tepat, ada kemungkinan semua pod sumber daya intensif akan ditempatkan pada node yang sama. Mereka akan memperjuangkan sumber daya, bahkan jika node yang tersisa dari cluster tetap praktis gratis.


Anda juga dapat sering melihat kasus-kasus yang kurang kritis di mana beberapa aplikasi dipengaruhi oleh tetangga mereka. Bahkan jika aplikasi "tidak bersalah" ini memiliki sumber daya mereka dikonfigurasi dengan benar, yang keliling mungkin datang dan membunuh mereka. Contoh skenario seperti itu:


  1. Aplikasi Anda meminta memori 4 GB, tetapi pada awalnya hanya membutuhkan 1 GB.
  2. Mengembara di bawah, tanpa konfigurasi sumber daya, ditugaskan ke node yang sama.
  3. Berkeliaran di bawah menghabiskan semua memori yang tersedia.
  4. Aplikasi Anda mencoba mengalokasikan lebih banyak memori dan macet karena tidak ada lagi.

Kasus lain yang cukup populer adalah revaluasi. Beberapa pengembang membuat permintaan besar dalam manifes "berjaga-jaga" dan tidak pernah menggunakan sumber daya ini. Hasilnya adalah buang-buang uang.


Teori keputusan


Horor! Benar?
Untungnya, Kubernetes menawarkan cara untuk memberlakukan beberapa pembatasan pada pod dengan menentukan konfigurasi sumber daya default serta nilai minimum dan maksimum. Ini diimplementasikan menggunakan objek LimitRange . LimitRange adalah alat yang sangat nyaman ketika Anda memiliki jumlah ruang nama yang terbatas atau kendali penuh atas proses pembuatannya. Bahkan tanpa konfigurasi sumber daya yang tepat, aplikasi Anda akan terbatas dalam penggunaannya. "Innocent", perapian yang disetel dengan benar akan aman dan terlindungi dari tetangga yang berbahaya. Jika seseorang menyebarkan aplikasi serakah tanpa konfigurasi sumber daya, aplikasi ini akan menerima nilai default dan mungkin akan macet. Dan itu saja! Aplikasi tidak akan lagi menyeret siapa pun.


Jadi, kami memiliki alat untuk mengontrol dan memaksa konfigurasi sumber daya untuk perapian, sekarang tampaknya kami aman. Jadi? Tidak juga. Faktanya adalah bahwa, seperti yang kami jelaskan sebelumnya, ruang nama kami dapat dibuat oleh pengguna, dan oleh karena itu, LimitRange mungkin tidak ada di ruang nama tersebut, karena itu harus dibuat di setiap ruang nama secara terpisah. Oleh karena itu, kita memerlukan sesuatu tidak hanya di level namespace, tetapi juga di level cluster. Tetapi belum ada fungsi seperti itu di Kubernetes.


Itu sebabnya saya memutuskan untuk menulis solusi untuk masalah ini. Biarkan saya memperkenalkan Anda - Batasi Operator. Ini adalah operator yang dibuat atas dasar kerangka kerja Operator SDK , yang menggunakan sumber daya khusus ClusterLimit dan membantu mengamankan semua aplikasi "tidak bersalah" di dalam kluster. Dengan menggunakan operator ini, Anda dapat mengontrol nilai default dan batas sumber daya untuk semua ruang nama menggunakan jumlah minimum konfigurasi. Ini juga memungkinkan Anda untuk memilih di mana tepatnya menerapkan konfigurasi menggunakan namespaceSelector.


Contoh ClusterLimit
apiVersion: limit.myafq.com/v1alpha1 kind: ClusterLimit metadata: name: default-limit spec: namespaceSelector: matchLabels: limit: "limited" limitRange: limits: - type: Container max: cpu: "800m" memory: "1Gi" min: cpu: "100m" memory: "99Mi" default: cpu: "700m" memory: "900Mi" defaultRequest: cpu: "110m" memory: "111Mi" - type: Pod max: cpu: "2" memory: "2Gi" 

Dengan konfigurasi ini, operator akan membuat LimitRange hanya di namespace dengan label limit: limited . Ini akan berguna untuk memberikan batasan yang lebih ketat dalam kelompok ruang nama tertentu. Jika namespaceSelector tidak ditentukan, operator akan menerapkan LimitRange ke semua ruang nama. Jika Anda ingin mengonfigurasi LimitRange secara manual untuk namespace tertentu, Anda dapat menggunakan anotasi "limit.myafq.com/unlimited": true ini akan memberitahu operator untuk melewati namespace ini dan tidak membuat LimitRange secara otomatis.


Contoh skrip untuk menggunakan operator:


  • Buat ClusterLimit default dengan batasan liberal dan tanpa namespaceSelector - itu akan diterapkan di mana-mana.
  • Untuk sekumpulan ruang nama dengan aplikasi ringan, buat ClusterLimit tambahan, lebih ketat, dengan namespaceSelector. Letakkan label pada ruang nama ini sesuai.
  • Pada namespace dengan aplikasi yang sangat intensif sumber daya, letakkan anotasi "limit.myafq.com/unlimited": true dan konfigurasikan LimitRange secara manual dengan batas yang jauh lebih luas daripada yang ditentukan dalam ClusteLimit default.

Hal penting yang perlu diketahui tentang beberapa LimitRange dalam satu ruang nama:
Ketika sub dibuat di namespace dengan beberapa LimitRange, default terbesar akan diambil untuk mengkonfigurasi sumber dayanya. Tetapi nilai maksimum dan minimum akan diperiksa sesuai dengan LimitRange yang paling ketat.

Contoh praktis


Operator akan melacak semua perubahan di semua namespace, ClusterLimits, child LimitRanges dan akan memulai koordinasi keadaan cluster dengan setiap perubahan pada objek yang dipantau. Mari kita lihat cara kerjanya dalam praktik.


Untuk memulai, buat di bawah tanpa batasan apa pun:


kubectl jalankan / dapatkan output
 ❯() kubectl run --generator=run-pod/v1 --image=bash bash pod/bash created ❯() kubectl get pod bash -o yaml apiVersion: v1 kind: Pod metadata: labels: run: bash name: bash namespace: default spec: containers: - image: bash name: bash resources: {} 

Catatan: bagian dari output perintah telah dihilangkan untuk menyederhanakan contoh.


Seperti yang Anda lihat, bidang "sumber daya" kosong, yang berarti sub ini dapat diluncurkan di mana saja.
Sekarang kita akan membuat ClusterLimit default untuk seluruh cluster dengan nilai yang cukup liberal:


default-limit.yaml
 apiVersion: limit.myafq.com/v1alpha1 kind: ClusterLimit metadata: name: default-limit spec: limitRange: limits: - type: Container max: cpu: "4" memory: "5Gi" default: cpu: "700m" memory: "900Mi" defaultRequest: cpu: "500m" memory: "512Mi" 

Dan juga lebih ketat untuk subset ruang nama:


restriktif-limit.yaml
 apiVersion: limit.myafq.com/v1alpha1 kind: ClusterLimit metadata: name: restrictive-limit spec: namespaceSelector: matchLabels: limit: "restrictive" limitRange: limits: - type: Container max: cpu: "800m" memory: "1Gi" default: cpu: "100m" memory: "128Mi" defaultRequest: cpu: "50m" memory: "64Mi" - type: Pod max: cpu: "2" memory: "2Gi" 

Kemudian buat ruang nama dan pod di dalamnya untuk melihat cara kerjanya.
Namespace normal dengan batasan default:


 apiVersion: v1 kind: Namespace metadata: name: regular 

Dan namespace yang sedikit lebih terbatas, menurut legenda - untuk aplikasi ringan:


 apiVersion: v1 kind: Namespace metadata: labels: limit: "restrictive" name: lightweight 

Jika Anda melihat log operator segera setelah membuat namespace, Anda dapat menemukan sesuatu seperti itu di bawah spoiler:


log operator
 {...,"msg":"Reconciling ClusterLimit","Triggered by":"/regular"} {...,"msg":"Creating new namespace LimitRange.","Namespace":"regular","LimitRange":"default-limit"} {...,"msg":"Updating namespace LimitRange.","Namespace":"regular","Name":"default-limit"} {...,"msg":"Reconciling ClusterLimit","Triggered by":"/lightweight"} {...,"msg":"Creating new namespace LimitRange.","Namespace":"lightweight","LimitRange":"default-limit"} {...,"msg":"Updating namespace LimitRange.","Namespace":"lightweight","Name":"default-limit"} {...,"msg":"Creating new namespace LimitRange.","Namespace":"lightweight","LimitRange":"restrictive-limit"} {...,"msg":"Updating namespace LimitRange.","Namespace":"lightweight","Name":"restrictive-limit"} 

Bagian log yang hilang berisi 3 bidang lainnya yang tidak relevan saat ini


Seperti yang Anda lihat, pembuatan setiap namespace memulai pembuatan LimitRange baru. Namespace yang lebih terbatas mendapat dua LimitRange - default dan lebih ketat.


Sekarang mari kita coba membuat sepasang perapian di ruang nama ini.


kubectl jalankan / dapatkan output
 ❯() kubectl run --generator=run-pod/v1 --image=bash bash -n regular pod/bash created ❯() kubectl get pod bash -o yaml -n regular apiVersion: v1 kind: Pod metadata: annotations: kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container bash; cpu, memory limit for container bash' labels: run: bash name: bash namespace: regular spec: containers: - image: bash name: bash resources: limits: cpu: 700m memory: 900Mi requests: cpu: 500m memory: 512Mi 

Seperti yang Anda lihat, meskipun fakta bahwa kami belum mengubah cara pod dibuat, bidang sumber daya sekarang diisi. Anda mungkin juga memperhatikan anotasi yang dibuat secara otomatis oleh LimitRanger.


Sekarang buat di bawah dalam namespace yang ringan:


kubectl jalankan / dapatkan output
 ❯() kubectl run --generator=run-pod/v1 --image=bash bash -n lightweight pod/bash created ❯() kubectl get pods -n lightweight bash -o yaml apiVersion: v1 kind: Pod metadata: annotations: kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container bash; cpu, memory limit for container bash' labels: run: bash name: bash namespace: lightweight spec: containers: - image: bash name: bash resources: limits: cpu: 700m memory: 900Mi requests: cpu: 500m memory: 512Mi 

Harap perhatikan bahwa sumber daya di perapian sama dengan contoh sebelumnya. Ini karena dalam kasus beberapa LimitRange, nilai standar yang kurang ketat akan digunakan saat membuat pod. Tetapi mengapa kita membutuhkan LimitRange yang lebih terbatas? Ini akan digunakan untuk memeriksa nilai sumber daya maksimum dan minimum. Untuk menunjukkan, kami akan membuat ClusterLimit terbatas kami semakin terbatas:


restriktif-limit.yaml
 apiVersion: limit.myafq.com/v1alpha1 kind: ClusterLimit metadata: name: restrictive-limit spec: namespaceSelector: matchLabels: limit: "restrictive" limitRange: limits: - type: Container max: cpu: "200m" memory: "250Mi" default: cpu: "100m" memory: "128Mi" defaultRequest: cpu: "50m" memory: "64Mi" - type: Pod max: cpu: "2" memory: "2Gi" 

Perhatikan bagian:


 - type: Container max: cpu: "200m" memory: "250Mi" 

Sekarang kita telah menetapkan CPU 200m dan memori 250Mi sebagai maksimum untuk wadah di perapian. Dan sekarang lagi, coba buat di bawah:


 ❯() kubectl run --generator=run-pod/v1 --image=bash bash -n lightweight Error from server (Forbidden): pods "bash" is forbidden: [maximum cpu usage per Container is 200m, but limit is 700m., maximum memory usage per Container is 250Mi, but limit is 900Mi.] 

Sub kami memiliki nilai besar yang ditetapkan oleh LimitRange default dan tidak dapat memulai karena tidak lulus pemeriksaan sumber daya maksimum yang diizinkan.




Ini adalah contoh penggunaan Operator Batas. Cobalah sendiri dan mainkan dengan ClusterLimit dalam instance Kubernetes lokal Anda.


Dalam repositori Operator Batas GitHub , Anda dapat menemukan manifes untuk penggunaan operator, serta kode sumber. Jika Anda ingin memperluas fungsionalitas operator, pencarian-tarik dan pencarian-fitur dipersilakan!


Kesimpulan


Manajemen sumber daya di Kubernetes sangat penting untuk stabilitas dan keandalan aplikasi Anda. Sesuaikan sumber daya perapian Anda jika memungkinkan. Dan gunakan LimitRange untuk memastikan terhadap kasus-kasus ketika itu tidak mungkin. Mengotomatiskan pembuatan LimitRange menggunakan Operator Batas.


Ikuti kiat-kiat ini, dan kluster Anda akan selalu aman dari kekacauan sumber daya alam yang tersesat.

Source: https://habr.com/ru/post/id473908/


All Articles