Routing dan balancing langsung dengan NFT vs Nginx

Saat mengembangkan aplikasi jaringan yang sangat dimuat, ada kebutuhan untuk load balancing.

Alat balancing L7 yang populer adalah Nginx. Ini memungkinkan Anda untuk menyimpan jawaban, memilih strategi yang berbeda, dan bahkan skrip pada LUA.

Terlepas dari semua pesona Nginx, jika:

  1. Tidak perlu bekerja dengan HTTP.
  2. Anda perlu memeras maksimal dari jaringan.
  3. Tidak perlu melakukan cache apa pun - penyeimbang memiliki server API bersih dengan dinamika.

Mungkin timbul pertanyaan: mengapa Anda membutuhkan Nginx? Mengapa menghabiskan sumber daya menyeimbangkan pada L7, bukankah lebih mudah untuk hanya meneruskan paket SYN? (L4 Direct Routing).

Penyeimbangan lapisan 4 atau cara menyeimbangkan pada zaman kuno


Alat penerusan paket yang populer adalah IPVS. Dia melakukan tugas menyeimbangkan melalui terowongan dan Direct Routing.

Dalam kasus pertama, saluran TCP dibuat untuk setiap koneksi dan paket dari pengguna pergi ke penyeimbang, lalu ke antek, dan kemudian dalam urutan terbalik.



Dalam skema ini, masalah utama terlihat: dalam arah yang berlawanan, data pertama-tama menuju penyeimbang, dan kemudian ke pengguna (Nginx bekerja dengan cara yang sama). Pekerjaan yang tidak perlu dilakukan, mengingat fakta bahwa biasanya lebih banyak data dikirim ke pengguna, perilaku ini menyebabkan beberapa kehilangan kinerja.

Kerugian seperti itu dirampas (tetapi diberkahi dengan yang baru) metode penyeimbangan yang disebut Direct Routing. Secara skematis, tampilannya seperti ini:



Dalam kasus Direct Routing, paket yang dikembalikan langsung ke pengguna, melewati penyeimbang. Jelas, baik sumber daya penyeimbang dan jaringan disimpan. Dengan menghemat sumber daya jaringan, ini tidak terlalu menghemat lalu lintas, karena praktik yang biasa adalah menghubungkan server ke kisi yang terpisah dan tidak memperhitungkan lalu lintas, tetapi kenyataan bahwa bahkan mentransfer melalui penyeimbang adalah hilangnya milidetik.

Metode ini menerapkan batasan tertentu:

  1. Pusat data di mana infrastruktur berada harus memungkinkan spoofing alamat lokal. Dalam diagram di atas, setiap antek harus mengirim kembali paket atas nama IP 10.10.0.1, yang ditugaskan ke penyeimbang.
  2. Penyeimbang tidak tahu apa-apa tentang keadaan kaki tangan. Akibatnya, strategi Least Conn dan Least Time tidak layak dilakukan. Dalam salah satu artikel berikut ini saya akan mencoba mengimplementasikannya dan menunjukkan hasilnya.

Di Sini Datang NFTables


Beberapa tahun yang lalu, Linux mulai secara aktif mempromosikan NFTables sebagai pengganti untuk IPTable, ArpTables, EBTables, dan semua orang lainnya [az] {1,} Tabel. Pada saat kami di Adram perlu memeras setiap milidetik dari jawaban dari jaringan, saya memutuskan untuk mengeluarkan checker dan mencari - atau mungkin ipTables belajar melakukan penerusan iphash dan Anda dapat mempercepatnya untuk menyeimbangkannya. Kemudian saya menemukan nftables, yang bisa dan tidak hanya itu, tetapi iptables masih tidak bisa melakukan ini.
Setelah beberapa hari percobaan, saya akhirnya bisa mendapatkan Direct Routing dan Channel Routing melalui NFTables di lab uji dan juga membandingkan mereka dibandingkan dengan nginx.

Jadi, lab uji. Kami memiliki 5 mobil:

  1. nft-router - router yang melakukan tugas menghubungkan klien dan subnet AppServer. Ini memiliki 2 kartu jaringan: 192.168.56.254 - melihat jaringan server aplikasi, 192.168.97.254 - melihat klien. Ip_forward diaktifkan dan semua rute terdaftar.
  2. nft-client: klien dari mana ab, ip 192.168.97.2 akan dikejar
  3. nft-balancer: balancer. Ini memiliki dua IP: 192.168.56.4, yang diakses oleh klien dan 192.168.13.1, dari subnet minion.
  4. nft-minion-a dan nft-minion-b: minion ipy: 192.168.56.2, 192.168.56.3 dan 192.168.13.2 dan 192.168.13.3 (saya mencoba menyeimbangkan melalui jaringan yang sama dan melalui yang berbeda). Dalam tes, saya berhenti pada fakta bahwa pelayan memiliki tipe "eksternal" - dalam subnet 192.168.56.0/24

Semua antarmuka MTU 1500.

Routing langsung


Pengaturan NFTables pada balancer:

table ip raw { chain input { type filter hook prerouting priority -300; policy accept; tcp dport http ip daddr set jhash tcp sport mod 2 map { 0: 192.168.56.2, 1: 192.168.56.3 } } } 

Rantai mentah dibuat, di hook, dengan prioritas -300.

Jika paket dengan alamat tujuan http tiba, maka tergantung pada port sumber (dilakukan untuk pengujian dari satu mesin, Anda benar-benar membutuhkan ip saddr), baik 56.2 atau 56.3 dipilih dan ditetapkan sebagai alamat tujuan dalam paket, dan kemudian dikirim lebih jauh di sepanjang rute. Secara kasar, masing-masing untuk port 56.2, untuk port ganjil, masing-masing, 56.3 (pada kenyataannya, tidak, karena untuk hash genap / ganjil, tetapi lebih mudah untuk memahami hal itu). Setelah menetapkan IP target, paket kembali ke jaringan. Tidak ada NAT terjadi, paket datang ke antek-antek dengan IP sumber klien, dan bukan penyeimbang, yang penting untuk Routing Langsung.

Pengaturan NFT Minion:

 table ip raw { chain output { type filter hook output priority -300; policy accept; tcp sport http ip saddr set 192.168.56.4 } } 

Kait keluaran mentah dibuat dengan prioritas -300 (prioritas sangat penting di sini, pada tingkat yang lebih tinggi mengling yang diperlukan tidak akan berfungsi untuk paket balasan).

Semua lalu lintas keluar dari port http ditandatangani oleh 56,4 (penyeimbang ip) dan dikirim langsung ke klien, melewati penyeimbang.

Untuk memeriksa apakah semuanya akan berfungsi dengan benar, saya membawa klien ke jaringan lain dan membiarkannya melalui router.

Saya juga menonaktifkan arp_filter, rp_filter (agar spoofing bekerja) dan mengaktifkan ip_forward baik pada balancer maupun pada router.

Untuk bangku, dalam kasus NFT, Nginx + php7.2-FPM digunakan melalui soket unix pada setiap antek. Tidak ada apa-apa di penyeimbang.

Dalam kasus Nginx, kami menggunakan: nginx pada balancer dan php7.2-FPM melalui TCP pada minion. Akibatnya, saya tidak menyeimbangkan server web di belakang balancer, tetapi langsung FPM (yang akan lebih jujur ​​dengan nginx, dan lebih konsisten dengan kehidupan nyata).

Untuk NFT, hanya strategi hash yang digunakan ( nft dr dalam tabel), untuk nginx: hash ( ngx eq ) dan paling sedikit samb ( ngx lc )

Beberapa tes telah dilakukan.

  1. Script cepat kecil (kecil) .

     <?php system('hostname'); 

  2. Script dengan penundaan acak (rand) .

     <?php usleep(mt_rand(100000,200000)); echo "ok"; 
  3. Sebuah skrip dengan pengiriman sejumlah besar data (ukuran) .

     <?php $size=$_GET['size']; $file='/tmp/'.$size; if (!file_exists($file)) { $dummy=""; exec ("dd if=/dev/urandom of=$file bs=$size count=1 2>&1",$dummy); } fpassthru (fopen($file,'rb')); 

    Ukuran berikut digunakan:
    512.1440.1460.1480.1500.2048.65535.655350 byte.
    Sebelum tes, saya memanaskan file statika pada setiap antek.

Diuji ab tiga kali setiap tes:

 #!/bin/bash function do_test() { rep=$3 for i in $(seq $rep) do echo "testing $2 # $i" echo "$2 pass $i" >> $2 ab $1 >> $2 echo "--------------------------" >> $2 done } do_test " -n 5000 -c 100 http://192.168.56.4:80/rand.php" "ngx_eq_test_rand" 3 do_test " -n 10000 -c 100 http://192.168.56.4:80/" "ngx_eq_test_small" 3 size=512 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1440 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1460 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1480 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=1500 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=2048 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=65535 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 size=655350 do_test " -n 10000 -c 100 http://192.168.56.4:80/size.php?size=$size" "ngx_eq_test_size_$size" 3 

Awalnya, saya berencana untuk membawa waktu pengujian, milidetik, dan yang lainnya, sebagai hasilnya saya memilih RPS - mereka representatif dan berkorelasi dengan indikator waktu.

Mendapat hasil sebagai berikut:

Uji ukuran - kolom - ukuran data yang diberikan.



Seperti yang Anda lihat, routing langsung nft menang dengan margin besar.

Saya mengandalkan beberapa hasil lain yang terkait dengan ukuran kerangka ethernet, tetapi tidak ada korelasi yang ditemukan. Mungkin 512 bodi tidak masuk dalam 1500 MTU, meskipun, saya ragu, tes kecil akan menjadi indikasi.

Saya perhatikan bahwa pada volume besar (650k) nginx mengurangi pemisahan. Mungkin ini ada hubungannya dengan buffer dan ukuran TCP Windows.

Hasil uji rand. Memperlihatkan bagaimana paling sedikit samb menangani dalam kondisi kecepatan eksekusi skrip yang berbeda pada antek yang berbeda.



Anehnya, hash nginx bekerja lebih cepat daripada yang paling sedikit samb, dan hanya di pass terakhir yang paling sedikit samb yang maju sedikit, yang tidak berpura-pura signifikan.
Jumlah operan sangat berbeda karena fakta bahwa 100 thread sekaligus, dan FPM-ok dari awal memuat sekitar 10. Pada pass ketiga, mereka punya waktu untuk terbiasa - yang menunjukkan penerapan strategi untuk semburan.

NFT diharapkan kehilangan tes ini. Nginx mengoptimalkan interaksi dengan FPM dengan baik dalam situasi seperti itu.

tes kecil



nft menang sedikit di RPS, paling tidak kon lagi adalah orang luar.

By the way, dalam tes ini Anda dapat melihat bahwa 400-500RPS dikeluarkan, meskipun, pada tes dengan mengirim 512 byte itu untuk 1500 - tampaknya sistem makan ribuan ini.

Kesimpulan


NFT berkinerja baik dalam situasi optimalisasi beban seragam: ketika banyak data diberikan, dan waktu operasi aplikasi ditentukan dan sumber daya kluster cukup untuk memproses aliran yang masuk tanpa masuk ke tailspin.

Dalam situasi di mana beban pada setiap permintaan kacau dan tidak mungkin untuk menyeimbangkan beban server dengan sisa primitif dari divisi hash, NFT akan hilang.

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


All Articles