Kubernet memuat load balancing dan menskalakan koneksi yang berumur panjang


Artikel ini akan membantu Anda memahami cara kerja load balancing di Kubernetes, apa yang terjadi ketika menskalakan koneksi berumur panjang dan mengapa Anda harus mempertimbangkan balancing di sisi klien jika Anda menggunakan HTTP / 2, gRPC, RSockets, AMQP atau protokol lain yang berumur panjang. 

Sedikit tentang bagaimana lalu lintas didistribusikan kembali di Kubernetes 


Kubernetes menyediakan dua abstraksi yang mudah digunakan untuk meluncurkan aplikasi: Layanan dan Penyebaran.

Penyebaran menjelaskan bagaimana dan berapa banyak salinan aplikasi Anda harus berjalan pada waktu tertentu. Setiap aplikasi ditempatkan sebagai di bawah (Pod) dan diberi alamat IP.

Layanan fitur mirip dengan penyeimbang beban. Mereka dirancang untuk mendistribusikan lalu lintas di beberapa perapian.

Mari kita lihat tampilannya .

  1. Dalam diagram di bawah ini, Anda melihat tiga contoh aplikasi yang sama dan penyeimbang beban:

  2. Penyeimbang beban disebut Layanan, itu diberikan alamat IP. Setiap permintaan masuk dialihkan ke salah satu pod:

  3. Skrip penerapan menentukan jumlah instance aplikasi. Anda hampir tidak perlu menggunakan langsung di bawah:

  4. Setiap pod diberi alamat IP sendiri:



Sangat berguna untuk mempertimbangkan layanan sebagai satu set alamat IP. Setiap kali Anda mengakses layanan, salah satu alamat IP dipilih dari daftar dan digunakan sebagai alamat tujuan.

Ini sebagai berikut .

  1. Ada permintaan curl 10.96.45.152 ke layanan:

  2. Layanan memilih salah satu dari tiga alamat pod sebagai tujuan:

  3. Lalu lintas dialihkan ke pod tertentu:



Jika aplikasi Anda terdiri dari frontend dan backend, maka Anda akan memiliki layanan dan penyebaran untuk masing-masing.

Ketika frontend memenuhi permintaan ke backend, ia tidak perlu tahu persis berapa banyak tumpukan yang dilayani: mungkin ada satu, sepuluh, atau seratus.

Juga, frontend tidak tahu apa-apa tentang alamat perapian yang melayani backend.

Ketika frontend membuat permintaan ke backend, ia menggunakan alamat IP dari layanan backend, yang tidak berubah.

Ini tampilannya .

  1. Di bawah 1 meminta komponen internal backend. Alih-alih memilih yang spesifik untuk backend, ia melakukan permintaan layanan:

  2. Layanan memilih salah satu pod backend sebagai alamat tujuan:

  3. Lalu lintas beralih dari perapian 1 ke perapian 5 yang dipilih oleh layanan:

  4. Di bawah 1, ia tidak tahu persis berapa banyak perapian seperti di bawah 5 yang tersembunyi di balik layanan:



Tetapi bagaimana tepatnya layanan ini mendistribusikan permintaan? Apakah penyeimbangan round-robin tampaknya digunakan? Mari kita perbaiki. 

Menyeimbangkan dalam Layanan Kubernetes


Layanan Kubernetes tidak ada. Tidak ada proses untuk layanan yang diberikan alamat dan port IP.

Anda dapat memverifikasi ini dengan masuk ke simpul apa pun di kluster dan menjalankan perintah netstat -ntlp.

Anda bahkan tidak dapat menemukan alamat IP yang dialokasikan untuk layanan ini.

Alamat IP dari layanan ini terletak di lapisan kontrol, di controller, dan dicatat dalam database - etcd. Alamat yang sama digunakan oleh komponen lain - kube-proxy.
Kube-proxy menerima daftar alamat IP untuk semua layanan dan membentuk seperangkat aturan iptables pada setiap node cluster.

Aturan-aturan ini mengatakan: "Jika kita melihat alamat IP layanan, kita perlu mengubah alamat tujuan permintaan dan mengirimkannya ke salah satu pod."

Alamat IP layanan hanya digunakan sebagai titik masuk dan tidak dilayani oleh proses apa pun yang mendengarkan alamat dan port ip ini.

Mari kita lihat itu

  1. Pertimbangkan sekelompok tiga simpul. Ada pod pada setiap node:

  2. Perapian rajutan yang dicat krem ​​adalah bagian dari layanan ini. Karena layanan tidak ada sebagai suatu proses, maka layanan ini berwarna abu-abu:

  3. Yang pertama meminta layanan dan harus jatuh pada salah satu perapian terkait:

  4. Tetapi layanan tidak ada, tidak ada proses. Bagaimana cara kerjanya?

  5. Sebelum permintaan meninggalkan node, ia melewati aturan iptables:

  6. Aturan iptables tahu bahwa tidak ada layanan, dan ganti alamat IP-nya dengan salah satu alamat IP dari pod yang terkait dengan layanan ini:

  7. Permintaan menerima alamat IP yang valid sebagai alamat tujuan dan biasanya diproses:

  8. Bergantung pada topologi jaringan, permintaan akhirnya mencapai perapian:



Apakah iptables dapat menyeimbangkan beban?


Tidak, iptables digunakan untuk pemfilteran dan tidak dirancang untuk menyeimbangkan.

Namun, dimungkinkan untuk menulis seperangkat aturan yang berfungsi seperti pseudo-balancer .

Dan itulah yang dilakukan Kubernetes.

Jika Anda memiliki tiga pod, kube-proxy akan menulis aturan berikut:

  1. Pilih yang pertama dengan probabilitas 33%, jika tidak pergi ke aturan berikutnya.
  2. Pilih yang kedua dengan probabilitas 50%, jika tidak pergi ke aturan berikutnya.
  3. Pilih yang ketiga di bawah.

Sistem seperti itu mengarah pada fakta bahwa setiap sub dipilih dengan probabilitas 33%.



Dan tidak ada jaminan bahwa di bawah 2 akan dipilih berikutnya setelah file 1.

Catatan : iptables menggunakan modul statistik distribusi acak. Dengan demikian, algoritma balancing didasarkan pada pemilihan acak.

Sekarang setelah Anda memahami cara kerja layanan, mari kita lihat skenario kerja yang lebih menarik.

Koneksi berumur panjang di Kubernetes tidak menskala secara default


Setiap permintaan HTTP dari front-end ke back-end dilayani oleh koneksi TCP yang terpisah, yang membuka dan menutup.

Jika frontend mengirim 100 permintaan per detik ke backend, maka 100 koneksi TCP yang berbeda membuka dan menutup.

Anda dapat mengurangi waktu pemrosesan permintaan dan mengurangi beban jika Anda membuka satu koneksi TCP dan menggunakannya untuk semua permintaan HTTP berikutnya.

Protokol HTTP berisi fitur yang disebut HTTP keep-live, atau penggunaan kembali koneksi. Dalam hal ini, satu koneksi TCP digunakan untuk mengirim dan menerima banyak permintaan dan tanggapan HTTP:



Fitur ini tidak diaktifkan secara default: baik server dan klien harus dikonfigurasi sesuai.

Pengaturannya sendiri sederhana dan dapat diakses untuk sebagian besar bahasa dan lingkungan pemrograman.

Berikut ini beberapa tautan ke contoh dalam berbagai bahasa:


Apa yang terjadi jika kita menggunakan keep-live di Kubernetes?
Mari kita asumsikan bahwa baik dukungan frontend dan backend tetap hidup.

Kami memiliki satu salinan frontend dan tiga salinan backend. Frontend membuat permintaan pertama dan membuka koneksi TCP ke backend. Permintaan mencapai layanan, salah satu pod backend dipilih sebagai alamat tujuan. Ini mengirim respons ke backend, dan frontend menerimanya.

Berbeda dengan situasi yang biasa, ketika koneksi TCP ditutup setelah menerima respons, itu sekarang tetap terbuka untuk permintaan HTTP berikut.

Apa yang terjadi jika frontend mengirim lebih banyak permintaan backend?

Untuk meneruskan permintaan ini, koneksi TCP terbuka akan digunakan, semua permintaan akan dikirim ke yang sama di bawah backend, di mana permintaan pertama didapat.

Tidakkah seharusnya iptables mendistribusikan kembali traffic?

Tidak dalam hal ini.

Ketika koneksi TCP dibuat, ia akan melewati aturan iptables, yang memilih yang spesifik untuk backend di mana lalu lintas akan pergi.

Karena semua permintaan berikut membahas koneksi TCP yang sudah terbuka, aturan iptables tidak lagi dipanggil.

Mari kita lihat tampilannya .

  1. Sub pertama mengirimkan permintaan ke layanan:

  2. Anda sudah tahu apa yang akan terjadi selanjutnya. Layanan tidak ada, tetapi ada aturan iptables yang akan menangani permintaan:

  3. Salah satu pod backend akan dipilih sebagai alamat tujuan:

  4. Permintaan mencapai perapian. Pada titik ini, koneksi TCP permanen antara dua pod akan dibuat:

  5. Setiap permintaan berikutnya dari pod pertama akan melalui koneksi yang sudah ada:



Hasilnya, Anda mendapat respons yang lebih cepat dan bandwidth yang lebih tinggi, tetapi kehilangan kemampuan untuk menskalakan backend.

Bahkan jika Anda memiliki dua pod di backend, dengan koneksi konstan, traffic akan selalu menuju ke salah satunya.

Bisakah ini diperbaiki?

Karena Kubernetes tidak tahu bagaimana menyeimbangkan koneksi yang persisten, tugas ini adalah tanggung jawab Anda.

Layanan adalah seperangkat alamat IP dan port yang disebut titik akhir.

Aplikasi Anda bisa mendapatkan daftar titik akhir dari layanan dan memutuskan bagaimana mendistribusikan permintaan di antara mereka. Anda dapat membuka koneksi persisten ke setiap perapian dan menyeimbangkan permintaan antara koneksi ini menggunakan round-robin.

Atau terapkan algoritma balancing yang lebih canggih .

Kode sisi klien yang bertanggung jawab untuk menyeimbangkan harus mengikuti logika ini:

  1. Dapatkan daftar titik akhir dari layanan.
  2. Untuk setiap titik akhir, buka koneksi persisten.
  3. Saat Anda perlu membuat permintaan, gunakan salah satu koneksi terbuka.
  4. Secara teratur perbarui daftar titik akhir, buat yang baru, atau tutup koneksi yang lama jika daftar berubah.

Ini adalah tampilannya .

  1. Alih-alih mengirim permintaan pertama ke layanan, Anda dapat menyeimbangkan permintaan di sisi klien:

  2. Anda perlu menulis kode yang menanyakan pod mana yang merupakan bagian dari layanan:

  3. Segera setelah Anda menerima daftar, simpan di sisi klien dan gunakan untuk terhubung ke pod:

  4. Anda sendiri yang bertanggung jawab atas algoritma load balancing:



Sekarang pertanyaannya adalah: apakah masalah ini hanya berlaku untuk HTTP keep-live?

Penyeimbangan beban sisi klien


HTTP bukan satu-satunya protokol yang dapat menggunakan koneksi TCP persisten.

Jika aplikasi Anda menggunakan database, maka koneksi TCP tidak terbuka setiap kali Anda perlu menjalankan permintaan atau mendapatkan dokumen dari database. 

Sebagai gantinya, koneksi TCP permanen ke database dibuka dan digunakan.

Jika basis data Anda digunakan di Kubernetes dan akses disediakan sebagai layanan, maka Anda akan menghadapi masalah yang sama seperti yang dijelaskan di bagian sebelumnya.

Satu replika basis data akan dimuat lebih dari yang lain. Kube-proxy dan Kubernetes tidak akan membantu menyeimbangkan koneksi. Anda harus menjaga keseimbangan pertanyaan ke basis data Anda.

Bergantung pada perpustakaan mana yang Anda gunakan untuk terhubung ke database, Anda mungkin memiliki berbagai opsi untuk menyelesaikan masalah ini.

Berikut ini adalah contoh mengakses cluster database MySQL dari 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

Ada banyak protokol lain yang menggunakan koneksi TCP persisten:

  • WebSockets dan WebSockets yang diamankan
  • HTTP / 2
  • gRPC
  • RSockets
  • AMQP

Anda seharusnya sudah terbiasa dengan sebagian besar protokol ini.

Tetapi jika protokol ini sangat populer, mengapa tidak ada solusi penyeimbangan standar? Mengapa perubahan dalam logika klien diperlukan? Apakah ada solusi Kubernet asli?

Kube-proxy dan iptables dirancang untuk menutup sebagian besar skenario penerapan standar untuk Kubernetes. Ini untuk kenyamanan.

Jika Anda menggunakan layanan web yang menyediakan API REST, Anda beruntung - dalam hal ini, koneksi TCP permanen tidak digunakan, Anda dapat menggunakan layanan Kubernetes apa pun.

Tetapi segera setelah Anda mulai menggunakan koneksi TCP yang persisten, Anda harus mencari tahu bagaimana mendistribusikan beban secara merata di backend. Kubernetes tidak mengandung solusi siap pakai untuk kasus ini.

Namun, tentu saja, ada opsi yang dapat membantu.

Menyeimbangkan koneksi berumur panjang di Kubernetes


Kubernetes memiliki empat jenis layanan:

  1. Clusterip
  2. NodePort
  3. Loadbalancer
  4. Tanpa kepala

Tiga layanan pertama didasarkan pada alamat IP virtual, yang digunakan oleh kube-proxy untuk membangun aturan iptables. Tetapi dasar mendasar dari semua layanan adalah layanan tipe tanpa kepala.

Tidak ada alamat IP yang dikaitkan dengan layanan tanpa kepala, dan hanya menyediakan mekanisme untuk mendapatkan daftar alamat IP dan port perapian terkait (titik akhir).

Semua layanan didasarkan pada layanan tanpa kepala.

Layanan ClusterIP adalah layanan tanpa kepala dengan beberapa tambahan: 

  1. Lapisan manajemen memberinya alamat IP.
  2. Kube-proxy membentuk aturan iptables yang diperlukan.

Dengan demikian, Anda dapat mengabaikan kube-proxy dan langsung menggunakan daftar titik akhir yang diterima dari layanan tanpa kepala untuk menyeimbangkan beban dalam aplikasi Anda.

Tetapi bagaimana cara menambahkan logika yang sama untuk semua aplikasi yang digunakan dalam sebuah cluster?

Jika aplikasi Anda sudah dikerahkan, maka tugas seperti itu mungkin tampak mustahil. Namun, ada alternatif.

Service Mesh akan membantu Anda


Anda mungkin sudah memperhatikan bahwa strategi load balancing sisi klien cukup standar.

Ketika aplikasi dimulai, itu:

  1. Mendapat daftar alamat IP dari layanan.
  2. Membuka dan memelihara kumpulan koneksi.
  3. Memperbarui kumpulan secara berkala, menambah atau menghapus titik akhir.

Segera setelah aplikasi ingin membuat permintaan, itu:

  1. Memilih koneksi yang tersedia menggunakan semacam logika (mis. Round-robin).
  2. Memenuhi permintaan.

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

Anda dapat memisahkan logika ini menjadi perpustakaan terpisah dan menggunakannya dalam aplikasi Anda.

Namun, kisi-kisi layanan seperti Istio atau Linkerd dapat digunakan sebagai gantinya.

Service Mesh melengkapi aplikasi Anda dengan proses yang:

  1. Secara otomatis mencari alamat IP layanan.
  2. Memeriksa koneksi seperti WebSockets dan gRPC.
  3. Saldo permintaan menggunakan protokol yang benar.

Service Mesh membantu mengelola lalu lintas di dalam kluster, tetapi cukup intensif sumber daya. Pilihan lain menggunakan perpustakaan pihak ketiga, seperti Netflix Ribbon, atau proksi yang dapat diprogram, seperti Utusan.

Apa yang terjadi jika Anda mengabaikan masalah keseimbangan?


Anda tidak dapat menggunakan load balancing dan tidak melihat adanya perubahan. Mari kita lihat beberapa skenario kerja.

Jika Anda memiliki lebih banyak klien daripada server, ini bukan masalah besar.

Misalkan ada lima klien yang terhubung ke dua server. Bahkan jika tidak ada penyeimbangan, kedua server akan digunakan:



Koneksi dapat didistribusikan secara tidak merata: mungkin empat klien terhubung ke server yang sama, tetapi ada kemungkinan baik kedua server akan digunakan.

Yang lebih bermasalah adalah skenario sebaliknya.

Jika Anda memiliki lebih sedikit klien dan lebih banyak server, sumber daya Anda mungkin tidak cukup digunakan dan kemacetan potensial akan muncul.

Misalkan ada dua klien dan lima server. Paling-paling, akan ada dua koneksi permanen ke dua dari lima server.

Server lain akan menganggur:



Jika kedua server ini tidak dapat menangani pemrosesan permintaan klien, penskalaan horizontal tidak akan membantu.

Kesimpulan


Layanan Kubernetes dirancang untuk bekerja di sebagian besar skenario aplikasi web standar.

Namun, segera setelah Anda mulai bekerja dengan protokol aplikasi yang menggunakan koneksi TCP persisten, seperti database, gRPC atau WebSockets, layanan tidak lagi cocok. Kubernetes tidak menyediakan mekanisme internal untuk menyeimbangkan koneksi TCP yang persisten.

Ini berarti Anda harus menulis aplikasi dengan kemungkinan penyeimbangan di sisi klien.

Terjemahan disiapkan oleh tim Kubernetes AAS dari dari Mail.ru .

Apa lagi yang harus dibaca pada topik :

  1. Tiga tingkat autoscaling di Kubernetes dan cara menggunakannya secara efektif
  2. Kubernet dalam semangat pembajakan dengan templat implementasi .
  3. Kubernetes .

All Articles