Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes
Artikel ini akan membantu anda memahami cara pengimbangan beban berfungsi dalam Kubernetes, perkara yang berlaku apabila menskalakan sambungan jangka panjang dan sebab anda harus mempertimbangkan pengimbangan sisi klien jika anda menggunakan HTTP/2, gRPC, RSockets, AMQP atau protokol jangka hayat yang lain . 

Sedikit tentang cara trafik diagihkan semula dalam Kubernetes 

Kubernetes menyediakan dua abstraksi yang mudah untuk menggunakan aplikasi: Perkhidmatan dan Penerapan.

Alokasi menerangkan cara dan bilangan salinan aplikasi anda harus dijalankan pada bila-bila masa. Setiap aplikasi digunakan sebagai Pod dan diberikan alamat IP.

Perkhidmatan mempunyai fungsi yang serupa dengan pengimbang beban. Mereka direka bentuk untuk mengedarkan trafik merentas berbilang pod.

Jom tengok macam mana rupanya.

  1. Dalam rajah di bawah anda boleh melihat tiga contoh aplikasi yang sama dan pengimbang beban:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Pengimbang beban dipanggil Perkhidmatan dan diberikan alamat IP. Sebarang permintaan masuk diubah hala ke salah satu pod:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Senario penempatan menentukan bilangan kejadian aplikasi. Anda hampir tidak perlu mengembangkan terus di bawah:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  4. Setiap pod diberikan alamat IPnya sendiri:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Adalah berguna untuk menganggap perkhidmatan sebagai koleksi alamat IP. Setiap kali anda mengakses perkhidmatan, salah satu alamat IP dipilih daripada senarai dan digunakan sebagai alamat destinasi.

Ia kelihatan seperti ini.

  1. Permintaan curl 10.96.45.152 diterima kepada perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Perkhidmatan memilih satu daripada tiga alamat pod sebagai destinasi:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Trafik diubah hala ke pod tertentu:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Jika aplikasi anda terdiri daripada bahagian hadapan dan bahagian belakang, maka anda akan mempunyai kedua-dua perkhidmatan dan penggunaan untuk setiap satu.

Apabila bahagian hadapan membuat permintaan kepada bahagian belakang, ia tidak perlu mengetahui dengan tepat berapa banyak pod yang disediakan oleh bahagian belakang: mungkin terdapat satu, sepuluh atau seratus.

Selain itu, bahagian hadapan tidak mengetahui apa-apa tentang alamat pod yang menyediakan bahagian belakang.

Apabila bahagian hadapan membuat permintaan kepada bahagian belakang, ia menggunakan alamat IP perkhidmatan bahagian belakang, yang tidak berubah.

Beginilah rupanya.

  1. Di bawah 1 meminta komponen bahagian belakang dalaman. Daripada memilih yang khusus untuk bahagian belakang, ia membuat permintaan kepada perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Perkhidmatan memilih salah satu pod hujung belakang sebagai alamat destinasi:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Trafik pergi dari Pod 1 ke Pod 5, dipilih oleh perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  4. Bawah 1 tidak tahu dengan tepat berapa banyak pod seperti bawah 5 yang tersembunyi di sebalik perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Tetapi bagaimana sebenarnya perkhidmatan mengedarkan permintaan? Nampaknya round-robin balancing digunakan? Mari kita fikirkan. 

Mengimbangi dalam perkhidmatan Kubernetes

Perkhidmatan Kubernetes tidak wujud. Tiada proses untuk perkhidmatan yang diberikan alamat IP dan port.

Anda boleh mengesahkan ini dengan log masuk ke mana-mana nod dalam kluster dan menjalankan perintah netstat -ntlp.

Anda tidak akan dapat mencari alamat IP yang diperuntukkan kepada perkhidmatan tersebut.

Alamat IP perkhidmatan terletak di lapisan kawalan, dalam pengawal, dan direkodkan dalam pangkalan data - etcd. Alamat yang sama digunakan oleh komponen lain - kube-proxy.
Kube-proxy menerima senarai alamat IP untuk semua perkhidmatan dan menjana satu set peraturan iptables pada setiap nod dalam kelompok.

Peraturan ini menyatakan: "Jika kami melihat alamat IP perkhidmatan, kami perlu mengubah suai alamat destinasi permintaan dan menghantarnya ke salah satu pod."

Alamat IP perkhidmatan hanya digunakan sebagai titik masuk dan tidak disediakan oleh sebarang proses mendengar alamat dan port IP tersebut.

Mari lihat ini

  1. Pertimbangkan sekumpulan tiga nod. Setiap nod mempunyai pod:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Pod terikat dicat kuning air adalah sebahagian daripada perkhidmatan. Kerana perkhidmatan tidak wujud sebagai proses, ia ditunjukkan dalam warna kelabu:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Pod pertama meminta perkhidmatan dan mesti pergi ke salah satu pod yang berkaitan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  4. Tetapi perkhidmatan itu tidak wujud, proses itu tidak wujud. Bagaimanakah ia berfungsi?

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  5. Sebelum permintaan meninggalkan nod, ia melalui peraturan iptables:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  6. Peraturan iptables mengetahui bahawa perkhidmatan itu tidak wujud dan menggantikan alamat IPnya dengan salah satu alamat IP pod yang dikaitkan dengan perkhidmatan itu:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  7. Permintaan menerima alamat IP yang sah sebagai alamat destinasi dan diproses seperti biasa:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  8. Bergantung pada topologi rangkaian, permintaan akhirnya sampai ke pod:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Bolehkah iptables memuatkan baki?

Tidak, iptables digunakan untuk penapisan dan tidak direka untuk mengimbangi.

Walau bagaimanapun, adalah mungkin untuk menulis satu set peraturan yang berfungsi seperti pengimbang pseudo.

Dan inilah yang dilaksanakan dalam Kubernetes.

Jika anda mempunyai tiga pod, kube-proxy akan menulis peraturan berikut:

  1. Pilih sub pertama dengan kebarangkalian 33%, jika tidak pergi ke peraturan seterusnya.
  2. Pilih yang kedua dengan kebarangkalian 50%, jika tidak pergi ke peraturan seterusnya.
  3. Pilih yang ketiga di bawah.

Sistem ini menyebabkan setiap pod dipilih dengan kebarangkalian 33%.

Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Dan tiada jaminan bahawa Pod 2 akan dipilih seterusnya selepas Pod 1.

Nota: iptables menggunakan modul statistik dengan taburan rawak. Oleh itu, algoritma pengimbangan adalah berdasarkan pemilihan rawak.

Sekarang setelah anda memahami cara perkhidmatan berfungsi, mari lihat senario perkhidmatan yang lebih menarik.

Sambungan jangka panjang dalam Kubernetes tidak berskala secara lalai

Setiap permintaan HTTP dari bahagian hadapan ke bahagian belakang disediakan oleh sambungan TCP yang berasingan, yang dibuka dan ditutup.

Jika bahagian hadapan menghantar 100 permintaan sesaat ke bahagian belakang, maka 100 sambungan TCP yang berbeza dibuka dan ditutup.

Anda boleh mengurangkan masa pemprosesan permintaan dan memuatkan dengan membuka satu sambungan TCP dan menggunakannya untuk semua permintaan HTTP berikutnya.

Protokol HTTP mempunyai ciri yang dipanggil HTTP keep-alive, atau penggunaan semula sambungan. Dalam kes ini, sambungan TCP tunggal digunakan untuk menghantar dan menerima berbilang permintaan dan respons HTTP:

Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Ciri ini tidak didayakan secara lalai: kedua-dua pelayan dan klien mesti dikonfigurasikan dengan sewajarnya.

Persediaan itu sendiri adalah mudah dan boleh diakses untuk kebanyakan bahasa pengaturcaraan dan persekitaran.

Berikut ialah beberapa pautan kepada contoh dalam bahasa yang berbeza:

Apakah yang berlaku jika kita menggunakan keep-alive dalam perkhidmatan Kubernetes?
Mari kita anggap bahawa kedua-dua bahagian hadapan dan bahagian belakang menyokong kekal hidup.

Kami mempunyai satu salinan bahagian hadapan dan tiga salinan bahagian belakang. Bahagian hadapan membuat permintaan pertama dan membuka sambungan TCP ke bahagian belakang. Permintaan mencapai perkhidmatan, salah satu pod hujung belakang dipilih sebagai alamat destinasi. Bahagian belakang menghantar respons, dan bahagian hadapan menerimanya.

Tidak seperti keadaan biasa di mana sambungan TCP ditutup selepas menerima respons, ia kini dibuka untuk permintaan HTTP selanjutnya.

Apakah yang berlaku jika bahagian hadapan menghantar lebih banyak permintaan ke bahagian belakang?

Untuk memajukan permintaan ini, sambungan TCP terbuka akan digunakan, semua permintaan akan pergi ke hujung belakang yang sama di mana permintaan pertama pergi.

Bukankah iptables sepatutnya mengagihkan semula trafik?

Tidak dalam kes ini.

Apabila sambungan TCP dibuat, ia melalui peraturan iptables, yang memilih bahagian belakang tertentu ke mana trafik akan pergi.

Memandangkan semua permintaan berikutnya adalah pada sambungan TCP yang sudah terbuka, peraturan iptables tidak lagi dipanggil.

Jom tengok macam mana rupanya.

  1. Pod pertama menghantar permintaan kepada perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Anda sudah tahu apa yang akan berlaku seterusnya. Perkhidmatan tidak wujud, tetapi terdapat peraturan iptables yang akan memproses permintaan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Salah satu pod bahagian belakang akan dipilih sebagai alamat destinasi:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  4. Permintaan itu sampai ke pod. Pada ketika ini, sambungan TCP yang berterusan antara kedua-dua pod akan diwujudkan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  5. Sebarang permintaan seterusnya daripada pod pertama akan melalui sambungan yang telah ditetapkan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Hasilnya ialah masa tindak balas yang lebih pantas dan daya pemprosesan yang lebih tinggi, tetapi anda kehilangan keupayaan untuk menskala bahagian belakang.

Walaupun anda mempunyai dua pod di bahagian belakang, dengan sambungan berterusan, trafik akan sentiasa pergi ke salah satu daripadanya.

Bolehkah ini diperbaiki?

Memandangkan Kubernetes tidak tahu cara mengimbangi sambungan berterusan, tugas ini diserahkan kepada anda.

Perkhidmatan ialah koleksi alamat IP dan port yang dipanggil titik akhir.

Aplikasi anda boleh mendapatkan senarai titik akhir daripada perkhidmatan dan memutuskan cara untuk mengedarkan permintaan antara mereka. Anda boleh membuka sambungan berterusan ke setiap pod dan permintaan keseimbangan antara sambungan ini menggunakan round-robin.

Atau memohon lebih banyak algoritma pengimbangan yang kompleks.

Kod sisi pelanggan yang bertanggungjawab untuk mengimbangi harus mengikut logik ini:

  1. Dapatkan senarai titik akhir daripada perkhidmatan.
  2. Buka sambungan berterusan untuk setiap titik akhir.
  3. Apabila permintaan perlu dibuat, gunakan salah satu sambungan terbuka.
  4. Kemas kini senarai titik akhir secara kerap, buat yang baharu atau tutup sambungan berterusan lama jika senarai berubah.

Inilah rupanya.

  1. Daripada pod pertama menghantar permintaan kepada perkhidmatan, anda boleh mengimbangi permintaan di sisi pelanggan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  2. Anda perlu menulis kod yang menanyakan pod mana yang merupakan sebahagian daripada perkhidmatan:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  3. Sebaik sahaja anda mempunyai senarai, simpannya pada bahagian pelanggan dan gunakannya untuk menyambung ke pod:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

  4. Anda bertanggungjawab untuk algoritma pengimbangan beban:

    Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Sekarang timbul persoalan: adakah masalah ini hanya terpakai kepada HTTP keep-alive?

Pengimbangan beban sebelah pelanggan

HTTP bukan satu-satunya protokol yang boleh menggunakan sambungan TCP berterusan.

Jika aplikasi anda menggunakan pangkalan data, maka sambungan TCP tidak dibuka setiap kali anda perlu membuat permintaan atau mendapatkan semula dokumen daripada pangkalan data. 

Sebaliknya, sambungan TCP berterusan ke pangkalan data dibuka dan digunakan.

Jika pangkalan data anda digunakan pada Kubernetes dan akses disediakan sebagai perkhidmatan, maka anda akan menghadapi masalah yang sama yang diterangkan dalam bahagian sebelumnya.

Satu replika pangkalan data akan lebih dimuatkan daripada yang lain. Kube-proxy dan Kubernetes tidak akan membantu mengimbangi sambungan. Anda mesti berhati-hati untuk mengimbangi pertanyaan ke pangkalan data anda.

Bergantung pada perpustakaan yang anda gunakan untuk menyambung ke pangkalan data, anda mungkin mempunyai pilihan yang berbeza untuk menyelesaikan masalah ini.

Di bawah ialah contoh mengakses kluster pangkalan data MySQL daripada Node.js:

var mysql = require('mysql');
var poolCluster = mysql.createPoolCluster();

var endpoints = /* retrieve endpoints from the Service */

for (var [index, endpoint] of endpoints) {
  poolCluster.add(`mysql-replica-${index}`, endpoint);
}

// Make queries to the clustered MySQL database

Terdapat banyak protokol lain yang menggunakan sambungan TCP berterusan:

  • WebSockets dan WebSockets terjamin
  • HTTP / 2
  • gRPC
  • RSockets
  • AMQP

Anda sepatutnya sudah biasa dengan kebanyakan protokol ini.

Tetapi jika protokol ini sangat popular, mengapa tidak ada penyelesaian pengimbangan piawai? Mengapa logik pelanggan perlu diubah? Adakah terdapat penyelesaian Kubernetes asli?

Kube-proxy dan iptables direka bentuk untuk merangkumi kebanyakan kes penggunaan biasa apabila digunakan ke Kubernetes. Ini untuk kemudahan.

Jika anda menggunakan perkhidmatan web yang mendedahkan API REST, anda beruntung - dalam kes ini, sambungan TCP berterusan tidak digunakan, anda boleh menggunakan mana-mana perkhidmatan Kubernetes.

Tetapi sebaik sahaja anda mula menggunakan sambungan TCP yang berterusan, anda perlu memikirkan cara untuk mengagihkan beban secara sama rata pada bahagian belakang. Kubernetes tidak mengandungi penyelesaian sedia untuk kes ini.

Walau bagaimanapun, sudah tentu ada pilihan yang boleh membantu.

Mengimbangi sambungan jangka panjang dalam Kubernetes

Terdapat empat jenis perkhidmatan dalam Kubernetes:

  1. KlusterIP
  2. NodePort
  3. LoadBalancer
  4. Tanpa kepala

Tiga perkhidmatan pertama beroperasi berdasarkan alamat IP maya, yang digunakan oleh kube-proxy untuk membina peraturan iptables. Tetapi asas asas semua perkhidmatan adalah perkhidmatan tanpa kepala.

Perkhidmatan tanpa kepala tidak mempunyai sebarang alamat IP yang dikaitkan dengannya dan hanya menyediakan mekanisme untuk mendapatkan semula senarai alamat IP dan port pod (titik akhir) yang dikaitkan dengannya.

Semua perkhidmatan adalah berdasarkan perkhidmatan tanpa kepala.

Perkhidmatan ClusterIP ialah perkhidmatan tanpa kepala dengan beberapa tambahan: 

  1. Lapisan pengurusan memberikannya alamat IP.
  2. Kube-proxy menjana peraturan iptables yang diperlukan.

Dengan cara ini anda boleh mengabaikan kube-proxy dan terus menggunakan senarai titik akhir yang diperoleh daripada perkhidmatan tanpa kepala untuk memuatkan baki aplikasi anda.

Tetapi bagaimana kita boleh menambah logik yang sama kepada semua aplikasi yang digunakan dalam kelompok?

Jika aplikasi anda telah digunakan, tugas ini mungkin kelihatan mustahil. Walau bagaimanapun, terdapat pilihan alternatif.

Service Mesh akan membantu anda

Anda mungkin telah perasan bahawa strategi pengimbangan beban pihak pelanggan adalah agak standard.

Apabila aplikasi bermula, ia:

  1. Mendapat senarai alamat IP daripada perkhidmatan.
  2. Membuka dan mengekalkan kolam sambungan.
  3. Mengemas kini kumpulan secara berkala dengan menambah atau mengalih keluar titik tamat.

Sebaik sahaja permohonan itu ingin membuat permintaan, ia:

  1. Memilih sambungan yang tersedia menggunakan beberapa logik (cth round-robin).
  2. Melaksanakan permintaan.

Langkah-langkah ini berfungsi untuk kedua-dua sambungan WebSockets, gRPC dan AMQP.

Anda boleh memisahkan logik ini ke dalam perpustakaan yang berasingan dan menggunakannya dalam aplikasi anda.

Walau bagaimanapun, anda boleh menggunakan jejaring perkhidmatan seperti Istio atau Linkerd sebaliknya.

Service Mesh menambah permohonan anda dengan proses yang:

  1. Secara automatik mencari alamat IP perkhidmatan.
  2. Menguji sambungan seperti WebSockets dan gRPC.
  3. Mengimbangi permintaan menggunakan protokol yang betul.

Service Mesh membantu mengurus trafik dalam kelompok, tetapi ia agak intensif sumber. Pilihan lain menggunakan perpustakaan pihak ketiga seperti Netflix Ribbon atau proksi boleh atur cara seperti Envoy.

Apa yang berlaku jika anda mengabaikan isu pengimbangan?

Anda boleh memilih untuk tidak menggunakan pengimbangan beban dan masih tidak melihat sebarang perubahan. Mari lihat beberapa senario kerja.

Jika anda mempunyai lebih banyak pelanggan daripada pelayan, ini bukan masalah besar.

Katakan terdapat lima pelanggan yang menyambung ke dua pelayan. Walaupun tiada pengimbangan, kedua-dua pelayan akan digunakan:

Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Sambungan mungkin tidak diagihkan sama rata: mungkin empat pelanggan disambungkan ke pelayan yang sama, tetapi terdapat kemungkinan besar kedua-dua pelayan akan digunakan.

Apa yang lebih bermasalah ialah senario sebaliknya.

Jika anda mempunyai lebih sedikit pelanggan dan lebih banyak pelayan, sumber anda mungkin kurang digunakan dan potensi kesesakan akan muncul.

Katakan terdapat dua pelanggan dan lima pelayan. Dalam kes terbaik, akan ada dua sambungan kekal ke dua pelayan daripada lima.

Pelayan yang tinggal akan melahu:

Pengimbangan beban dan penskalaan sambungan jangka panjang dalam Kubernetes

Jika kedua-dua pelayan ini tidak dapat mengendalikan permintaan pelanggan, penskalaan mendatar tidak akan membantu.

Kesimpulan

Perkhidmatan Kubernetes direka bentuk untuk berfungsi dalam kebanyakan senario aplikasi web standard.

Walau bagaimanapun, sebaik sahaja anda mula bekerja dengan protokol aplikasi yang menggunakan sambungan TCP berterusan, seperti pangkalan data, gRPC atau WebSockets, perkhidmatan tidak lagi sesuai. Kubernetes tidak menyediakan mekanisme dalaman untuk mengimbangi sambungan TCP yang berterusan.

Ini bermakna anda mesti menulis aplikasi dengan mengimbangi sisi klien dalam fikiran.

Terjemahan disediakan oleh pasukan Kubernetes aaS daripada Mail.ru.

Apa lagi yang perlu dibaca mengenai topik tersebut:

  1. Tiga tahap autoscaling dalam Kubernetes dan cara menggunakannya dengan berkesan
  2. Kubernetes dalam semangat cetak rompak dengan templat untuk pelaksanaan.
  3. Saluran Telegram kami tentang transformasi digital.

Sumber: www.habr.com

Tambah komen