การทำให้โหลดบาลานซ์ NGINX ง่ายขึ้นด้วย Loadcat
เผยแพร่แล้ว: 2022-03-11เว็บแอปพลิเคชันที่ออกแบบให้ปรับขนาดได้ในแนวนอนมักต้องการโหนดสมดุลโหลดตั้งแต่หนึ่งโหนดขึ้นไป วัตถุประสงค์หลักของพวกเขาคือเพื่อกระจายการรับส่งข้อมูลขาเข้าผ่านเว็บเซิร์ฟเวอร์ที่มีอยู่อย่างยุติธรรม ความสามารถในการเพิ่มความจุโดยรวมของเว็บแอปพลิเคชันโดยการเพิ่มจำนวนโหนดและการปรับโหลดบาลานเซอร์ให้เข้ากับการเปลี่ยนแปลงนี้สามารถพิสูจน์ได้ว่ามีประโยชน์อย่างมากในการผลิต
NGINX เป็นเว็บเซิร์ฟเวอร์ที่นำเสนอฟีเจอร์โหลดบาลานซ์ประสิทธิภาพสูง ท่ามกลางความสามารถอื่นๆ มากมาย คุณลักษณะเหล่านี้บางส่วนมีให้ใช้งานโดยเป็นส่วนหนึ่งของรูปแบบการสมัครใช้บริการเท่านั้น แต่เวอร์ชันโอเพ่นซอร์สฟรีและยังมีคุณลักษณะมากมายและมาพร้อมกับคุณลักษณะการโหลดบาลานซ์ที่สำคัญที่สุดที่แกะกล่อง
ในบทช่วยสอนนี้ เราจะสำรวจกลไกภายในของเครื่องมือทดลองที่ให้คุณกำหนดค่าอินสแตนซ์ NGINX ของคุณได้ทันทีเพื่อทำหน้าที่เป็นโหลดบาลานเซอร์ โดยแยกรายละเอียดที่สำคัญทั้งหมดของไฟล์การกำหนดค่า NGINX ออกโดยจัดเตรียมเว็บ ส่วนต่อประสานผู้ใช้ตาม บทความนี้มีวัตถุประสงค์เพื่อแสดงให้เห็นว่าการเริ่มต้นสร้างเครื่องมือดังกล่าวทำได้ง่ายเพียงใด เป็นมูลค่าการกล่าวขวัญว่าโครงการ Loadcat ได้รับแรงบันดาลใจอย่างมากจาก NodeBalancers ของ Linode
NGINX เซิร์ฟเวอร์และอัปสตรีม
การใช้งานที่ได้รับความนิยมมากที่สุดอย่างหนึ่งของ NGINX คือคำขอพร็อกซี่ย้อนกลับจากไคลเอนต์ไปยังแอปพลิเคชันเว็บเซิร์ฟเวอร์ แม้ว่าเว็บแอปพลิเคชันที่พัฒนาด้วยภาษาโปรแกรมอย่าง Node.js และ Go จะเป็นเว็บเซิร์ฟเวอร์แบบพอเพียง แต่การมี reverse-proxy หน้าแอปพลิเคชันเซิร์ฟเวอร์จริงมีประโยชน์มากมาย บล็อก "เซิร์ฟเวอร์" สำหรับกรณีการใช้งานทั่วไปเช่นนี้ในไฟล์การกำหนดค่า NGINX อาจมีลักษณะดังนี้:
server { listen 80; server_name example.com; location / { proxy_pass http://192.168.0.51:5000; } }
สิ่งนี้จะทำให้ NGINX ฟังบนพอร์ต 80 สำหรับคำขอทั้งหมดที่ชี้ไปที่ example.com และส่งแต่ละคำขอไปยังแอปพลิเคชันเว็บเซิร์ฟเวอร์ที่ทำงานที่ 192.168.0.51:5000 เรายังสามารถใช้ที่อยู่ IP ลูปแบ็ค 127.0.0.1 ที่นี่ หากเว็บแอปพลิเคชันเซิร์ฟเวอร์ทำงานอยู่ในเครื่อง โปรดทราบว่าตัวอย่างด้านบนไม่มีการปรับแต่งที่ชัดเจนซึ่งมักใช้ในการกำหนดค่าพร็อกซีย้อนกลับ แต่ถูกเก็บไว้ในลักษณะนี้เพื่อความกระชับ
แต่ถ้าเราต้องการสร้างสมดุลให้กับคำขอที่เข้ามาทั้งหมดระหว่างสองอินสแตนซ์ของเว็บแอปพลิเคชันเซิร์ฟเวอร์เดียวกัน นี่คือจุดที่คำสั่ง "ต้นน้ำ" มีประโยชน์ ใน NGINX ด้วยคำสั่ง "อัพสตรีม" เป็นไปได้ที่จะกำหนดโหนดแบ็คเอนด์หลายโหนด ซึ่ง NGINX จะสร้างสมดุลให้กับคำขอที่เข้ามาทั้งหมด ตัวอย่างเช่น:
upstream nodes { server 192.168.0.51:5000; server 192.168.0.52:5000; } server { listen 80; server_name example.com; location / { proxy_pass http://nodes; } }
สังเกตว่าเรากำหนดบล็อก "อัปสตรีม" ชื่อ "โหนด" อย่างไร ซึ่งประกอบด้วยเซิร์ฟเวอร์สองเครื่อง แต่ละเซิร์ฟเวอร์จะถูกระบุโดยที่อยู่ IP และหมายเลขพอร์ตที่พวกเขารับฟัง ด้วยเหตุนี้ NGINX จึงกลายเป็นโหลดบาลานเซอร์ในรูปแบบที่ง่ายที่สุด โดยค่าเริ่มต้น NGINX จะแจกจ่ายคำขอที่เข้ามาในลักษณะแบบวนซ้ำ โดยที่คำขอแรกจะถูกส่งไปยังเซิร์ฟเวอร์แรก คำขอที่สองไปยังเซิร์ฟเวอร์ที่สอง คำขอที่สามไปยังเซิร์ฟเวอร์แรก และอื่นๆ
อย่างไรก็ตาม NGINX มีอะไรอีกมากมายที่จะนำเสนอเมื่อพูดถึงการทำโหลดบาลานซ์ ช่วยให้คุณสามารถกำหนดน้ำหนักสำหรับแต่ละเซิร์ฟเวอร์ ทำเครื่องหมายว่าใช้งานไม่ได้ชั่วคราว เลือกอัลกอริธึมการปรับสมดุลที่แตกต่างกัน (เช่น มีอัลกอริทึมที่ทำงานตามแฮช IP ของไคลเอ็นต์) เป็นต้น คุณลักษณะและคำสั่งการกำหนดค่าเหล่านี้ได้รับการบันทึกไว้อย่างดีที่ nginx.org . นอกจากนี้ NGINX ยังอนุญาตให้เปลี่ยนและโหลดไฟล์การกำหนดค่าได้ทันทีโดยแทบไม่มีการหยุดชะงัก
ความสามารถในการกำหนดค่าของ NGINX และไฟล์การกำหนดค่าที่เรียบง่ายทำให้ง่ายต่อการปรับให้เข้ากับความต้องการมากมาย และมีบทช่วยสอนมากมายบนอินเทอร์เน็ตที่สอนวิธีกำหนดค่า NGINX ให้เป็นโหลดบาลานเซอร์
Loadcat: เครื่องมือกำหนดค่า NGINX
มีบางอย่างที่น่าสนใจเกี่ยวกับโปรแกรมที่แทนที่จะทำบางอย่างด้วยตัวเอง ให้กำหนดค่าเครื่องมืออื่นๆ ให้ทำเพื่อพวกเขา พวกเขาไม่ได้ทำอะไรมากไปกว่าการป้อนข้อมูลของผู้ใช้และสร้างไฟล์สองสามไฟล์ ประโยชน์ส่วนใหญ่ที่คุณได้รับจากเครื่องมือเหล่านั้นคือคุณสมบัติของเครื่องมืออื่นๆ แต่พวกเขาทำให้ชีวิตง่ายขึ้นอย่างแน่นอน ขณะพยายามตั้งค่าตัวโหลดบาลานซ์สำหรับหนึ่งในโปรเจ็กต์ของฉันเอง ฉันสงสัยว่า: ทำไมไม่ลองทำอะไรที่คล้ายคลึงกันสำหรับ NGINX และความสามารถในการโหลดบาลานซ์ของมันล่ะ
Loadcat เกิดแล้ว!
Loadcat ซึ่งสร้างด้วย Go ยังอยู่ในช่วงเริ่มต้น ในขณะนี้ เครื่องมือนี้อนุญาตให้คุณกำหนดค่า NGINX สำหรับการโหลดบาลานซ์และการยกเลิก SSL เท่านั้น มันมี GUI บนเว็บที่เรียบง่ายสำหรับผู้ใช้ แทนที่จะเดินผ่านคุณลักษณะแต่ละอย่างของเครื่องมือ ให้เราดูที่สิ่งที่อยู่ด้านล่าง พึงระวังว่า หากมีคนสนุกกับการทำงานกับไฟล์การกำหนดค่า NGINX ด้วยมือ พวกเขาอาจพบว่าเครื่องมือดังกล่าวมีค่าเพียงเล็กน้อย
มีเหตุผลบางประการที่อยู่เบื้องหลังการเลือก Go เป็นภาษาการเขียนโปรแกรมสำหรับสิ่งนี้ หนึ่งในนั้นคือ Go สร้างไบนารีที่คอมไพล์แล้ว ซึ่งช่วยให้เราสามารถสร้างและแจกจ่ายหรือปรับใช้ Loadcat เป็นไบนารีที่คอมไพล์แล้วไปยังเซิร์ฟเวอร์ระยะไกลโดยไม่ต้องกังวลเกี่ยวกับการแก้ไขการขึ้นต่อกัน สิ่งที่ทำให้ขั้นตอนการตั้งค่าง่ายขึ้นมาก แน่นอน ไบนารีถือว่า NGINX ได้รับการติดตั้งแล้วและมีไฟล์ systemd unit อยู่แล้ว
ในกรณีที่คุณไม่ใช่วิศวกร Go ไม่ต้องกังวลเลย Go นั้นค่อนข้างง่ายและสนุกในการเริ่มต้น ยิ่งไปกว่านั้น การใช้งานนั้นตรงไปตรงมามาก และคุณควรจะสามารถปฏิบัติตามได้อย่างง่ายดาย
โครงสร้าง
Go build tools กำหนดข้อจำกัดบางประการเกี่ยวกับวิธีจัดโครงสร้างแอปพลิเคชันของคุณและปล่อยให้ส่วนที่เหลือเป็นหน้าที่ของนักพัฒนาซอฟต์แวร์ ในกรณีของเรา เราได้แบ่งสิ่งต่าง ๆ ออกเป็นแพ็คเกจ Go สองสามแพ็คเกจตามวัตถุประสงค์:
- cfg: โหลด แยกวิเคราะห์ และจัดเตรียมค่าการกำหนดค่า
- cmd/loadcat: แพ็คเกจหลัก มีจุดเริ่มต้น คอมไพล์เป็นไบนารี
- ข้อมูล: มี "รุ่น" ใช้ที่เก็บคีย์/ค่าที่ฝังไว้เพื่อความคงอยู่
- feline: มีฟังก์ชันหลัก เช่น การสร้างไฟล์การกำหนดค่า กลไกการโหลดซ้ำ ฯลฯ
- ui: มีเทมเพลต ตัวจัดการ URL ฯลฯ
หากเราพิจารณาโครงสร้างแพ็คเกจอย่างละเอียดยิ่งขึ้น โดยเฉพาะอย่างยิ่งภายในแพ็คเกจของแมว เราจะสังเกตเห็นว่ารหัสเฉพาะของ NGINX ทั้งหมดถูกเก็บไว้ในแพ็คเกจย่อย feline/nginx สิ่งนี้ทำขึ้นเพื่อให้เราสามารถรักษาตรรกะของแอปพลิเคชันที่เหลือ และเพิ่มการรองรับสำหรับโหลดบาลานเซอร์อื่นๆ (เช่น HAProxy) ได้ในอนาคต
จุดเริ่มต้น
ให้เราเริ่มจากแพ็คเกจหลักสำหรับ Loadcat ซึ่งอยู่ใน “cmd/loadcatd” หน้าที่หลัก จุดเริ่มต้นของแอปพลิเคชัน ทำสามสิ่ง
func main() { fconfig := flag.String("config", "loadcat.conf", "") flag.Parse() cfg.LoadFile(*fconfig) feline.SetBase(filepath.Join(cfg.Current.Core.Dir, "out")) data.OpenDB(filepath.Join(cfg.Current.Core.Dir, "loadcat.db")) defer data.DB.Close() data.InitDB() http.Handle("/api", api.Router) http.Handle("/", ui.Router) go http.ListenAndServe(cfg.Current.Core.Address, nil) // Wait for an “interrupt“ signal (Ctrl+C in most terminals) }
เพื่อให้ทุกอย่างเรียบง่ายและทำให้อ่านโค้ดได้ง่ายขึ้น รหัสการจัดการข้อผิดพลาดทั้งหมดจึงถูกลบออกจากตัวอย่างด้านบน (และจากตัวอย่างในบทความนี้ด้วย)
ดังที่คุณทราบได้จากโค้ด เรากำลังโหลดไฟล์การกำหนดค่าตามแฟล็กบรรทัดคำสั่ง "-config" (ซึ่งมีค่าเริ่มต้นเป็น "loadcat.conf" ในไดเร็กทอรีปัจจุบัน) ต่อไป เรากำลังเริ่มต้นส่วนประกอบสองสามอย่าง ได้แก่ แพ็คเกจหลักของแมวและฐานข้อมูล สุดท้ายนี้ เรากำลังเริ่มต้นเว็บเซิร์ฟเวอร์สำหรับ GUI บนเว็บ
การกำหนดค่า
การโหลดและแยกวิเคราะห์ไฟล์การกำหนดค่าน่าจะเป็นส่วนที่ง่ายที่สุดที่นี่ เรากำลังใช้ TOML เพื่อเข้ารหัสข้อมูลการกำหนดค่า มีแพ็คเกจการแยกวิเคราะห์ TOML ที่เรียบร้อยสำหรับ Go เราต้องการข้อมูลการกำหนดค่าเพียงเล็กน้อยจากผู้ใช้ และในกรณีส่วนใหญ่ เราสามารถกำหนดค่าเริ่มต้นที่เหมาะสมสำหรับค่าเหล่านี้ได้ struct ต่อไปนี้แสดงถึงโครงสร้างของไฟล์คอนฟิกูเรชัน:
struct { Core struct { Address string Dir string Driver string } Nginx struct { Mode string Systemd struct { Service string } } }
และนี่คือสิ่งที่ไฟล์ “loadcat.conf” ทั่วไปอาจมีลักษณะดังนี้:
[core] address=":26590" dir="/var/lib/loadcat" driver="nginx" [nginx] mode="systemd" [nginx.systemd] service="nginx.service"
ดังที่เราเห็น มีความคล้ายคลึงกันระหว่างโครงสร้างของไฟล์การกำหนดค่าที่เข้ารหัส TOML กับโครงสร้างที่แสดงด้านบน แพ็คเกจการกำหนดค่าเริ่มต้นด้วยการตั้งค่าเริ่มต้นที่สมเหตุสมผลสำหรับบางฟิลด์ของ struct แล้วแยกวิเคราะห์ไฟล์การกำหนดค่า ในกรณีที่ไม่พบไฟล์การกำหนดค่าตามเส้นทางที่ระบุ ระบบจะสร้างไฟล์ขึ้นมาหนึ่งไฟล์และทิ้งค่าเริ่มต้นไว้ในไฟล์ก่อน
func LoadFile(name string) error { f, _ := os.Open(name) if os.IsNotExist(err) { f, _ = os.Create(name) toml.NewEncoder(f).Encode(Current) f.Close() return nil } toml.NewDecoder(f).Decode(&Current) return nil }
ข้อมูลและความคงอยู่
พบกับโบลท์ ที่เก็บคีย์/ค่าที่ฝังไว้ซึ่งเขียนด้วย Go ล้วนๆ มันมาในรูปแบบแพ็คเกจที่มี API ที่ง่ายมาก รองรับการทำธุรกรรมตั้งแต่แกะกล่อง และรวดเร็วจน น่ารำคาญ
ภายในข้อมูลแพ็คเกจ เรามี โครงสร้าง ที่แสดงถึงเอนทิตีแต่ละประเภท ตัวอย่างเช่น เรามี:
type Balancer struct { Id bson.ObjectId Label string Settings BalancerSettings } type Server struct { Id bson.ObjectId BalancerId bson.ObjectId Label string Settings ServerSettings }
… โดยที่อินสแตนซ์ของ Balancer แสดงถึงตัวโหลดบาลานซ์เดี่ยว Loadcat ช่วยให้คุณสร้างสมดุลของคำขอสำหรับเว็บแอปพลิเคชันหลายตัวผ่านอินสแตนซ์เดียวของ NGINX บาลานเซอร์ทุกคนสามารถมีเซิร์ฟเวอร์หนึ่งหรือหลายเซิร์ฟเวอร์อยู่เบื้องหลัง โดยที่แต่ละเซิร์ฟเวอร์สามารถเป็นโหนดแบ็คเอนด์ที่แยกจากกัน
เนื่องจาก Bolt เป็นที่เก็บคีย์-ค่า และไม่รองรับการสืบค้นฐานข้อมูลขั้นสูง เราจึงมีตรรกะด้านแอปพลิเคชันที่ทำสิ่งนี้ให้เรา Loadcat ไม่ได้มีไว้สำหรับกำหนดค่าบาลานเซอร์หลายพันเครื่องด้วยเซิร์ฟเวอร์หลายพันเครื่องในแต่ละเซิร์ฟเวอร์ ดังนั้นแนวทางที่ไร้เดียงสานี้ก็ใช้ได้ดี นอกจากนี้ Bolt ยังทำงานร่วมกับคีย์และค่าต่างๆ ที่เป็นแบบไบต์สไลซ์ นั่นคือเหตุผลที่เราเข้ารหัส BSON โครงสร้าง ก่อนที่จะจัดเก็บไว้ใน Bolt การใช้งานฟังก์ชันที่ดึงรายการ โครงสร้าง Balancer จากฐานข้อมูลแสดงอยู่ด้านล่าง:
func ListBalancers() ([]Balancer, error) { bals := []Balancer{} DB.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("balancers")) c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { bal := Balancer{} bson.Unmarshal(v, &bal) bals = append(bals, bal) } return nil }) return bals, nil }
ฟังก์ชัน ListBalancers เริ่มต้นธุรกรรมแบบอ่านอย่างเดียว วนซ้ำคีย์และค่าทั้งหมดภายในบัคเก็ต "บาลานเซอร์" ถอดรหัสแต่ละค่าเป็นอินสแตนซ์ของโครงสร้าง Balancer และส่งคืนในอาร์เรย์

การจัดเก็บบาลานเซอร์ในถังนั้นง่ายพอๆ กัน:
func (l *Balancer) Put() error { if !l.Id.Valid() { l.Id = bson.NewObjectId() } if l.Label == "" { l.Label = "Unlabelled" } if l.Settings.Protocol == "https" { // Parse certificate details } else { // Clear fields relevant to HTTPS only, such as SSL options and certificate details } return DB.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("balancers")) p, err := bson.Marshal(l) if err != nil { return err } return b.Put([]byte(l.Id.Hex()), p) }) }
ฟังก์ชัน Put จะกำหนดค่าเริ่มต้นให้กับบางฟิลด์ แยกวิเคราะห์ใบรับรอง SSL ที่แนบมาในการตั้งค่า HTTPS เริ่มธุรกรรม เข้ารหัสอินสแตนซ์ struct และจัดเก็บไว้ในบัคเก็ตโดยเทียบกับ ID ของบาลานเซอร์
ขณะแยกวิเคราะห์ใบรับรอง SSL ข้อมูลสองส่วนจะถูกดึงออกมาโดยใช้การเข้ารหัสแพ็คเกจมาตรฐาน/pem และจัดเก็บไว้ใน SSLOptions ใต้ฟิลด์ การตั้งค่า : ชื่อ DNS และลายนิ้วมือ
เรายังมีฟังก์ชันที่ค้นหาเซิร์ฟเวอร์ตามบาลานเซอร์:
func ListServersByBalancer(bal *Balancer) ([]Server, error) { srvs := []Server{} DB.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("servers")) c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { srv := Server{} bson.Unmarshal(v, &srv) if srv.BalancerId.Hex() != bal.Id.Hex() { continue } srvs = append(srvs, srv) } return nil }) return srvs, nil }
ฟังก์ชันนี้แสดงให้เห็นว่าแนวทางของเราไร้เดียงสาจริงๆ ในที่นี้ เรากำลังอ่านบัคเก็ต "เซิร์ฟเวอร์" ทั้งหมดอย่างมีประสิทธิภาพและกรองเอนทิตีที่ไม่เกี่ยวข้องออกก่อนที่จะส่งคืนอาร์เรย์ แต่แล้วอีกครั้ง วิธีนี้ใช้ได้ดี และไม่มีเหตุผลที่แท้จริงที่จะเปลี่ยนแปลง
ฟังก์ชัน Put สำหรับเซิร์ฟเวอร์นั้นง่ายกว่าโครงสร้าง Balancer มาก เนื่องจากไม่ต้องการบรรทัดค่าเริ่มต้นของการตั้งค่าโค้ดหลายบรรทัดและฟิลด์ที่คำนวณ
การควบคุม NGINX
ก่อนใช้ Loadcat เราต้องกำหนดค่า NGINX เพื่อโหลดไฟล์การกำหนดค่าที่สร้างขึ้น Loadcat สร้างไฟล์ “nginx.conf” สำหรับบาลานเซอร์แต่ละตัวภายใต้ไดเร็กทอรีตาม ID ของบาลานเซอร์ (สตริงฐานสิบหกแบบสั้น) ไดเร็กทอรีเหล่านี้ถูกสร้างขึ้นภายใต้ไดเร็กทอรี "out" ที่ cwd
ดังนั้นจึงเป็นสิ่งสำคัญที่คุณจะต้องกำหนดค่า NGINX เพื่อโหลดไฟล์การกำหนดค่าที่สร้างขึ้นเหล่านี้ ซึ่งสามารถทำได้โดยใช้คำสั่ง "รวม" ภายในบล็อก "http":
แก้ไข /etc/nginx/nginx.conf และเพิ่มบรรทัดต่อไปนี้ที่ส่วนท้ายของบล็อก "http":
http { include /path/to/out/*/nginx.conf; }
การทำเช่นนี้จะทำให้ NGINX สแกนไดเร็กทอรีทั้งหมดที่พบใน "/path/to/out/" ค้นหาไฟล์ชื่อ "nginx.conf" ภายในแต่ละไดเร็กทอรี และโหลดแต่ละไฟล์ที่พบ
ในแพ็คเกจหลักของเรา feline เรากำหนดอินเทอร์เฟซ Driver โครงสร้างใด ๆ ที่มีสองฟังก์ชันคือ Generate และ Reload ด้วยลายเซ็นที่ถูกต้องจะถือเป็นไดรเวอร์
type Driver interface { Generate(string, *data.Balancer) error Reload() error }
ตัวอย่างเช่น โครงสร้าง Nginx ภายใต้แพ็คเกจ feline/nginx:
type Nginx struct { sync.Mutex Systemd *dbus.Conn } func (n Nginx) Generate(dir string, bal *data.Balancer) error { // Acquire a lock on n.Mutex, and release before return f, _ := os.Create(filepath.Join(dir, "nginx.conf")) TplNginxConf.Execute(f, /* template parameters */) f.Close() if bal.Settings.Protocol == "https" { // Dump private key and certificate to the output directory (so that Nginx can find them) } return nil } func (n Nginx) Reload() error { // Acquire a lock on n.Mutex, and release before return switch cfg.Current.Nginx.Mode { case "systemd": if n.Systemd == nil { c, err := dbus.NewSystemdConnection() n.Systemd = c } ch := make(chan string) n.Systemd.ReloadUnit(cfg.Current.Nginx.Systemd.Service, "replace", ch) <-ch return nil default: return errors.New("unknown Nginx mode") } }
Generate สามารถเรียกใช้ได้ด้วยสตริงที่มีเส้นทางไปยังไดเร็กทอรีเอาต์พุตและตัวชี้ไปยังอินสแตนซ์โครงสร้าง Balancer Go มีแพ็คเกจมาตรฐานสำหรับการสร้างเทมเพลตข้อความ ซึ่งไดรเวอร์ NGINX ใช้เพื่อสร้างไฟล์การกำหนดค่า NGINX สุดท้าย เทมเพลตประกอบด้วยบล็อก "อัปสตรีม" ตามด้วยบล็อก "เซิร์ฟเวอร์" ซึ่งสร้างขึ้นตามการกำหนดค่าบาลานเซอร์:
var TplNginxConf = template.Must(template.New("").Parse(` upstream {{.Balancer.Id.Hex}} { {{if eq .Balancer.Settings.Algorithm "least-connections"}} least_conn; {{else if eq .Balancer.Settings.Algorithm "source-ip"}} ip_hash; {{end}} {{range $srv := .Balancer.Servers}} server {{$srv.Settings.Address}} weight={{$srv.Settings.Weight}} {{if eq $srv.Settings.Availability "available"}}{{else if eq $srv.Settings.Availability "backup"}}backup{{else if eq $srv.Settings.Availability "unavailable"}}down{{end}}; {{end}} } server { {{if eq .Balancer.Settings.Protocol "http"}} listen {{.Balancer.Settings.Port}}; {{else if eq .Balancer.Settings.Protocol "https"}} listen {{.Balancer.Settings.Port}} ssl; {{end}} server_name {{.Balancer.Settings.Hostname}}; {{if eq .Balancer.Settings.Protocol "https"}} ssl on; ssl_certificate {{.Dir}}/server.crt; ssl_certificate_key {{.Dir}}/server.key; {{end}} location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://{{.Balancer.Id.Hex}}; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; } } `))
การ โหลด ซ้ำเป็นฟังก์ชันอื่นบนโครงสร้าง Nginx ที่ทำให้ NGINX โหลดไฟล์การกำหนดค่าซ้ำ กลไกที่ใช้จะขึ้นอยู่กับการกำหนดค่า Loadcat โดยค่าเริ่มต้น จะถือว่า NGINX เป็นบริการ systemd ที่ทำงานเป็น nginx.service ดังนั้น [sudo] systemd reload nginx.service
จะทำงาน อย่างไรก็ตาม แทนที่จะรันคำสั่งเชลล์ มันสร้างการเชื่อมต่อกับ systemd ผ่าน D-Bus โดยใช้แพ็คเกจ github.com/coreos/go-systemd/dbus
GUI บนเว็บ
ด้วยส่วนประกอบทั้งหมดเหล่านี้ เราจะปิดท้ายด้วยอินเทอร์เฟซผู้ใช้ Bootstrap ธรรมดา
สำหรับฟังก์ชันพื้นฐานเหล่านี้ ตัวจัดการเส้นทาง GET และ POST ธรรมดาสองสามตัวก็เพียงพอแล้ว:
GET /balancers GET /balancers/new POST /balancers/new GET /balancers/{id} GET /balancers/{id}/edit POST /balancers/{id}/edit GET /balancers/{id}/servers/new POST /balancers/{id}/servers/new GET /servers/{id} GET /servers/{id}/edit POST /servers/{id}/edit
การเดินผ่านแต่ละเส้นทางอาจไม่ใช่สิ่งที่น่าสนใจที่สุดที่จะทำที่นี่ เนื่องจากเป็นหน้า CRUD ที่ค่อนข้างมาก อย่าลังเลที่จะดูที่รหัส ui ของแพ็คเกจเพื่อดูว่ามีการใช้งานตัวจัดการสำหรับแต่ละเส้นทางเหล่านี้อย่างไร
ฟังก์ชันตัวจัดการแต่ละรายการเป็นกิจวัตรที่:
- ดึงข้อมูลจากที่เก็บข้อมูลและตอบสนองด้วยเทมเพลตที่แสดงผล (โดยใช้ข้อมูลที่ดึงมา)
- แยกวิเคราะห์ข้อมูลแบบฟอร์มที่เข้ามา ทำการเปลี่ยนแปลงที่จำเป็นใน datastore และใช้ package feline เพื่อสร้างไฟล์การกำหนดค่า NGINX ใหม่
ตัวอย่างเช่น:
func ServeServerNewForm(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bal, _ := data.GetBalancer(bson.ObjectIdHex(vars["id"])) TplServerNewForm.Execute(w, struct { Balancer *data.Balancer }{ Balancer: bal, }) } func HandleServerCreate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bal, _ := data.GetBalancer(bson.ObjectIdHex(vars["id"])) r.ParseForm() body := struct { Label string `schema:"label"` Settings struct { Address string `schema:"address"` } `schema:"settings"` }{} schema.NewDecoder().Decode(&body, r.PostForm) srv := data.Server{} srv.BalancerId = bal.Id srv.Label = body.Label srv.Settings.Address = body.Settings.Address srv.Put() feline.Commit(bal) http.Redirect(w, r, "/servers/"+srv.Id.Hex()+"/edit", http.StatusSeeOther) }
ฟังก์ชัน ServeServerNewForm ทั้งหมดทำหน้าที่ดึงบาลานเซอร์จากที่เก็บข้อมูลและแสดงเทมเพลต TplServerList ในกรณีนี้ ซึ่งจะดึงรายการเซิร์ฟเวอร์ที่เกี่ยวข้องโดยใช้ฟังก์ชัน เซิร์ฟเวอร์ บนบาลานเซอร์
ในทางกลับกัน ฟังก์ชัน HandleServerCreate จะแยกวิเคราะห์เพย์โหลด POST ขาเข้าจากเนื้อหาเป็น struct และใช้ข้อมูลเหล่านั้นเพื่อสร้างอินสแตนซ์และคงโครงสร้าง เซิร์ฟเวอร์ ใหม่ในที่เก็บข้อมูลก่อนที่จะใช้แพ็คเกจ feline เพื่อสร้างไฟล์การกำหนดค่า NGINX ใหม่สำหรับบาลานเซอร์
เทมเพลตของเพจทั้งหมดถูกจัดเก็บไว้ในไฟล์ “ui/templates.go” และไฟล์ HTML ของเทมเพลตที่เกี่ยวข้องจะอยู่ใต้ไดเร็กทอรี “ui/templates”
ลองเลย
การปรับใช้ Loadcat กับเซิร์ฟเวอร์ระยะไกลหรือแม้แต่ในสภาพแวดล้อมในพื้นที่ของคุณนั้นง่ายมาก หากคุณใช้ Linux (64 บิต) คุณสามารถคว้าไฟล์เก็บถาวรที่มีไบนารี Loadcat ที่สร้างไว้ล่วงหน้าได้จากส่วนการเผยแพร่ของที่เก็บ หากคุณรู้สึกอยากผจญภัย คุณสามารถโคลนที่เก็บและคอมไพล์โค้ดได้ด้วยตัวเอง แม้ว่าประสบการณ์ในกรณีนั้นอาจจะ น่าผิดหวัง เล็กน้อยเนื่องจากการรวบรวมโปรแกรม Go นั้นไม่ใช่เรื่องท้าทายจริงๆ และในกรณีที่คุณใช้ Arch Linux แสดงว่าคุณโชคดี! มีการสร้างแพ็คเกจสำหรับการแจกจ่ายเพื่อความสะดวก เพียงดาวน์โหลดและติดตั้งโดยใช้ตัวจัดการแพ็คเกจของคุณ ขั้นตอนที่เกี่ยวข้องมีรายละเอียดเพิ่มเติมในไฟล์ README.md ของโครงการ
เมื่อคุณกำหนดค่าและใช้งาน Loadcat แล้ว ให้ชี้เว็บเบราว์เซอร์ไปที่ “http://localhost:26590” (สมมติว่าทำงานอยู่ในเครื่องและกำลังฟังบนพอร์ต 26590) ขั้นต่อไป สร้างบาลานเซอร์ สร้างเซิร์ฟเวอร์สองสามเครื่อง ตรวจสอบให้แน่ใจว่ามีบางอย่างกำลังฟังพอร์ตที่กำหนดเหล่านั้น และ voila คุณควรมีคำขอโหลดบาลานซ์ NGINX ขาเข้าระหว่างเซิร์ฟเวอร์ที่ทำงานอยู่เหล่านั้น
อะไรต่อไป?
เครื่องมือนี้ยังห่างไกลจากความสมบูรณ์แบบ และที่จริงแล้วมันค่อนข้างเป็นโครงการทดลอง เครื่องมือนี้ไม่ครอบคลุมถึงฟังก์ชันพื้นฐานทั้งหมดของ NGINX ตัวอย่างเช่น หากคุณต้องการแคชเนื้อหาที่ให้บริการโดยโหนดส่วนหลังที่เลเยอร์ NGINX คุณจะต้องแก้ไขไฟล์การกำหนดค่า NGINX ด้วยมือ และนั่นคือสิ่งที่ทำให้สิ่งต่างๆ น่าตื่นเต้น มีหลายอย่างที่สามารถทำได้ที่นี่ และนั่นคือสิ่งที่จะเกิดขึ้นต่อไป: ครอบคลุมคุณสมบัติการโหลดบาลานซ์ของ NGINX มากยิ่งขึ้น - คุณสมบัติพื้นฐานและแม้กระทั่งคุณสมบัติที่ NGINX Plus มีให้
ให้ Loadcat ลอง ตรวจสอบรหัส แยกมัน เปลี่ยนมัน เล่นกับมัน นอกจากนี้ โปรดแจ้งให้เราทราบหากคุณได้สร้างเครื่องมือที่กำหนดค่าซอฟต์แวร์อื่นหรือใช้ซอฟต์แวร์ที่คุณชอบในส่วนความคิดเห็นด้านล่าง