Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes
Artikel ini akan membantu Anda memahami cara kerja penyeimbangan beban di Kubernetes, apa yang terjadi saat menskalakan koneksi yang berumur panjang, dan mengapa Anda harus mempertimbangkan penyeimbangan sisi klien jika Anda menggunakan HTTP/2, gRPC, RSockets, AMQP, atau protokol berumur panjang lainnya . 

Sedikit tentang bagaimana lalu lintas didistribusikan di Kubernetes 

Kubernetes menyediakan dua abstraksi yang mudah digunakan untuk menerapkan aplikasi: Layanan dan Penerapan.

Deployment menjelaskan bagaimana dan berapa banyak salinan aplikasi Anda yang harus dijalankan pada waktu tertentu. Setiap aplikasi disebarkan sebagai Pod dan diberi alamat IP.

Layanan memiliki fungsi serupa dengan penyeimbang beban. Mereka dirancang untuk mendistribusikan lalu lintas ke beberapa pod.

Mari kita lihat seperti apa.

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  2. Penyeimbang beban disebut Layanan dan diberi alamat IP. Setiap permintaan yang masuk akan dialihkan ke salah satu pod:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  3. Skenario penerapan menentukan jumlah instance aplikasi. Anda hampir tidak perlu melakukan ekspansi langsung di bawah:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  4. Setiap pod diberi alamat IP-nya sendiri:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

Ini terlihat seperti ini.

  1. Permintaan curl 10.96.45.152 diterima ke layanan:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  3. Lalu lintas dialihkan ke pod tertentu:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

Saat frontend membuat permintaan ke backend, frontend tidak perlu mengetahui secara pasti berapa banyak pod yang dilayani backend: mungkin ada satu, sepuluh, atau seratus.

Selain itu, frontend tidak mengetahui apa pun tentang alamat pod yang melayani backend.

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

Berikut adalah cara terlihat.

  1. Di bawah 1 permintaan komponen backend internal. Daripada memilih yang spesifik untuk backend, ia membuat permintaan ke layanan:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  3. Lalu lintas berpindah dari Pod 1 ke Pod 5, dipilih oleh layanan:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  4. Under 1 tidak mengetahui secara pasti berapa banyak pod seperti under 5 yang tersembunyi di balik layanan:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Namun bagaimana tepatnya layanan mendistribusikan permintaan? Sepertinya penyeimbangan round-robin digunakan? Mari kita cari tahu. 

Menyeimbangkan layanan Kubernetes

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

Anda dapat memverifikasi ini dengan masuk ke node mana pun di cluster dan menjalankan perintah netstat -ntlp.

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

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

Aturan ini berbunyi: β€œJika kami melihat alamat IP layanan, kami 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 IP dan port tersebut.

Mari kita lihat ini

  1. Pertimbangkan sekelompok tiga node. Setiap node memiliki pod:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  2. Polong yang diikat dan dicat krem ​​​​adalah bagian dari layanan. Karena layanan tidak ada sebagai suatu proses, layanan tersebut ditampilkan dalam warna abu-abu:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  3. Pod pertama meminta layanan dan harus menuju ke salah satu pod terkait:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  4. Tapi pelayanannya tidak ada, prosesnya tidak ada. Bagaimana cara kerjanya?

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  6. Aturan iptables mengetahui bahwa layanan tersebut tidak ada dan mengganti alamat IP-nya dengan salah satu alamat IP dari pod yang terkait dengan layanan tersebut:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  7. Permintaan tersebut menerima alamat IP yang valid sebagai alamat tujuan dan diproses secara normal:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Bisakah iptables memuat keseimbangan?

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

Namun, dimungkinkan untuk menulis seperangkat aturan yang berfungsi seperti itu penyeimbang semu.

Dan inilah yang diterapkan di Kubernetes.

Jika kamu mempunyai tiga pod, kube-proxy akan menulis aturan berikut:

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

Sistem ini menghasilkan setiap pod terpilih dengan probabilitas 33%.

Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Dan tidak ada jaminan bahwa Pod 2 akan dipilih berikutnya setelah Pod 1.

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

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

Koneksi yang berumur panjang di Kubernetes tidak berskala secara default

Setiap permintaan HTTP dari frontend ke backend dilayani oleh koneksi TCP terpisah, yang dibuka dan ditutup.

Jika frontend mengirimkan 100 permintaan per detik ke backend, maka 100 koneksi TCP berbeda akan dibuka dan ditutup.

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

Protokol HTTP memiliki fitur yang disebut HTTP keep-alive, atau penggunaan kembali koneksi. Dalam hal ini, satu koneksi TCP digunakan untuk mengirim dan menerima beberapa permintaan dan respons HTTP:

Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

Penyiapannya sendiri sederhana dan dapat diakses oleh sebagian besar bahasa dan lingkungan pemrograman.

Berikut beberapa tautan ke contoh dalam berbagai bahasa:

Apa yang terjadi jika kita menggunakan keep-alive di layanan Kubernetes?
Mari kita asumsikan bahwa dukungan frontend dan backend tetap aktif.

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. Backend mengirimkan respons, dan frontend menerimanya.

Berbeda dengan situasi biasa di mana koneksi TCP ditutup setelah menerima respons, koneksi tersebut sekarang tetap terbuka untuk permintaan HTTP lebih lanjut.

Apa yang terjadi jika frontend mengirimkan lebih banyak permintaan ke backend?

Untuk meneruskan permintaan ini, koneksi TCP terbuka akan digunakan, semua permintaan akan menuju ke backend yang sama tempat permintaan pertama pergi.

Bukankah iptables seharusnya mendistribusikan ulang lalu lintas?

Tidak dalam kasus ini.

Ketika koneksi TCP dibuat, ia melewati aturan iptables, yang memilih backend tertentu ke mana lalu lintas akan menuju.

Karena semua permintaan selanjutnya berada pada koneksi TCP yang sudah terbuka, aturan iptables tidak lagi dipanggil.

Mari kita lihat seperti apa.

  1. Pod pertama mengirimkan permintaan ke layanan:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  4. Permintaan tersebut mencapai pod. Pada titik ini, koneksi TCP yang persisten antara kedua pod akan dibuat:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  5. Permintaan selanjutnya dari pod pertama akan melalui koneksi yang sudah dibuat:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Hasilnya adalah waktu respons yang lebih cepat dan throughput yang lebih tinggi, namun Anda kehilangan kemampuan untuk menskalakan backend.

Meskipun Anda memiliki dua pod di backend, dengan koneksi yang konstan, lalu lintas akan selalu menuju ke salah satunya.

Bisakah ini diperbaiki?

Karena Kubernetes tidak tahu cara menyeimbangkan koneksi persisten, tugas ini ada di tangan Anda.

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

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

Atau terapkan lebih banyak algoritma penyeimbangan yang kompleks.

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

  1. Dapatkan daftar titik akhir dari layanan.
  2. Buka koneksi persisten untuk setiap titik akhir.
  3. Saat permintaan perlu dibuat, gunakan salah satu koneksi terbuka.
  4. Perbarui daftar titik akhir secara rutin, buat yang baru, atau tutup koneksi persisten lama jika daftar berubah.

Seperti inilah tampilannya.

  1. Daripada pod pertama yang mengirimkan permintaan ke layanan, Anda dapat menyeimbangkan permintaan di sisi klien:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  3. Setelah Anda memiliki daftarnya, simpan di sisi klien dan gunakan untuk terhubung ke pod:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

  4. Anda bertanggung jawab atas algoritma penyeimbangan beban:

    Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Sekarang muncul pertanyaan: apakah masalah ini hanya berlaku untuk HTTP tetap hidup?

Penyeimbangan beban sisi klien

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

Jika aplikasi Anda menggunakan database, maka koneksi TCP tidak dibuka setiap kali Anda perlu membuat permintaan atau mengambil dokumen dari database. 

Sebaliknya, koneksi TCP persisten ke database dibuka dan digunakan.

Jika database Anda diterapkan di Kubernetes dan akses diberikan sebagai layanan, Anda akan menghadapi masalah yang sama seperti yang dijelaskan di bagian sebelumnya.

Satu replika database akan dimuat lebih banyak dibandingkan yang lain. Kube-proxy dan Kubernetes tidak akan membantu menyeimbangkan koneksi. Anda harus berhati-hati dalam menyeimbangkan kueri ke database Anda.

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

Di bawah 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
  • soket RS
  • AMQP

Anda seharusnya sudah familiar dengan sebagian besar protokol ini.

Namun jika protokol ini begitu populer, mengapa tidak ada solusi penyeimbangan yang terstandarisasi? Mengapa logika klien perlu diubah? Apakah ada solusi asli Kubernetes?

Kube-proxy dan iptables dirancang untuk mencakup kasus penggunaan paling umum saat diterapkan ke Kubernetes. Ini untuk kenyamanan.

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

Namun begitu Anda mulai menggunakan koneksi TCP persisten, Anda harus memikirkan cara mendistribusikan beban secara merata ke seluruh backend. Kubernetes tidak berisi solusi siap pakai untuk kasus ini.

Namun, tentu ada pilihan yang bisa membantu.

Menyeimbangkan koneksi jangka panjang di Kubernetes

Ada empat jenis layanan di Kubernetes:

  1. ClusterIP
  2. Pelabuhan Node
  3. Penyeimbang Beban
  4. Tanpa kepala

Tiga layanan pertama beroperasi berdasarkan alamat IP virtual, yang digunakan oleh kube-proxy untuk membuat aturan iptables. Namun dasar mendasar dari semua layanan adalah layanan tanpa kepala.

Layanan headless tidak memiliki alamat IP apa pun yang terkait dengannya dan hanya menyediakan mekanisme untuk mengambil daftar alamat IP dan port dari pod (titik akhir) yang terkait dengannya.

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 menghasilkan aturan iptables yang diperlukan.

Dengan cara ini Anda dapat mengabaikan kube-proxy dan langsung menggunakan daftar endpoint yang diperoleh dari layanan headless untuk menyeimbangkan beban aplikasi Anda.

Namun bagaimana kita bisa menambahkan logika serupa ke semua aplikasi yang diterapkan di cluster?

Jika aplikasi Anda sudah diterapkan, tugas ini mungkin tampak mustahil. Namun, ada pilihan alternatif.

Service Mesh akan membantu Anda

Anda mungkin telah memperhatikan bahwa strategi penyeimbangan beban sisi klien cukup standar.

Saat aplikasi dimulai, itu:

  1. Mendapatkan daftar alamat IP dari layanan.
  2. Membuka dan memelihara kumpulan koneksi.
  3. Memperbarui kumpulan secara berkala dengan menambahkan atau menghapus titik akhir.

Setelah aplikasi ingin membuat permintaan, aplikasi:

  1. Memilih koneksi yang tersedia menggunakan beberapa logika (misalnya round-robin).
  2. Jalankan permintaan.

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

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

Namun, Anda dapat menggunakan jerat layanan seperti Istio atau Linkerd.

Service Mesh menambah aplikasi Anda dengan proses yang:

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

Service Mesh membantu mengelola lalu lintas dalam cluster, namun cukup boros sumber daya. Pilihan lainnya adalah menggunakan perpustakaan pihak ketiga seperti Netflix Ribbon atau proxy yang dapat diprogram seperti Envoy.

Apa yang terjadi jika Anda mengabaikan masalah keseimbangan?

Anda dapat memilih untuk tidak menggunakan penyeimbangan beban dan tetap tidak melihat perubahan apa pun. Mari kita lihat beberapa skenario kerja.

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

Katakanlah ada lima klien yang terhubung ke dua server. Meskipun tidak ada penyeimbangan, kedua server akan digunakan:

Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

Koneksi mungkin tidak terdistribusi secara merata: mungkin empat klien terhubung ke server yang sama, namun ada kemungkinan besar kedua server akan digunakan.

Yang lebih bermasalah adalah skenario sebaliknya.

Jika Anda memiliki lebih sedikit klien dan lebih banyak server, sumber daya Anda mungkin kurang dimanfaatkan dan potensi hambatan akan muncul.

Katakanlah ada dua klien dan lima server. Dalam kasus terbaik, akan ada dua koneksi permanen ke dua dari lima server.

Server yang tersisa akan menganggur:

Penyeimbangan beban dan penskalaan koneksi jangka panjang di Kubernetes

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

Kesimpulan

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

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

Ini berarti Anda harus menulis aplikasi dengan mempertimbangkan keseimbangan sisi klien.

Terjemahan disiapkan oleh tim Kubernetes aaS dari Mail.ru.

Apa lagi yang harus dibaca tentang topik tersebut:

  1. Tiga tingkat penskalaan otomatis di Kubernetes dan cara menggunakannya secara efektif
  2. Kubernetes dalam semangat pembajakan dengan template untuk implementasi.
  3. Saluran Telegram kami tentang transformasi digital.

Sumber: www.habr.com

Tambah komentar