SLB คือโหลดบาลานเซอร์แบบไม่มีเซสชันสำหรับการรับส่งข้อมูล UDP และแก้ไขปัญหาที่เกิดจากการใช้โหลดบาลานเซอร์แบบดั้งเดิม (มีคุณลักษณะหลากหลาย) สำหรับการรับส่งข้อมูลดังกล่าว
สำหรับโปรโตคอล UDP แบบธรรมดาและไร้สถานะ ไม่มีประโยชน์ในการพยายามรักษา "ความสัมพันธ์" (หรือที่เรียกว่า "เซสชัน") ระหว่างไคลเอนต์และอินสแตนซ์ส่วนหลัง โหลดบาลานเซอร์แบบดั้งเดิมถือว่าความสัมพันธ์นั้นมีประโยชน์ และจะพยายามกำหนดเส้นทางแพ็กเก็ตจากไคลเอนต์ไปยังเซิร์ฟเวอร์แบ็คเอนด์ที่สอดคล้องกัน ในทางตรงกันข้าม SLB จะกระจายแพ็กเก็ตเท่าๆ กัน (แบบสุ่ม) ทีละชุดบนแบ็คเอนด์ที่มีอยู่ทั้งหมด ซึ่งส่งผลให้มีการโหลดแบ็กเอนด์ที่สม่ำเสมอ และปรับปรุงประสิทธิภาพเมื่ออินสแตนซ์แบ็กเอนด์หนึ่งล้มเหลว (จะมีการสูญเสียแพ็กเก็ตเพิ่มขึ้นสำหรับไคลเอนต์ทั้งหมด แทนที่จะสูญเสียการรับส่งข้อมูลทั้งหมดสำหรับไคลเอนต์บางราย)
ตามค่าเริ่มต้น SLB จะรับฟังพอร์ต 1812
และ 1813
สำหรับแพ็กเก็ต UDP ขาเข้าและส่งต่อไปยังเป้าหมายแบ็กเอนด์แบบสุ่มที่รู้จัก พอร์ตที่รับฟังสามารถทำได้ด้วยตัวเลือก --server-port-range
ซึ่งยอมรับพอร์ตเดียว (เช่น 541
) หรือช่วงของพอร์ต (เช่น 4000-5000
)
หากต้องการให้ SLB ทราบถึงแบ็กเอนด์ จำเป็นต้องส่งแพ็กเก็ต "watchdog" (หรือที่เรียกว่า "keep survival") ไปยังพอร์ตผู้ดูแลระบบ (เพิ่มเติมด้านล่างนี้) ตามค่าเริ่มต้น พอร์ตผู้ดูแลระบบคือ 1111
แต่สามารถกำหนดค่าได้โดยใช้ตัวเลือก --admin-port
หากมีการ์ดเครือข่ายหลายตัวในระบบของคุณ คุณสามารถระบุ IP ได้โดยใช้ตัวเลือก --admin-ip
หาก IP ที่ระบุด้วย --admin-ip
อยู่ในช่วง CIDR มัลติคาสต์ ( 244.0.0.0/4
) SLB จะเข้าร่วมกลุ่มมัลติคาสต์นั้นโดยอัตโนมัติ (เพิ่มเติมด้านล่างนี้)
ตัวเลือกอื่น ๆ อธิบายไว้ในคำสั่งช่วยเหลือ:
SimplestLoadBalancer:
Sessionless UDP Load Balancer sends packets to backends without session affinity.
Usage:
SimplestLoadBalancer [options]
Options:
--server-port-range <server-port-range> Set the ports to listen to and forward to backend targets
(default "1812-1813") [default: 1812-1813]
--admin-ip <admin-ip> Set the IP to listen on for watchdog events [default is first private IP]
--admin-port <admin-port> Set the port that targets will send watchdog events [default: 1111]
--client-timeout <client-timeout> Seconds to allow before cleaning-up idle clients [default: 30]
--target-timeout <target-timeout> Seconds to allow before removing target missing watchdog events [default: 30]
--default-target-weight <default-target-weight> Weight to apply to targets when not specified [default: 100]
--unwise Allows public IP addresses for targets [default: False]
--stats-period-ms <stats-period-ms> Sets the number of milliseconds between statistics messages printed to the
console (disable: 0, max: 65535) [default: 1000]
--default-group-id <default-group-id> Sets the group ID to assign to backends that when a registration packet doesn't
include one, and when port isn't assigned a group [default: 0]
--version Show version information
-?, -h, --help Show help and usage information
แบ็กเอนด์ไม่ได้รับการกำหนดค่าที่บรรทัดคำสั่ง แต่จะมีการลงทะเบียนและยกเลิกการลงทะเบียนแบบไดนามิกโดยใช้แพ็กเก็ต UDP เป็นระยะที่ส่งไปยังพอร์ตผู้ดูแลระบบ ( --admin-port
) เนื้อหาของแพ็กเก็ตเหล่านั้นอาจแตกต่างกันไปตามวิธีที่คุณใช้ SLB ในสภาพแวดล้อมของคุณ
หากคุณใช้เซิร์ฟเวอร์ SLB เดียว คุณสามารถกำหนดค่าแบ็กเอนด์ให้ส่งแพ็กเก็ตไปยัง IP นั้นและบนพอร์ตผู้ดูแลระบบได้ นี่เป็นสถานการณ์ที่ง่ายที่สุด แต่ละแบ็กเอนด์จะส่งข้อความที่มี "ไบต์วิเศษ" สองตัวเพื่อระบุ "การลงทะเบียนแบ็กเอนด์" สำหรับเนื้อหา:
0x11 0x11
SLB จะตีความแพ็คเก็ตดังกล่าวว่า "ลงทะเบียนผู้ส่งเป็นแบ็กเอนด์" ทางเลือก ข้อความอาจมีไบต์เพิ่มเติมหนึ่งหรือสองไบต์ (น้ำหนักและรหัสกลุ่ม) ซึ่งจะกล่าวถึงวัตถุประสงค์โดยละเอียดด้านล่าง
0x11 0x11 [X] [X]
^ ^
| |
| one byte for group id
|
one byte for weight
ในบางสภาพแวดล้อม แพ็กเก็ตการลงทะเบียนจะไม่ถูกส่งจากแบ็กเอนด์เอง และ SLB รองรับกรณีการใช้งานดังกล่าว เมื่อแพ็กเก็ตการลงทะเบียนถูกส่งจาก "บุคคลที่สาม" เนื้อหาจะต้องมีที่อยู่ IP ของแบ็กเอนด์ที่กำลังลงทะเบียน:
0x11 0x11 X X X X [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
อีกครั้ง ไบต์น้ำหนักและรหัสกลุ่มอาจต่อท้ายหรือไม่ก็ได้
เมื่อจำเป็นต้องมีการปรับใช้ HA ที่แข็งแกร่งยิ่งขึ้นด้วย SLB หลายตัว การสื่อสารระหว่างแบ็กเอนด์และ SLB สามารถทำให้ง่ายขึ้นได้โดยใช้ IP กลุ่มแบบหลายผู้รับ สิ่งนี้มีประโยชน์เนื่องจากแต่ละ SLB จะต้องทราบถึงแต่ละแบ็กเอนด์ ในกรณีเช่นนี้ เซิร์ฟเวอร์ SLB ควรใช้ตัวเลือก --admin-ip
เพื่อระบุที่อยู่แบบหลายผู้รับ ซึ่งจะทำให้ SLB เข้าร่วมกลุ่มหลายผู้รับ และด้วยเหตุนี้ทั้งหมดจึงได้รับข้อความใด ๆ ที่ส่งไปยัง IP นั้น แบ็กเอนด์สามารถกำหนดค่าได้ด้วย IP เดียวนั้น ซึ่งช่วยลดภาระงานและทำให้การกำหนดค่าง่ายขึ้น (โดยเฉพาะอย่างยิ่งเมื่อ SLB ถูกหมุนเวียนเข้าและออกจากบริการเนื่องจากการปรับขนาดอัตโนมัติและ/หรือการใช้อินสแตนซ์สปอต)
โปรดทราบว่าการใช้ IP แบบมัลติคาสต์ต้องใช้สวิตช์ที่รองรับมัลติคาสต์ หรือ (มีแนวโน้มมากกว่า) ที่ทำงานอยู่ใน AWS VPC ที่กำหนดค่าด้วยโดเมนมัลติคาสต์
รูปแบบแพ็กเก็ตของผู้ดูแลระบบนั้นง่ายมากในเวอร์ชัน 2.0 ในกรณีการใช้งาน SLB เดี่ยวที่ง่ายที่สุด แพ็กเก็ตการลงทะเบียนจากแบ็กเอนด์อาจประกอบด้วยเมจิกไบต์ไม่เกิน 2 ไบต์ ( 0x11
0x11
) อีกทางหนึ่ง แพ็กเก็ตอาจมาจากแหล่งอื่น (เช่น เซิร์ฟเวอร์การจัดการ) และมีไบต์สี่ไบต์เพื่อระบุที่อยู่ ipv4 ของแบ็กเอนด์ ไม่ว่าในกรณีใด อาจมีการผนวกไบต์เพิ่มเติมอีกสองไบต์สำหรับ "น้ำหนัก" การรับส่งข้อมูลที่สัมพันธ์กับแบ็กเอนด์อื่นๆ และสำหรับ "กลุ่ม" ที่จะกำหนดให้กับแบ็กเอนด์อาจถูกต่อท้าย (ข้อมูลเพิ่มเติมเกี่ยวกับกลุ่มด้านล่าง) ในศิลปะ ASCII:
0x11 0x11 [X X X X] [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
หากต้องการลบเป้าหมายออกทันที ให้ส่งแพ็กเก็ตที่มี 0x86
เป็นไบต์แรกแทน 0x11
(หากส่งจากเซิร์ฟเวอร์การจัดการ ให้เพิ่ม IP ของแบ็กเอนด์ต่อท้ายเพื่อลบ):
0x86 0x11 [X X X X]
^
|
four bytes for ip to remove
น้ำหนักใช้เพื่อควบคุมปริมาณการรับส่งข้อมูลที่ส่งไปยังแต่ละแบ็กเอนด์ หากไม่มีการระบุน้ำหนัก ค่าเริ่มต้นคือ 100 (กำหนดค่าด้วย --default-target-weight
) จะถูกนำไปใช้กับแบ็กเอนด์ และแต่ละรายการจะได้รับแพ็กเก็ตในปริมาณเท่ากัน ตามที่กล่าวไว้ เป็นที่คาดหวัง (และแนะนำให้เลือก) ที่แบ็กเอนด์จะปรับค่าน้ำหนักในแพ็คเก็ตผู้ดูแลระบบตามความสามารถในการจัดการการรับส่งข้อมูล (อาจลดลงเมื่อ CPU สูง กำลังใช้การอัปเดต เป็นต้น) ตัวอย่างเช่น:
100
, 50
และ 50
ตามลำดับ แบ็กเอนด์แรกจะได้รับ 50% ของการเข้าชม และแบ็กเอนด์ที่สองและสามจะได้รับ 25% ตามลำดับ31
และ 31
ตามลำดับ แต่ละแบ็กเอนด์จะได้รับ 50% ของการรับส่งข้อมูลเมื่อใช้กลุ่ม น้ำหนักสัมพัทธ์จะถูกประเมินเทียบกับแบ็กเอนด์อื่นๆ ในกลุ่มเดียวกัน (ไม่ใช่ทุกกลุ่ม)
สิ่งสำคัญคือต้องส่งแพ็กเก็ตของผู้ดูแลระบบอย่างน่าเชื่อถือและในจังหวะที่เพียงพอ แต่ละครั้งที่ SLB ได้รับแพ็กเก็ต เวลา "เห็นล่าสุด" ของแบ็กเอนด์จะได้รับการอัปเดต หากผ่านไป 30 วินาที (กำหนดค่าด้วย
--target-timeout
) โดยไม่มองเห็นแบ็กเอนด์ จะถูกลบออก และจะไม่มีการส่งการรับส่งข้อมูลเพิ่มเติม
ตามค่าเริ่มต้น แบ็กเอนด์ทั้งหมดจะถูกใช้เพื่อให้บริการพอร์ตทั้งหมดที่ให้บริการโดยโหลดบาลานเซอร์
อย่างไรก็ตาม คุณสามารถกำหนดพอร์ตแต่ละพอร์ตให้กับชุดย่อยของแบ็กเอนด์ได้โดยใช้ข้อความการกำหนดพอร์ต SLB และระบุ ID กลุ่มในข้อความการลงทะเบียน ตัวอย่างเช่น พิจารณาว่าคุณต้องการให้มีการรับส่งข้อมูลสมดุลโหลด SLB สำหรับพอร์ต 1812-1813 แต่กำหนดการรับส่งข้อมูลที่เข้าถึงแต่ละพอร์ตให้กับชุดเซิร์ฟเวอร์ที่แตกต่างกัน โดยทำดังนี้:
x66 x11
) พร้อมหมายเลขพอร์ต (สองไบต์) และ ID กลุ่ม (หนึ่งไบต์) ข้อความเหล่านี้ไม่จำเป็นต้องทำซ้ำ และสามารถส่งได้เมื่อต้องการเปลี่ยนแปลงการกำหนดกลุ่มพอร์ต (อย่างไรก็ตาม การทำซ้ำจะไม่เสียหาย ซึ่งสะดวกเพื่อให้แน่ใจว่าพอร์ตได้รับการกำหนดกลุ่มอย่างถูกต้องหลังจากเริ่มบริการใหม่) 0x66 0x11 X X X
^ ^
| |
| one byte for group ID
|
two bytes for port number, litten endian
การใช้ Linux bash
การส่งแพ็กเก็ตผู้ดูแลระบบนั้นตรงไปตรงมา ซึ่งสามารถทำได้โดยใช้คำสั่ง netcat
(aka. nc
) หรือระบบไฟล์ /dev/udp
ตัวอย่างเช่น หากโหลดบาลานเซอร์ของคุณกำลังรับฟังพอร์ตผู้ดูแลระบบเริ่มต้น 1111
และคุณต้องการเพิ่มเป้าหมายด้วย IP 192.168.1.22
:
$ echo -e $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.0.0.1/1111
เนื่องจากอาจเป็นเรื่องที่น่าเบื่อที่จะต้องส่งแพ็กเก็ตเหล่านั้นด้วยตนเองเพื่อเก็บชุดเป้าหมายที่ลงทะเบียนไว้ คุณอาจสร้างเชลล์สคริปต์ขนาดเล็ก เช่น lb.sh
:
#! /bin/bash
echo -ne $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.23 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.24 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.25 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.26 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.27 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
จากนั้นใช้คำสั่ง watch
เพื่อเรียกสคริปต์นั้นทุกๆ สองสามวินาที:
$ watch -n10 ./lb.sh
ไบนารีที่สร้างไว้ล่วงหน้าสำหรับ Linux และ Windows x64 และ Linux ARM พร้อมใช้งานในรูปแบบ "เผยแพร่" ของ GitHub นี่เป็นโปรเจ็กต์ .Net 8.0 ที่ง่ายมาก ดังนั้นในการสร้างมันให้รัน (สมมติว่าคุณติดตั้ง dotnet-sdk-8.0 แล้ว):
dotnet build
คุณอาจต้องการสร้างไฟล์ปฏิบัติการไบนารีดั้งเดิม ซึ่งสะดวกและให้ประโยชน์ด้านประสิทธิภาพบางประการ
สำหรับลินุกซ์:
dotnet publish -o ./ -c Release -r linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
สำหรับวินโดวส์:
dotnet publish -o ./ -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
ในทำนองเดียวกัน การรันโดยใช้ dotnet run
ในไดเร็กทอรีโปรเจ็กต์ก็ทำได้ง่ายเช่นกัน:
$ dotnet run
หรือหากคุณได้สร้างไฟล์ปฏิบัติการแบบเนทีฟ:
$ ./SimplestLoadBalancer
โปรดอย่าลังเลที่จะสร้างปัญหาที่นี่บน GitHub สำหรับคำถามและรายงานข้อผิดพลาด ไม่มีเทมเพลตหรือข้อกำหนดให้มา แต่โปรดพยายามอธิบายให้มากที่สุดเท่าที่จะเป็นไปได้ ซึ่งจะช่วยให้แน่ใจว่าเราจะสามารถตอบสนองได้อย่างสมเหตุสมผล ดูเพิ่มเติมที่
สนุก!