บนอินเทอร์เน็ต
ฉันจะพูดถึงแพลตฟอร์ม dotCloud ซึ่งสร้างขึ้นจากไมโครเซอร์วิสกว่าร้อยรายการและรองรับแอปพลิเคชันหลายพันรายการในคอนเทนเนอร์ ฉันจะอธิบายความท้าทายที่เราพบในการพัฒนาและการเปิดตัว และวิธีที่ Service Mesh อาจช่วย (หรืออาจจะไม่) ช่วยได้
ประวัติความเป็นมาของดอทคลาวด์
ฉันได้เขียนเกี่ยวกับประวัติของ dotCloud และการเลือกสถาปัตยกรรมสำหรับแพลตฟอร์มนี้แล้ว แต่ยังไม่ได้พูดคุยเกี่ยวกับเลเยอร์เครือข่ายมากนัก ถ้าไม่อยากอ่าน
ฉันจะบอกคุณว่าการรับส่งข้อมูลถูกส่งไปยังแพลตฟอร์ม dotCloud อย่างไร ไม่ใช่เพราะมันเจ๋งเป็นพิเศษ (แม้ว่าระบบจะทำงานได้ดีในช่วงเวลานั้น!) แต่โดยหลักแล้วเป็นเพราะด้วยเครื่องมือที่ทันสมัย การออกแบบดังกล่าวสามารถนำไปใช้งานได้อย่างง่ายดายในระยะเวลาอันสั้นโดยทีมงานที่ไม่ซับซ้อน หากต้องการวิธีกำหนดเส้นทางการรับส่งข้อมูลระหว่างกลุ่ม ของไมโครเซอร์วิสหรือแอพพลิเคชั่นมากมาย ด้วยวิธีนี้ คุณสามารถเปรียบเทียบตัวเลือกต่างๆ ได้: จะเกิดอะไรขึ้นหากคุณพัฒนาทุกอย่างด้วยตัวเองหรือใช้ Service Mesh ที่มีอยู่ ทางเลือกมาตรฐานคือทำเองหรือซื้อ
การกำหนดเส้นทางการรับส่งข้อมูลสำหรับแอปพลิเคชันที่โฮสต์
แอปพลิเคชันบน dotCloud สามารถเปิดเผยจุดสิ้นสุด HTTP และ TCP
ปลายทาง HTTP เพิ่มแบบไดนามิกให้กับการกำหนดค่าคลัสเตอร์ของโหลดบาลานเซอร์
ไคลเอนต์เชื่อมต่อกับตำแหน่งข้อมูล HTTP ผ่านโดเมนที่เหมาะสม โดยที่ชื่อโดเมนชี้ไปที่โหลดบาลานเซอร์ dotCloud ไม่มีอะไรพิเศษ.
จุดสิ้นสุด TCP เชื่อมโยงกับหมายเลขพอร์ต ซึ่งจะถูกส่งต่อไปยังคอนเทนเนอร์ทั้งหมดในสแต็กนั้นผ่านตัวแปรสภาพแวดล้อม
ไคลเอนต์สามารถเชื่อมต่อกับตำแหน่งข้อมูล TCP โดยใช้ชื่อโฮสต์ที่เหมาะสม (เช่น gateway-X.dotcloud.com) และหมายเลขพอร์ต
ชื่อโฮสต์นี้แก้ไขเป็นคลัสเตอร์เซิร์ฟเวอร์ "nats" (ไม่เกี่ยวข้องกับ
หากคุณคุ้นเคยกับ Kubernetes สิ่งนี้อาจจะเตือนคุณถึงบริการต่างๆ
ไม่มีบริการใดที่เทียบเท่าบนแพลตฟอร์ม dotCloud
ทุกอย่างได้รับการจัดระเบียบค่อนข้างเรียบง่าย: การใช้งานดั้งเดิมของเครือข่ายการกำหนดเส้นทาง HTTP และ TCP อาจเป็นเพียงไม่กี่ร้อยบรรทัดของ Python อัลกอริธึมที่เรียบง่าย (ฉันจะบอกว่าไร้เดียงสา) ที่ได้รับการปรับปรุงตามการเติบโตของแพลตฟอร์มและการเกิดขึ้นของข้อกำหนดเพิ่มเติม
ไม่จำเป็นต้องมีการปรับโครงสร้างโค้ดที่มีอยู่อย่างกว้างขวาง โดยเฉพาะอย่างยิ่ง,
สิ่งนี้แตกต่างจาก Service Mesh สมัยใหม่อย่างไร
ถูก จำกัด ทัศนวิสัย. เราไม่มีตัวชี้วัดใดๆ สำหรับ TCP Routing Mesh เลย เมื่อพูดถึงการกำหนดเส้นทาง HTTP เวอร์ชันล่าสุดจะมีตัววัด HTTP โดยละเอียดพร้อมรหัสข้อผิดพลาดและเวลาตอบสนอง แต่โครงข่ายบริการสมัยใหม่ยังไปไกลกว่านั้นอีก โดยให้การผสานรวมกับระบบรวบรวมตัววัดเช่น Prometheus เป็นต้น
การมองเห็นมีความสำคัญไม่เพียงแต่จากมุมมองการปฏิบัติงาน (เพื่อช่วยแก้ไขปัญหา) แต่ยังรวมถึงเมื่อมีการเปิดตัวคุณสมบัติใหม่ด้วย พูดถึงเรื่องปลอดภัย.
ประสิทธิภาพการกำหนดเส้นทาง ก็มีจำกัดเช่นกัน ใน DotCloud Routing Mesh การรับส่งข้อมูลทั้งหมดต้องผ่านคลัสเตอร์ของโหนดการกำหนดเส้นทางเฉพาะ ซึ่งหมายความว่าอาจข้ามขอบเขต AZ (โซนความพร้อมใช้งาน) หลายแห่งและมีเวลาแฝงเพิ่มขึ้นอย่างมาก ฉันจำโค้ดการแก้ไขปัญหาที่สร้างแบบสอบถาม SQL มากกว่าร้อยรายการต่อหน้า และเปิดการเชื่อมต่อใหม่ไปยังเซิร์ฟเวอร์ SQL สำหรับแต่ละแบบสอบถาม เมื่อรันในเครื่อง เพจจะโหลดทันที แต่บน dotCloud จะใช้เวลาโหลดไม่กี่วินาที เนื่องจากการเชื่อมต่อ TCP แต่ละครั้ง (และการสืบค้น SQL ที่ตามมา) ใช้เวลาหลายสิบมิลลิวินาที ในกรณีนี้ การเชื่อมต่อแบบถาวรช่วยแก้ปัญหาได้
โครงข่ายบริการสมัยใหม่จะจัดการกับปัญหาดังกล่าวได้ดีกว่า ก่อนอื่น พวกเขาตรวจสอบว่าการเชื่อมต่อถูกกำหนดเส้นทางแล้ว ในแหล่งที่มา. การไหลแบบลอจิคัลจะเหมือนกัน: клиент → меш → сервис
แต่ตอนนี้เมชทำงานได้ในเครื่องและไม่ได้อยู่บนโหนดระยะไกล ดังนั้นการเชื่อมต่อ клиент → меш
เป็นท้องถิ่นและรวดเร็วมาก (ไมโครวินาทีแทนที่จะเป็นมิลลิวินาที)
โครงข่ายบริการสมัยใหม่ยังใช้อัลกอริธึมการปรับสมดุลโหลดที่ชาญฉลาดยิ่งขึ้นอีกด้วย ด้วยการตรวจสอบความสมบูรณ์ของแบ็กเอนด์ พวกเขาสามารถส่งการรับส่งข้อมูลไปยังแบ็กเอนด์ที่เร็วขึ้น ส่งผลให้ประสิทธิภาพโดยรวมดีขึ้น
ความปลอดภัย ก็ยังดีกว่า การกำหนดเส้นทาง dotCloud ทำงานทั้งหมดบน EC2 Classic และไม่ได้เข้ารหัสการรับส่งข้อมูล (ภายใต้สมมติฐานที่ว่าหากมีคนพยายามดักจับการรับส่งข้อมูลเครือข่าย EC2 แสดงว่าคุณกำลังประสบปัญหาใหญ่แล้ว) โครงข่ายบริการสมัยใหม่ช่วยปกป้องการรับส่งข้อมูลทั้งหมดของเราอย่างโปร่งใส เช่น ด้วยการตรวจสอบความถูกต้อง TLS ร่วมกันและการเข้ารหัสที่ตามมา
การกำหนดเส้นทางการรับส่งข้อมูลสำหรับบริการแพลตฟอร์ม
โอเค เราได้พูดคุยถึงการรับส่งข้อมูลระหว่างแอปพลิเคชันแล้ว แต่แพลตฟอร์ม dotCloud เองล่ะ?
แพลตฟอร์มดังกล่าวประกอบด้วยไมโครเซอร์วิสประมาณร้อยรายการที่รับผิดชอบฟังก์ชันต่างๆ บางคนยอมรับคำขอจากผู้อื่น และบางคนเป็นผู้ทำงานเบื้องหลังที่เชื่อมต่อกับบริการอื่น ๆ แต่ไม่ยอมรับการเชื่อมต่อด้วยตนเอง ไม่ว่าในกรณีใด แต่ละบริการจะต้องทราบจุดสิ้นสุดของที่อยู่ที่ต้องการเชื่อมต่อ
บริการระดับสูงจำนวนมากอาจใช้ Routing Mesh ที่อธิบายไว้ข้างต้น ในความเป็นจริง ไมโครเซอร์วิส dotCloud มากกว่า XNUMX รายการจำนวนมากถูกนำไปใช้เป็นแอปพลิเคชันทั่วไปบนแพลตฟอร์ม dotCloud เอง แต่บริการระดับต่ำจำนวนไม่มาก (โดยเฉพาะบริการที่ใช้ Routing Mesh นี้) ต้องการสิ่งที่ง่ายกว่าและมีการพึ่งพาน้อยลง (เพราะพวกเขาไม่สามารถพึ่งพาตนเองในการทำงานได้ - ปัญหาไก่และไข่ที่ดี)
บริการที่จำเป็นระดับต่ำเหล่านี้ได้รับการปรับใช้โดยการรันคอนเทนเนอร์โดยตรงบนโหนดหลักสองสามโหนด ในเวลาเดียวกัน บริการแพลตฟอร์มมาตรฐานไม่เกี่ยวข้อง: ตัวเชื่อมโยง ตัวกำหนดเวลา และตัวดำเนินการ หากจะเปรียบเทียบกับแพลตฟอร์มคอนเทนเนอร์สมัยใหม่ก็เหมือนกับการเปิดตัวเครื่องบินควบคุมด้วย docker run
บนโหนดโดยตรง แทนที่จะมอบหมายงานให้กับ Kubernetes มันค่อนข้างคล้ายกันในแนวคิด
บริการเหล่านี้ถูกเปิดเผยด้วยวิธีที่เรียบง่ายและหยาบคาย: ไฟล์ YAML ระบุชื่อและที่อยู่ และไคลเอนต์แต่ละรายจะต้องนำสำเนาของไฟล์ YAML นี้เพื่อนำไปใช้งาน
ในแง่หนึ่ง สิ่งนี้มีความน่าเชื่อถืออย่างยิ่ง เนื่องจากไม่ต้องการการสนับสนุนจากที่เก็บคีย์/ค่าภายนอก เช่น Zookeeper (โปรดจำไว้ว่า ในขณะนั้นไม่มี etcd หรือ Consul) ในทางกลับกันทำให้การเคลื่อนย้ายบริการทำได้ยาก แต่ละครั้งที่มีการย้าย ไคลเอนต์ทั้งหมดจะต้องได้รับไฟล์ YAML ที่อัปเดตแล้ว (และอาจโหลดซ้ำได้) ไม่ค่อยสบาย!
ต่อมา เราเริ่มใช้รูปแบบใหม่ โดยที่ไคลเอนต์แต่ละรายเชื่อมต่อกับพร็อกซีเซิร์ฟเวอร์ในเครื่อง แทนที่จะต้องทราบที่อยู่และพอร์ต เพียงแต่ต้องทราบหมายเลขพอร์ตของบริการและเชื่อมต่อผ่าน localhost
. พร็อกซีในเครื่องจะจัดการการเชื่อมต่อนี้และส่งต่อไปยังเซิร์ฟเวอร์จริง ตอนนี้เมื่อย้ายแบ็กเอนด์ไปยังเครื่องอื่นหรือปรับขนาด แทนที่จะอัปเดตไคลเอนต์ทั้งหมด เฉพาะพรอกซีในเครื่องเหล่านี้ทั้งหมดเท่านั้นที่ต้องได้รับการอัปเดต และไม่จำเป็นต้องรีบูตอีกต่อไป
(นอกจากนี้ยังมีการวางแผนที่จะห่อหุ้มการรับส่งข้อมูลในการเชื่อมต่อ TLS และติดตั้งพร็อกซีเซิร์ฟเวอร์อื่นที่ฝั่งรับ รวมถึงตรวจสอบใบรับรอง TLS โดยไม่ต้องมีส่วนร่วมของบริการรับซึ่งได้รับการกำหนดค่าให้ยอมรับการเชื่อมต่อเฉพาะบน localhost
. เพิ่มเติมในภายหลัง)
นี่จะคล้ายกันมากกับ
โดยส่วนตัวแล้วฉันถือว่า SmartStack เป็นหนึ่งในระบบรุ่นก่อนๆ เช่น Istio, Linkerd และ Consul Connect เนื่องจากระบบทั้งหมดมีรูปแบบเดียวกัน:
- เรียกใช้พรอกซีบนแต่ละโหนด
- ลูกค้าเชื่อมต่อกับพร็อกซี
- เครื่องบินควบคุมจะอัปเดตการกำหนดค่าพร็อกซีเมื่อแบ็กเอนด์มีการเปลี่ยนแปลง
- … กำไร!
การใช้งาน Service Mesh ที่ทันสมัย
หากเราจำเป็นต้องใช้กริดที่คล้ายกันในวันนี้ เราก็สามารถใช้หลักการที่คล้ายกันได้ ตัวอย่างเช่น ตั้งค่าโซน DNS ภายในโดยการจับคู่ชื่อบริการกับที่อยู่ในช่องว่าง 127.0.0.0/8
. จากนั้นรัน HAProxy บนแต่ละโหนดคลัสเตอร์ โดยยอมรับการเชื่อมต่อบนที่อยู่บริการแต่ละรายการ (บนเครือข่ายย่อยนั้น 127.0.0.0/8
) และเปลี่ยนเส้นทาง/ปรับสมดุลโหลดไปยังแบ็กเอนด์ที่เหมาะสม สามารถจัดการการกำหนดค่า HAProxy ได้
นี่เป็นวิธีการทำงานของ Istio ที่ค่อนข้างมาก! แต่มีความแตกต่างบางประการ:
- การใช้งาน
ผู้รับมอบฉันทะ แทน HAProxy - จัดเก็บการกำหนดค่าแบ็กเอนด์ผ่าน Kubernetes API แทน etcd หรือ Consul
- บริการต่างๆ ได้รับการจัดสรรที่อยู่ในซับเน็ตภายใน (ที่อยู่ Kubernetes ClusterIP) แทนที่จะเป็น 127.0.0.0/8
- มีองค์ประกอบเพิ่มเติม (Citadel) เพื่อเพิ่มการตรวจสอบความถูกต้อง TLS ร่วมกันระหว่างไคลเอนต์และเซิร์ฟเวอร์
- รองรับคุณสมบัติใหม่ เช่น การทำลายวงจร การติดตามแบบกระจาย การใช้งานคานารี ฯลฯ
ลองมาดูความแตกต่างบางประการอย่างรวดเร็ว
ผู้รับมอบฉันทะ
Envoy Proxy เขียนโดย Lyft [คู่แข่งของ Uber ในตลาดรถแท็กซี่ - ประมาณ ต่อ.]. มันคล้ายกับพรอกซีอื่นๆ ในหลาย ๆ ด้าน (เช่น HAProxy, Nginx, Traefik...) แต่ Lyft เขียนขึ้นมาเองเนื่องจากพวกเขาต้องการคุณสมบัติที่พรอกซีอื่นไม่มี และดูเหมือนว่าจะสมเหตุสมผลกว่าที่จะสร้างใหม่ แทนที่จะขยาย อันที่มีอยู่
เอกอัครราชทูตสามารถใช้งานได้เอง หากฉันมีบริการเฉพาะที่ต้องการเชื่อมต่อกับบริการอื่น ฉันสามารถตั้งค่าให้เชื่อมต่อกับ Envoy จากนั้นกำหนดค่าและกำหนดค่า Envoy แบบไดนามิกด้วยตำแหน่งของบริการอื่น ๆ ในขณะที่ได้รับความพิเศษที่ยอดเยี่ยมมากมาย เช่น การมองเห็น แทนที่จะใช้ไลบรารีไคลเอ็นต์แบบกำหนดเองหรือแทรกโค้ดติดตามการโทร เราจะส่งการรับส่งข้อมูลไปยัง Envoy และจะรวบรวมการวัดให้เรา
แต่ทูตยังสามารถทำงานเป็น เครื่องบินข้อมูล (ระนาบข้อมูล) สำหรับเซอร์วิสเมช ซึ่งหมายความว่าขณะนี้สำหรับ Service Mesh นี้ Envoy ได้รับการกำหนดค่าแล้ว เครื่องบินควบคุม (เครื่องบินควบคุม)
เครื่องบินควบคุม
ในระนาบควบคุม Istio อาศัย Kubernetes API นี่ก็ไม่แตกต่างจากการใช้ confd มากนักซึ่งอาศัย etcd หรือ Consul ในการค้นหาชุดคีย์ในที่เก็บข้อมูล Istio ตรวจสอบชุดทรัพยากร Kubernetes ผ่าน Kubernetes API
ระหว่างนี้กับต่อจากนั้น: โดยส่วนตัวแล้วฉันพบว่าสิ่งนี้มีประโยชน์
เซิร์ฟเวอร์ Kubernetes API คือ "เซิร์ฟเวอร์โง่ๆ" ที่นำเสนอพื้นที่เก็บข้อมูล การกำหนดเวอร์ชัน การตรวจสอบความถูกต้อง การอัปเดต และความหมายของทรัพยากร API
Istio ได้รับการออกแบบมาเพื่อทำงานร่วมกับ Kubernetes และหากคุณต้องการใช้นอก Kubernetes คุณจะต้องเริ่มต้นอินสแตนซ์ของเซิร์ฟเวอร์ Kubernetes API (และบริการตัวช่วย ฯลฯ )
ที่อยู่ที่ให้บริการ
Istio อาศัยที่อยู่ ClusterIP ที่ Kubernetes จัดสรร ดังนั้นบริการ Istio จึงได้รับที่อยู่ภายใน (ไม่อยู่ในช่วง 127.0.0.0/8
).
การรับส่งข้อมูลไปยังที่อยู่ ClusterIP สำหรับบริการเฉพาะในคลัสเตอร์ Kubernetes ที่ไม่มี Istio จะถูกขัดขวางโดย kube-proxy และส่งไปยังส่วนหลังของพร็อกซี หากคุณสนใจในรายละเอียดด้านเทคนิค kube-proxy จะตั้งค่ากฎ iptables (หรือตัวจัดสรรภาระงาน IPVS ขึ้นอยู่กับการกำหนดค่า) เพื่อเขียนที่อยู่ IP ปลายทางของการเชื่อมต่อที่ไปยังที่อยู่ ClusterIP ใหม่
เมื่อติดตั้ง Istio บนคลัสเตอร์ Kubernetes แล้ว จะไม่มีการเปลี่ยนแปลงใดๆ จนกว่าจะเปิดใช้งานอย่างชัดเจนสำหรับผู้ใช้บริการที่กำหนด หรือแม้แต่เนมสเปซทั้งหมด โดยการแนะนำคอนเทนเนอร์ sidecar
ไปยังพ็อดแบบกำหนดเอง คอนเทนเนอร์นี้จะเริ่มอินสแตนซ์ Envoy และตั้งค่าชุดกฎ iptables เพื่อสกัดกั้นการรับส่งข้อมูลที่ไปยังบริการอื่น ๆ และเปลี่ยนเส้นทางการรับส่งข้อมูลนั้นไปยัง Envoy
เมื่อรวมเข้ากับ Kubernetes DNS หมายความว่าโค้ดของเราสามารถเชื่อมต่อตามชื่อบริการได้ และทุกอย่าง "ใช้งานได้" กล่าวอีกนัยหนึ่ง รหัสของเราจะถามคำถามเช่น http://api/v1/users/4242
แล้ว api
แก้ไขคำขอสำหรับ 10.97.105.48
กฎ iptables จะสกัดกั้นการเชื่อมต่อจาก 10.97.105.48 และเปลี่ยนเส้นทางไปยังพร็อกซี Envoy ในเครื่อง ซึ่งจะส่งต่อคำขอไปยังแบ็กเอนด์ API จริง วุ้ย
จีบเพิ่มเติม
Istio ยังมีการเข้ารหัสและการตรวจสอบสิทธิ์จากต้นทางถึงปลายทางผ่าน mTLS (TLS รวม) ส่วนประกอบที่เรียกว่า ป้อมปราการ.
ก็ยังมีองค์ประกอบ เครื่องผสมซึ่งทูตสามารถขอได้ แต่ละ ขอให้ตัดสินใจพิเศษเกี่ยวกับคำขอนั้นขึ้นอยู่กับปัจจัยต่าง ๆ เช่น ส่วนหัว การโหลดแบ็กเอนด์ ฯลฯ ... (ไม่ต้องกังวล: มีเครื่องมือมากมายที่ทำให้ Mixer ทำงานต่อไปได้ และถึงแม้จะล่มก็ตาม Envoy ก็ยังทำงานต่อไป เป็นตัวแทน)
และแน่นอนว่า เราได้กล่าวถึงการเปิดเผยข้อมูล: Envoy รวบรวมตัวชี้วัดจำนวนมากในขณะที่ให้การติดตามแบบกระจาย ในสถาปัตยกรรมไมโครเซอร์วิส หากคำขอ API เดียวจำเป็นต้องผ่านไมโครเซอร์วิส A, B, C และ D จากนั้นเมื่อเข้าสู่ระบบ การติดตามแบบกระจายจะเพิ่มตัวระบุเฉพาะให้กับคำขอและจัดเก็บตัวระบุนี้ผ่านคำขอย่อยไปยังไมโครเซอร์วิสเหล่านี้ทั้งหมด ซึ่งช่วยให้ คุณสามารถบันทึกการโทรที่เกี่ยวข้องทั้งหมด ความล่าช้า ฯลฯ
พัฒนาหรือซื้อ
Istio มีชื่อเสียงว่าเป็นระบบที่ซับซ้อน ในทางตรงกันข้าม การสร้าง Routing Mesh ที่ฉันอธิบายไว้ตอนต้นของโพสต์นี้ค่อนข้างง่ายด้วยเครื่องมือที่มีอยู่ แล้วมันสมเหตุสมผลไหมที่จะสร้าง service mesh ของคุณเองแทน?
หากเรามีความต้องการเพียงเล็กน้อย (เราไม่ต้องการการมองเห็น เบรกเกอร์ และรายละเอียดปลีกย่อยอื่นๆ) ความคิดก็จะเกิดขึ้นเกี่ยวกับการพัฒนาเครื่องมือของเราเอง แต่หากเราใช้ Kubernetes ก็อาจไม่จำเป็นด้วยซ้ำ เนื่องจาก Kubernetes มีเครื่องมือพื้นฐานสำหรับการค้นหาบริการและการปรับสมดุลโหลดอยู่แล้ว
แต่หากเรามีข้อกำหนดขั้นสูง การ "ซื้อ" โครงข่ายบริการก็ดูเหมือนเป็นตัวเลือกที่ดีกว่ามาก (นี่ไม่ใช่ "การซื้อ" เสมอไปเนื่องจาก Istio เป็นโอเพ่นซอร์ส แต่เรายังต้องใช้เวลาด้านวิศวกรรมเพื่อทำความเข้าใจ ปรับใช้ และจัดการ)
มีอะไรให้เลือก: Istio, Linkerd หรือ Consul Connect?
จนถึงตอนนี้เราได้พูดคุยเกี่ยวกับ Istio เท่านั้น แต่ไม่ใช่บริการเดียวเท่านั้น ทางเลือกที่นิยมคือ
สิ่งที่ต้องเลือก?
พูดตามตรงฉันไม่รู้ ในขณะนี้ฉันไม่คิดว่าตัวเองมีความสามารถเพียงพอที่จะตอบคำถามนี้ มีไม่กี่อย่าง
แนวทางหนึ่งที่มีแนวโน้มดีคือการใช้เครื่องมือเช่น
ฉันเล่นกับ Istio และ SuperGloo นิดหน่อยและในบทความถัดไปฉันต้องการแสดงวิธีเพิ่ม Istio หรือ Linkerd ให้กับคลัสเตอร์ที่มีอยู่โดยใช้ SuperGloo และวิธีหลังจะทำงานของมันได้อย่างไรนั่นคือมันช่วยให้คุณเปลี่ยนจาก บริการหนึ่งไปยังอีกบริการหนึ่งโดยไม่ต้องเขียนการกำหนดค่าใหม่
ที่มา: will.com