19 คะแนน โดย xguru 2020-08-10 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
<p>บทความที่อธิบายข้อดีของ Envoy เมื่อเทียบกับ Nginx ได้อย่างดี โดย Dropbox ซึ่งกำลังใช้งานการเชื่อมต่อพร้อมกันหลายสิบล้าน รายการคำขอต่อวินาทีหลายล้านครั้ง และแบนด์วิดท์ระดับเทราไบต์<br /> <br /> เดิมที: nginx (เวอร์ชันโอเพนซอร์ส) + python2 + Jinja2 + YAML <br /> → เปลี่ยนแค่อย่างเดียวก็ต้อง redeploy ทั้งหมด<br /> → ส่วนที่เป็น dynamic พัฒนาด้วย Lua<br /> → ลอจิกที่ซับซ้อนประมวลผลใน Bandaid ซึ่งเป็นพร็อกซีที่พัฒนาขึ้นเองบน Go<br /> <br /> แม้จะทำงานได้ดีมาตลอดเกือบ 10 ปี แต่ก็ไม่ค่อยเหมาะกับสภาพแวดล้อมปัจจุบันแล้ว<br /> → API ภายในและภายนอก (แบบไม่เปิดเผยสู่สาธารณะ) กำลังค่อยๆ ย้ายจาก REST ไปเป็น gRPC จึงต้องการความสามารถ transcoding ที่พร็อกซีรองรับ<br /> → Protocol Buffers กลายเป็นมาตรฐานการนิยามบริการภายใน<br /> → ซอฟต์แวร์ทั้งหมด build และ test ด้วย Bazel โดยไม่ขึ้นกับภาษา<br /> → พนักงานมีส่วนร่วมอย่างเข้มข้นกับโอเพนซอร์สคอมมูนิตี้ของโครงการโครงสร้างพื้นฐานหลักหลายตัว<br /> <br /> Nginx ยังมีต้นทุนในการดูแลรักษาสูงในเชิงปฏิบัติการด้วย<br /> → ลอจิกการสร้าง config ยืดหยุ่นเกินไปและกระจายอยู่ใน YAML, Jinja2, Python<br /> → การมอนิเตอร์เป็นการผสมกันของ Lua / การ parse log / การมอนิเตอร์ระดับระบบ<br /> → เมื่อพึ่งพา third-party module มากขึ้น ก็ส่งผลต่อเสถียรภาพ/ประสิทธิภาพ และมีต้นทุนจากการอัปเกรดบ่อยครั้ง<br /> → การ deploy และการจัดการโปรเซสของ nginx เองก็แตกต่างจากบริการอื่นมาก โดยพึ่งพาสิ่งที่ต่างจากระบบพื้นฐานอย่าง syslog, logrotate เป็นต้น<br /> <br /> ดังนั้นจึงตัดสินใจหาตัวแทน Nginx เป็นครั้งแรกในรอบ 10 ปี<br /> <br /> * ทำไมไม่ไปใช้ Bandaid (พร็อกซีที่ Dropbox พัฒนาขึ้นเองบน Go) แทน? <br /> → Go ใช้ทรัพยากรมากกว่า C/C++ <br /> → TLS stack ของ Go ไม่รองรับ FIPS (มาตรฐานการประมวลผลข้อมูลของรัฐบาลกลางสหรัฐฯ)<br /> → เป็นเครื่องมือภายใน จึงไม่ได้รับการสนับสนุนจากคอมมูนิตี้ภายนอก <br /> <br /> ปัจจุบัน: กำลังย้ายไปสู่โครงสร้างพื้นฐานทราฟฟิกที่ใช้ Envoy เป็นฐาน <br /> <br /> ----- จุดที่ Envoy ดีกว่า Nginx ------<br /> <br /> * ประสิทธิภาพ *<br /> <br /> สถาปัตยกรรมของ Nginx เป็นแบบ event-driven / multi-process รองรับ SO_REUSEPORT &amp; EPOLLEXCLUSIVE<br /> แม้จะอิง event loop แต่ก็ไม่ได้ non-blocking อย่างสมบูรณ์ เวลามีการเปิดไฟล์หรือ logging event loop อาจหยุดได้ (แม้จะเปิดใช้ aio, aio_write และ threadpool แล้วก็ตาม)<br /> สิ่งนี้ทำให้เกิด tail latency และบางครั้งมีดีเลย์ระดับหลายวินาที<br /> <br /> Envoy ก็มีสถาปัตยกรรมแบบ event-driven คล้ายกัน แต่ใช้ thread แทน process <br /> รองรับ SO_REUSEPORT (พร้อมรองรับ BPF filter) และ event loop ผ่าน libevent <br /> ไม่มี blocking I/O บน event loop และ event logging ก็ทำแบบ non-blocking เช่นกัน<br /> <br /> ในทางทฤษฎีดูเหมือนจะมีลักษณะประสิทธิภาพใกล้เคียงกัน และผลทดสอบ workload ส่วนใหญ่ก็คล้ายกันจริง<br /> แต่ Nginx มี latency ที่ปลายหางสูงกว่า เพราะ event loop หยุดเมื่อมี I/O มาก<br /> <br /> หากไม่มีการเก็บสถิติ Nginx จะมีประสิทธิภาพใกล้เคียง Envoy แต่เครื่องมือเก็บสถิติภายในที่ทำด้วย Lua ทำให้ Nginx ช้าลง 3 เท่าในการทดสอบ high-RPS (สาเหตุคือ `lua_shared_dict` ที่ซิงโครไนซ์ด้วย mutex) แม้วิธีเก็บสถิติของ Dropbox เองก็มีปัญหา แต่ก็เลิกพยายามเขียนใหม่ให้มีประสิทธิภาพกว่าเดิม เพราะคาดว่าถ้าทำ instrumentation ภายใน Nginx จะทำให้อัปเกรดยากขึ้นในอนาคต<br /> <br /> อย่างไรก็ดี ปัญหาเหล่านี้ไม่มีใน Envoy จึงทำให้หลังการย้ายสามารถปล่อยคืนเซิร์ฟเวอร์ได้สูงสุด 60% ของจำนวนที่ Nginx เดิมใช้อยู่<br /> <br /> * Observability *<br /> <br /> Nginx รุ่นฟรีมีเพียง 7 สถิติจากโมดูล stub status <br /> ซึ่งแน่นอนว่าไม่เพียงพอ จึงต้องผูก `log_by_lua` handler เพื่อให้มีสถิติเพิ่มขึ้น<br /> นอกจากนี้ยังมี parser ของ `error.log` สำหรับส่งออกข้อมูลข้อผิดพลาด และมี exporter แยกต่างหากเพื่อส่งออกสถานะภายในของ nginx<br /> <br /> การตั้งค่า Envoy แบบพื้นฐานให้เมตริกได้หลายพันรายการในฟอร์แมต Prometheus <br /> ตั้งแต่ข้อมูลทราฟฟิกของพร็อกซีไปจนถึงสถานะภายในของเซิร์ฟเวอร์<br /> รวมทั้งสถิติตามคลัสเตอร์/อัปสตรีม/virtual host และสถิติ TCP/HTTP/TLS downstream ตาม listener ฯลฯ<br /> <br /> พร้อมกับสถิติที่หลากหลายเหล่านี้ Envoy ยังสามารถเสียบ Tracing Provider ได้แบบปลั๊กอิน<br /> จึงมีประโยชน์ไม่เฉพาะทีมทราฟฟิก แต่รวมถึงนักพัฒนาแอปพลิเคชันด้วย<br /> <br /> ท้ายที่สุด Envoy ยังสามารถสตรีม access log ผ่าน gRPC ได้<br /> ซึ่งช่วยลดภาระในการรองรับ syslog-to-hive bridge ของทีมทราฟฟิกลง<br /> การรัน gRPC service ทั่วไปนั้นง่ายและปลอดภัยกว่าการต่อ custom TCP/UDP listener มาก<br /> <br /> * Integration *<br /> <br /> การผสานรวมของ Nginx มีความเป็น Unix สูงมาก โดย configuration ค่อนข้าง static<br /> ต้องพึ่งพาไฟล์สำหรับ config, TLS certificate, allowlist/blocklist เป็นต้น<br /> มันเรียบง่ายและเข้ากันได้ย้อนหลัง จึงทำ automation ได้ด้วย shell script ไม่กี่ตัว<br /> แต่เมื่อระบบใหญ่ขึ้น ความสามารถในการทดสอบและการทำให้เป็นมาตรฐานก็สำคัญมากขึ้นเรื่อยๆ<br /> <br /> Envoy มีแนวทางของตัวเองสำหรับการผสานรวมลักษณะนี้<br /> มี API ที่เรียกว่า xDS ซึ่งสนับสนุนการใช้ protobuf และ gRPC<br /> Envoy จะค้นหา dynamic resource ผ่านการ query ไปยัง xDS เหล่านี้<br /> <br /> - ตอนนี้ xDS กำลังวิวัฒน์ไปไกลกว่า Envoy ภายใต้ชื่อ Universal Data Place API (UDPA) เพื่อมุ่งเป็นมาตรฐานโดยพฤตินัยของ L4/L7 load balancer และจากประสบการณ์ของเราก็เป็นไปได้ดี ขณะนี้กำลังพยายามใช้ UDPA กับ Katran eBPF/XDP L4 load balancer ที่ไม่ใช่ของ Envoy ด้วย<br /> <br /> สำหรับ Dropbox ที่ภายในเชื่อมต่อบริการกันผ่าน gRPC อยู่แล้ว จึงเหมาะกว่ามาก<br /> <br /> * Configuration *<br /> <br /> Nginx มีจุดเด่นใหญ่คือไฟล์คอนฟิกที่มนุษย์อ่านง่าย <br /> แต่ข้อดีนี้ค่อยๆ หายไปเมื่อคอนฟิกซับซ้อนขึ้นและถูกสร้างอัตโนมัติ<br /> ที่ Dropbox คอนฟิกถูกสร้างผ่าน Python2, Jinja2, YAML ทำให้ data model พันกันและซับซ้อนมากขึ้นด้วย<br /> <br /> Envoy มี data model แบบรวมศูนย์สำหรับ configuration ค่าตั้งทั้งหมดถูกนิยามไว้ใน Protocol Buffer ทำให้แก้ปัญหาการทำ data modeling และเพิ่มข้อมูลชนิดให้กับค่าคอนฟิกได้<br /> เนื่องจากภายใน Dropbox ใช้ protobuf อย่างแพร่หลายอยู่แล้ว การผสานรวมจึงง่าย <br /> <br /> * Extensibility * <br /> <br /> การขยายความสามารถของ Nginx ต้องเขียน C module ซึ่งการเขียนโมดูลให้ปลอดภัยต้องอาศัยวิศวกรอาวุโส แม้จะมีอินเทอร์เฟซ Perl / JS สำหรับทำโมดูลแบบเบากว่า แต่ก็จำกัดมาก ดังนั้นวิธีที่ใช้กันทั่วไปที่สุดคือผ่าน `lua-nginx-module` <br /> <br /> กลไกหลักในการขยายของ Envoy คือ C++ plugin ซึ่งเอกสารอาจไม่ดีเท่า nginx แต่ตัวมันง่ายมาก เพราะมีอินเทอร์เฟซที่สะอาดและใส่คอมเมนต์ไว้ดี รวมถึงใช้ภาษา C++14 และ standard library <br /> <br /> จุดต่างสำคัญของ Envoy เมื่อเทียบกับเว็บเซิร์ฟเวอร์อื่นคือการรองรับ WebAssembly (WASM)<br /> ซึ่งทำให้รองรับการพัฒนาส่วนขยายด้วยภาษาต่างๆ อย่าง Rust ได้ <br /> แม้ Dropbox ยังไม่ได้ใช้ WASM แต่หากวันหนึ่งมีการรองรับ Go SDK for proxy-wasm ก็อาจเปลี่ยนแปลงได้<br /> <br /> * Building and Testing *<br /> <br /> Nginx โดยพื้นฐานใช้การตั้งค่าแบบ custom shell และการ build แบบ make ซึ่งเรียบง่ายและยอดเยี่ยม แต่การนำไปผสานกับ monorepo ที่ build ด้วย Bazel ต้องใช้ความพยายามมากพอสมควร <br /> Nginx มี integration test ที่เขียนด้วย Perl แต่ไม่มี unit test<br /> <br /> Envoy มีระบบ build แบบ Bazel อยู่แล้ว และสามารถผสานเข้ากับ monorepo ของเราได้ง่าย<br /> รองรับ unit test ที่ใช้ gtest/gmock และ framework สำหรับ integration test<br /> <br /> * Security *<br /> <br /> โค้ดของ Nginx มีขนาดเล็กมากและมี dependency ภายนอกน้อย จึงมีช่องโหว่ด้านความปลอดภัยไม่มากนัก<br /> <br /> Envoy มีโค้ดจำนวนมาก จึงดูเหมือนมีพื้นที่ให้โจมตีได้มากกว่า ด้วยเหตุนี้ Envoy จึงพึ่งพาแนวปฏิบัติด้านความปลอดภัยสมัยใหม่จำนวนมาก เช่น AddressSanitizer, ThreadSanitizer, MemorySanitizer เป็นต้น <br /> <br /> * Features * <br /> <br /> ส่วนนี้มีความเห็นเชิงอัตวิสัยอยู่มาก โปรดใช้วิจารณญาณ<br /> <br /> Nginx เริ่มต้นจากการเป็นเว็บเซิร์ฟเวอร์สำหรับให้บริการ static file ด้วยทรัพยากรน้อยมาก <br /> กล่าวคือหน้าที่หลักคือ static serving, caching, range caching<br /> แต่ในมุมของพร็อกซี Nginx ยังขาดฟีเจอร์หลายอย่างที่โครงสร้างพื้นฐานยุคนี้ต้องการ <br /> ไม่รองรับการเชื่อมต่อ HTTP/2 กับ backend, ทำ gRPC proxy แบบหลายการเชื่อมต่อไม่ได้, และทำ gRPC transcoding ก็ไม่ได้ เป็นต้น<br /> ด้วยโมเดลไลเซนส์แบบ open-core ฟีเจอร์สำคัญบางอย่างจึงไม่มีใน "community version"<br /> <br /> Envoy เริ่มต้นมาเพื่อเป็น ingress/egress proxy โดยตรง และถูกใช้มากในสภาพแวดล้อมที่มีโหลด gRPC สูง<br /> ความสามารถด้านเว็บเซอร์วิสยังอยู่ในระดับพื้นฐานมาก ไม่รองรับ file serving, caching ยังอยู่ระหว่างพัฒนา, และยังไม่รองรับ brotli เป็นต้น <br /> สำหรับสภาพแวดล้อมเช่นนี้ก็ยังมีการใช้ชุดติดตั้ง Nginx ที่ใช้ Envoy เป็น upstream cluster อยู่ <br /> คาดว่าเมื่อ Envoy รองรับ HTTP cache ได้แล้ว ก็จะย้ายสภาพแวดล้อมการให้บริการ static ลักษณะนี้ได้ด้วย <br /> <br /> Envoy รองรับความสามารถที่เกี่ยวข้องกับ gRPC จำนวนมาก<br /> - gRPC proxying<br /> - HTTP/2 to backends<br /> - gRPC → HTTP bridge (+ reverse.) <br /> - gRPC-WEB <br /> - gRPC JSON transcoder<br /> <br /> นอกจากนี้ Envoy ยังใช้เป็น outbound proxy ได้ด้วย <br /> - Egress Proxy<br /> - Third-party software service discovery with Courier gRPC library <br /> <br /> * Community *<br /> <br /> การพัฒนาของ Nginx มีลักษณะรวมศูนย์และส่วนใหญ่ซ่อนอยู่ <br /> การพัฒนาของ Envoy เปิดกว้างและกระจายศูนย์ ดำเนินผ่าน GitHub issue/PR และยังคึกคักผ่านเมลลิงลิสต์/Slack เป็นต้น </p><p>----- สถานะการย้ายระบบของ Dropbox ในปัจจุบัน -----<br /> <br /> ได้รัน Nginx และ Envoy ควบคู่กันมาครึ่งปีแล้ว พร้อมค่อยๆ ย้ายทราฟฟิกผ่าน DNS <br /> ไม่ได้ย้ายได้โดยไม่มีปัญหาเลย ยังมีปัญหาเล็กๆ น้อยๆ อยู่บ้าง แต่ไม่มีเหตุขัดข้องร้ายแรง<br /> มีการสรุปวิธีแก้ปัญหาที่พบจากพฤติกรรมแบบ "unusual" หรือ "non-RFC" ไว้ด้วย (รายละเอียดอยู่ในบทความต้นฉบับ)<br /> <br /> ** สิ่งที่จะทำต่อไป **<br /> <br /> - HTTP/3 : Envoy เริ่มรองรับแบบทดลองแล้ว และมีแผนจะทดลองเมื่ออัปเกรด Linux kernel เพื่อเร่งความเร็ว UDP<br /> - internal xDS-based load balancer และ Outlier Detection<br /> - ส่วนขยาย Envoy ที่ใช้ WASM <br /> - แทนที่ Bandaid (พร็อกซีที่พัฒนาด้วย Go) ด้วย Envoy <br /> - นำ Envoy Mobile ไปใช้กับแอปมือถือด้วย</p>

3 ความคิดเห็น

 
baeba 2020-08-13
<p>ขอบคุณที่ช่วยสรุปเนื้อหาดี ๆ ให้เข้าใจง่าย<br /> มาอย่างกระชับครับ</p>
 
before30 2020-08-11
<p>ขอบคุณค่ะ :)</p>
 
loslch 2020-08-11
<p>ขอบคุณมากสำหรับการสรุปที่ละเอียดและความเห็นที่อธิบายอย่างเป็นกันเอง ช่วยให้เข้าใจได้มากเลย :)</p>