19 คะแนน โดย xguru 2020-08-10 | 3 ความคิดเห็น | แชร์ทาง WhatsApp

บทความที่อธิบายข้อดีของ Envoy เมื่อเทียบกับ Nginx ได้อย่างดี โดย Dropbox ซึ่งกำลังใช้งานการเชื่อมต่อพร้อมกันหลายสิบล้าน รายการคำขอต่อวินาทีหลายล้านครั้ง และแบนด์วิดท์ระดับเทราไบต์

เดิมที: nginx (เวอร์ชันโอเพนซอร์ส) + python2 + Jinja2 + YAML

→ เปลี่ยนแค่อย่างเดียวก็ต้อง redeploy ทั้งหมด

→ ส่วนที่เป็น dynamic พัฒนาด้วย Lua

→ ลอจิกที่ซับซ้อนประมวลผลใน Bandaid ซึ่งเป็นพร็อกซีที่พัฒนาขึ้นเองบน Go

แม้จะทำงานได้ดีมาตลอดเกือบ 10 ปี แต่ก็ไม่ค่อยเหมาะกับสภาพแวดล้อมปัจจุบันแล้ว

→ API ภายในและภายนอก (แบบไม่เปิดเผยสู่สาธารณะ) กำลังค่อยๆ ย้ายจาก REST ไปเป็น gRPC จึงต้องการความสามารถ transcoding ที่พร็อกซีรองรับ

→ Protocol Buffers กลายเป็นมาตรฐานการนิยามบริการภายใน

→ ซอฟต์แวร์ทั้งหมด build และ test ด้วย Bazel โดยไม่ขึ้นกับภาษา

→ พนักงานมีส่วนร่วมอย่างเข้มข้นกับโอเพนซอร์สคอมมูนิตี้ของโครงการโครงสร้างพื้นฐานหลักหลายตัว

Nginx ยังมีต้นทุนในการดูแลรักษาสูงในเชิงปฏิบัติการด้วย

→ ลอจิกการสร้าง config ยืดหยุ่นเกินไปและกระจายอยู่ใน YAML, Jinja2, Python

→ การมอนิเตอร์เป็นการผสมกันของ Lua / การ parse log / การมอนิเตอร์ระดับระบบ

→ เมื่อพึ่งพา third-party module มากขึ้น ก็ส่งผลต่อเสถียรภาพ/ประสิทธิภาพ และมีต้นทุนจากการอัปเกรดบ่อยครั้ง

→ การ deploy และการจัดการโปรเซสของ nginx เองก็แตกต่างจากบริการอื่นมาก โดยพึ่งพาสิ่งที่ต่างจากระบบพื้นฐานอย่าง syslog, logrotate เป็นต้น

ดังนั้นจึงตัดสินใจหาตัวแทน Nginx เป็นครั้งแรกในรอบ 10 ปี

  • ทำไมไม่ไปใช้ Bandaid (พร็อกซีที่ Dropbox พัฒนาขึ้นเองบน Go) แทน?

→ Go ใช้ทรัพยากรมากกว่า C/C++

→ TLS stack ของ Go ไม่รองรับ FIPS (มาตรฐานการประมวลผลข้อมูลของรัฐบาลกลางสหรัฐฯ)

→ เป็นเครื่องมือภายใน จึงไม่ได้รับการสนับสนุนจากคอมมูนิตี้ภายนอก

ปัจจุบัน: กำลังย้ายไปสู่โครงสร้างพื้นฐานทราฟฟิกที่ใช้ Envoy เป็นฐาน

----- จุดที่ Envoy ดีกว่า Nginx ------

  • ประสิทธิภาพ *

สถาปัตยกรรมของ Nginx เป็นแบบ event-driven / multi-process รองรับ SO_REUSEPORT & EPOLLEXCLUSIVE

แม้จะอิง event loop แต่ก็ไม่ได้ non-blocking อย่างสมบูรณ์ เวลามีการเปิดไฟล์หรือ logging event loop อาจหยุดได้ (แม้จะเปิดใช้ aio, aio_write และ threadpool แล้วก็ตาม)

สิ่งนี้ทำให้เกิด tail latency และบางครั้งมีดีเลย์ระดับหลายวินาที

Envoy ก็มีสถาปัตยกรรมแบบ event-driven คล้ายกัน แต่ใช้ thread แทน process

รองรับ SO_REUSEPORT (พร้อมรองรับ BPF filter) และ event loop ผ่าน libevent

ไม่มี blocking I/O บน event loop และ event logging ก็ทำแบบ non-blocking เช่นกัน

ในทางทฤษฎีดูเหมือนจะมีลักษณะประสิทธิภาพใกล้เคียงกัน และผลทดสอบ workload ส่วนใหญ่ก็คล้ายกันจริง

แต่ Nginx มี latency ที่ปลายหางสูงกว่า เพราะ event loop หยุดเมื่อมี I/O มาก

หากไม่มีการเก็บสถิติ Nginx จะมีประสิทธิภาพใกล้เคียง Envoy แต่เครื่องมือเก็บสถิติภายในที่ทำด้วย Lua ทำให้ Nginx ช้าลง 3 เท่าในการทดสอบ high-RPS (สาเหตุคือ lua_shared_dict ที่ซิงโครไนซ์ด้วย mutex) แม้วิธีเก็บสถิติของ Dropbox เองก็มีปัญหา แต่ก็เลิกพยายามเขียนใหม่ให้มีประสิทธิภาพกว่าเดิม เพราะคาดว่าถ้าทำ instrumentation ภายใน Nginx จะทำให้อัปเกรดยากขึ้นในอนาคต

อย่างไรก็ดี ปัญหาเหล่านี้ไม่มีใน Envoy จึงทำให้หลังการย้ายสามารถปล่อยคืนเซิร์ฟเวอร์ได้สูงสุด 60% ของจำนวนที่ Nginx เดิมใช้อยู่

  • Observability *

Nginx รุ่นฟรีมีเพียง 7 สถิติจากโมดูล stub status

ซึ่งแน่นอนว่าไม่เพียงพอ จึงต้องผูก log_by_lua handler เพื่อให้มีสถิติเพิ่มขึ้น

นอกจากนี้ยังมี parser ของ error.log สำหรับส่งออกข้อมูลข้อผิดพลาด และมี exporter แยกต่างหากเพื่อส่งออกสถานะภายในของ nginx

การตั้งค่า Envoy แบบพื้นฐานให้เมตริกได้หลายพันรายการในฟอร์แมต Prometheus

ตั้งแต่ข้อมูลทราฟฟิกของพร็อกซีไปจนถึงสถานะภายในของเซิร์ฟเวอร์

รวมทั้งสถิติตามคลัสเตอร์/อัปสตรีม/virtual host และสถิติ TCP/HTTP/TLS downstream ตาม listener ฯลฯ

พร้อมกับสถิติที่หลากหลายเหล่านี้ Envoy ยังสามารถเสียบ Tracing Provider ได้แบบปลั๊กอิน

จึงมีประโยชน์ไม่เฉพาะทีมทราฟฟิก แต่รวมถึงนักพัฒนาแอปพลิเคชันด้วย

ท้ายที่สุด Envoy ยังสามารถสตรีม access log ผ่าน gRPC ได้

ซึ่งช่วยลดภาระในการรองรับ syslog-to-hive bridge ของทีมทราฟฟิกลง

การรัน gRPC service ทั่วไปนั้นง่ายและปลอดภัยกว่าการต่อ custom TCP/UDP listener มาก

  • Integration *

การผสานรวมของ Nginx มีความเป็น Unix สูงมาก โดย configuration ค่อนข้าง static

ต้องพึ่งพาไฟล์สำหรับ config, TLS certificate, allowlist/blocklist เป็นต้น

มันเรียบง่ายและเข้ากันได้ย้อนหลัง จึงทำ automation ได้ด้วย shell script ไม่กี่ตัว

แต่เมื่อระบบใหญ่ขึ้น ความสามารถในการทดสอบและการทำให้เป็นมาตรฐานก็สำคัญมากขึ้นเรื่อยๆ

Envoy มีแนวทางของตัวเองสำหรับการผสานรวมลักษณะนี้

มี API ที่เรียกว่า xDS ซึ่งสนับสนุนการใช้ protobuf และ gRPC

Envoy จะค้นหา dynamic resource ผ่านการ query ไปยัง xDS เหล่านี้

  • ตอนนี้ xDS กำลังวิวัฒน์ไปไกลกว่า Envoy ภายใต้ชื่อ Universal Data Place API (UDPA) เพื่อมุ่งเป็นมาตรฐานโดยพฤตินัยของ L4/L7 load balancer และจากประสบการณ์ของเราก็เป็นไปได้ดี ขณะนี้กำลังพยายามใช้ UDPA กับ Katran eBPF/XDP L4 load balancer ที่ไม่ใช่ของ Envoy ด้วย

สำหรับ Dropbox ที่ภายในเชื่อมต่อบริการกันผ่าน gRPC อยู่แล้ว จึงเหมาะกว่ามาก

  • Configuration *

Nginx มีจุดเด่นใหญ่คือไฟล์คอนฟิกที่มนุษย์อ่านง่าย

แต่ข้อดีนี้ค่อยๆ หายไปเมื่อคอนฟิกซับซ้อนขึ้นและถูกสร้างอัตโนมัติ

ที่ Dropbox คอนฟิกถูกสร้างผ่าน Python2, Jinja2, YAML ทำให้ data model พันกันและซับซ้อนมากขึ้นด้วย

Envoy มี data model แบบรวมศูนย์สำหรับ configuration ค่าตั้งทั้งหมดถูกนิยามไว้ใน Protocol Buffer ทำให้แก้ปัญหาการทำ data modeling และเพิ่มข้อมูลชนิดให้กับค่าคอนฟิกได้

เนื่องจากภายใน Dropbox ใช้ protobuf อย่างแพร่หลายอยู่แล้ว การผสานรวมจึงง่าย

  • Extensibility *

การขยายความสามารถของ Nginx ต้องเขียน C module ซึ่งการเขียนโมดูลให้ปลอดภัยต้องอาศัยวิศวกรอาวุโส แม้จะมีอินเทอร์เฟซ Perl / JS สำหรับทำโมดูลแบบเบากว่า แต่ก็จำกัดมาก ดังนั้นวิธีที่ใช้กันทั่วไปที่สุดคือผ่าน lua-nginx-module

กลไกหลักในการขยายของ Envoy คือ C++ plugin ซึ่งเอกสารอาจไม่ดีเท่า nginx แต่ตัวมันง่ายมาก เพราะมีอินเทอร์เฟซที่สะอาดและใส่คอมเมนต์ไว้ดี รวมถึงใช้ภาษา C++14 และ standard library

จุดต่างสำคัญของ Envoy เมื่อเทียบกับเว็บเซิร์ฟเวอร์อื่นคือการรองรับ WebAssembly (WASM)

ซึ่งทำให้รองรับการพัฒนาส่วนขยายด้วยภาษาต่างๆ อย่าง Rust ได้

แม้ Dropbox ยังไม่ได้ใช้ WASM แต่หากวันหนึ่งมีการรองรับ Go SDK for proxy-wasm ก็อาจเปลี่ยนแปลงได้

  • Building and Testing *

Nginx โดยพื้นฐานใช้การตั้งค่าแบบ custom shell และการ build แบบ make ซึ่งเรียบง่ายและยอดเยี่ยม แต่การนำไปผสานกับ monorepo ที่ build ด้วย Bazel ต้องใช้ความพยายามมากพอสมควร

Nginx มี integration test ที่เขียนด้วย Perl แต่ไม่มี unit test

Envoy มีระบบ build แบบ Bazel อยู่แล้ว และสามารถผสานเข้ากับ monorepo ของเราได้ง่าย

รองรับ unit test ที่ใช้ gtest/gmock และ framework สำหรับ integration test

  • Security *

โค้ดของ Nginx มีขนาดเล็กมากและมี dependency ภายนอกน้อย จึงมีช่องโหว่ด้านความปลอดภัยไม่มากนัก

Envoy มีโค้ดจำนวนมาก จึงดูเหมือนมีพื้นที่ให้โจมตีได้มากกว่า ด้วยเหตุนี้ Envoy จึงพึ่งพาแนวปฏิบัติด้านความปลอดภัยสมัยใหม่จำนวนมาก เช่น AddressSanitizer, ThreadSanitizer, MemorySanitizer เป็นต้น

  • Features *

ส่วนนี้มีความเห็นเชิงอัตวิสัยอยู่มาก โปรดใช้วิจารณญาณ

Nginx เริ่มต้นจากการเป็นเว็บเซิร์ฟเวอร์สำหรับให้บริการ static file ด้วยทรัพยากรน้อยมาก

กล่าวคือหน้าที่หลักคือ static serving, caching, range caching

แต่ในมุมของพร็อกซี Nginx ยังขาดฟีเจอร์หลายอย่างที่โครงสร้างพื้นฐานยุคนี้ต้องการ

ไม่รองรับการเชื่อมต่อ HTTP/2 กับ backend, ทำ gRPC proxy แบบหลายการเชื่อมต่อไม่ได้, และทำ gRPC transcoding ก็ไม่ได้ เป็นต้น

ด้วยโมเดลไลเซนส์แบบ open-core ฟีเจอร์สำคัญบางอย่างจึงไม่มีใน "community version"

Envoy เริ่มต้นมาเพื่อเป็น ingress/egress proxy โดยตรง และถูกใช้มากในสภาพแวดล้อมที่มีโหลด gRPC สูง

ความสามารถด้านเว็บเซอร์วิสยังอยู่ในระดับพื้นฐานมาก ไม่รองรับ file serving, caching ยังอยู่ระหว่างพัฒนา, และยังไม่รองรับ brotli เป็นต้น

สำหรับสภาพแวดล้อมเช่นนี้ก็ยังมีการใช้ชุดติดตั้ง Nginx ที่ใช้ Envoy เป็น upstream cluster อยู่

คาดว่าเมื่อ Envoy รองรับ HTTP cache ได้แล้ว ก็จะย้ายสภาพแวดล้อมการให้บริการ static ลักษณะนี้ได้ด้วย

Envoy รองรับความสามารถที่เกี่ยวข้องกับ gRPC จำนวนมาก

  • gRPC proxying

  • HTTP/2 to backends

  • gRPC → HTTP bridge (+ reverse.)

  • gRPC-WEB

  • gRPC JSON transcoder

นอกจากนี้ Envoy ยังใช้เป็น outbound proxy ได้ด้วย

  • Egress Proxy

  • Third-party software service discovery with Courier gRPC library

  • Community *

การพัฒนาของ Nginx มีลักษณะรวมศูนย์และส่วนใหญ่ซ่อนอยู่

การพัฒนาของ Envoy เปิดกว้างและกระจายศูนย์ ดำเนินผ่าน GitHub issue/PR และยังคึกคักผ่านเมลลิงลิสต์/Slack เป็นต้น

----- สถานะการย้ายระบบของ Dropbox ในปัจจุบัน -----

ได้รัน Nginx และ Envoy ควบคู่กันมาครึ่งปีแล้ว พร้อมค่อยๆ ย้ายทราฟฟิกผ่าน DNS

ไม่ได้ย้ายได้โดยไม่มีปัญหาเลย ยังมีปัญหาเล็กๆ น้อยๆ อยู่บ้าง แต่ไม่มีเหตุขัดข้องร้ายแรง

มีการสรุปวิธีแก้ปัญหาที่พบจากพฤติกรรมแบบ "unusual" หรือ "non-RFC" ไว้ด้วย (รายละเอียดอยู่ในบทความต้นฉบับ)

** สิ่งที่จะทำต่อไป **

  • HTTP/3 : Envoy เริ่มรองรับแบบทดลองแล้ว และมีแผนจะทดลองเมื่ออัปเกรด Linux kernel เพื่อเร่งความเร็ว UDP

  • internal xDS-based load balancer และ Outlier Detection

  • ส่วนขยาย Envoy ที่ใช้ WASM

  • แทนที่ Bandaid (พร็อกซีที่พัฒนาด้วย Go) ด้วย Envoy

  • นำ Envoy Mobile ไปใช้กับแอปมือถือด้วย

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

 
baeba 2020-08-13

ขอบคุณที่ช่วยสรุปเนื้อหาดี ๆ ให้เข้าใจง่าย

มาอย่างกระชับครับ

 
before30 2020-08-11

ขอบคุณค่ะ :)

 
loslch 2020-08-11

ขอบคุณมากสำหรับการสรุปที่ละเอียดและความเห็นที่อธิบายอย่างเป็นกันเอง ช่วยให้เข้าใจได้มากเลย :)