Postgres วันอังคารที่ 5: “PostgreSQL และ Kubernetes ซีไอ/ซีดี ทดสอบระบบอัตโนมัติ"

Postgres วันอังคารที่ 5: “PostgreSQL และ Kubernetes ซีไอ/ซีดี ทดสอบระบบอัตโนมัติ"

เมื่อปลายปีที่แล้ว มีการถ่ายทอดสดชุมชน PostgreSQL ของรัสเซียอีกครั้ง #RuPostgresในระหว่างนั้น ผู้ร่วมก่อตั้ง Nikolai Samokhvalov ได้พูดคุยกับผู้อำนวยการด้านเทคนิคของ Flant Dmitry Stolyarov เกี่ยวกับ DBMS นี้ในบริบทของ Kubernetes

เรากำลังเผยแพร่สำเนาส่วนหลักของการสนทนานี้และที่ ช่อง YouTube ของชุมชน โพสต์วิดีโอฉบับเต็ม:

ฐานข้อมูลและ Kubernetes

НС: เราจะไม่พูดถึง VACUUM และ CHECKPOINT วันนี้ เราต้องการพูดคุยเกี่ยวกับ Kubernetes ฉันรู้ว่าคุณมีประสบการณ์หลายปี ฉันดูวิดีโอของคุณและแม้แต่ดูซ้ำบางส่วนด้วยซ้ำ... มาเข้าประเด็นกันดีกว่า: ทำไมจึงต้องใช้ Postgres หรือ MySQL ใน K8

DS: ไม่มีและไม่สามารถให้คำตอบที่ชัดเจนสำหรับคำถามนี้ได้ แต่โดยรวมแล้วนี่คือความเรียบง่ายและสะดวกสบาย...มีศักยภาพ ทุกคนต้องการบริการที่มีการจัดการ

НС: ทำอย่างไร RDS, แค่ที่บ้านเหรอ?

DS: ใช่: เช่นเดียวกับ RDS ทุกที่

НС: “ทุกที่” เป็นจุดที่ดี ในบริษัทขนาดใหญ่ ทุกอย่างจะตั้งอยู่ในที่ต่างกัน แล้วทำไมถ้าเป็นบริษัทใหญ่ๆ จึงไม่เลือกใช้โซลูชั่นสำเร็จรูปล่ะ? ตัวอย่างเช่น Nutanix มีการพัฒนาของตัวเอง บริษัทอื่นๆ (VMware...) ก็มี “RDS เหมือนกัน ใช้ได้ที่บ้านเท่านั้น”

DS: แต่เรากำลังพูดถึงการใช้งานแยกต่างหากซึ่งจะใช้งานได้ภายใต้เงื่อนไขบางประการเท่านั้น และหากเรากำลังพูดถึง Kubernetes ก็จะมีโครงสร้างพื้นฐานที่หลากหลายมาก (ซึ่งอาจอยู่ใน K8) โดยพื้นฐานแล้ว นี่คือมาตรฐานสำหรับ API ไปยังระบบคลาวด์...

НС: นอกจากนี้ยังฟรี!

DS: มันไม่สำคัญเท่าไหร่ ความเป็นอิสระเป็นสิ่งสำคัญสำหรับตลาดที่มีขนาดไม่ใหญ่นัก อีกอย่างที่สำคัญ...คุณคงจำรายงานได้”ฐานข้อมูลและ Kubernetes"?

НС: ใช่.

DS: ฉันรู้ว่ามันถูกรับอย่างคลุมเครือมาก บางคนคิดว่าฉันกำลังพูดว่า: "พวกเรา เอาฐานข้อมูลทั้งหมดไปที่ Kubernetes กันเถอะ!" ในขณะที่บางคนตัดสินใจว่าสิ่งเหล่านี้ล้วนเป็นจักรยานที่แย่มาก แต่ฉันอยากจะพูดอะไรบางอย่างที่แตกต่างไปจากเดิมอย่างสิ้นเชิง: “ดูว่าเกิดอะไรขึ้น มีปัญหาอะไรบ้าง และจะแก้ไขได้อย่างไร เราควรใช้ฐานข้อมูล Kubernetes ตอนนี้หรือไม่? การผลิต? ก็ต่อเมื่อคุณชอบ...ทำบางสิ่ง แต่สำหรับนักพัฒนา ผมบอกได้เลยว่าผมแนะนำครับ สำหรับนักพัฒนา พลวัตของการสร้าง/การลบสภาพแวดล้อมมีความสำคัญมาก”

NS: โดย dev คุณหมายถึงสภาพแวดล้อมทั้งหมดที่ไม่ใช่ผลิตภัณฑ์ใช่ไหม การจัดเตรียม QA...

DS: หากเรากำลังพูดถึง perf stand ก็คงไม่ใช่ เพราะข้อกำหนดนั้นมีเฉพาะเจาะจง หากเรากำลังพูดถึงกรณีพิเศษที่จำเป็นต้องใช้ฐานข้อมูลขนาดใหญ่มากสำหรับการจัดเตรียม ก็อาจจะไม่... หากนี่เป็นสภาพแวดล้อมแบบคงที่และมีอายุการใช้งานยาวนาน การมีฐานข้อมูลอยู่ใน K8 มีประโยชน์อย่างไร

НС: ไม่มี. แต่เราจะมองเห็นสภาพแวดล้อมแบบคงที่ได้ที่ไหน? สภาพแวดล้อมแบบคงที่จะล้าสมัยในวันพรุ่งนี้

DS: การจัดเตรียมสามารถเป็นแบบคงที่ได้ เรามีลูกค้า...

НС: ใช่ ฉันมีอันหนึ่งด้วย มันจะเป็นปัญหาใหญ่หากคุณมีฐานข้อมูลขนาด 10 TB และความจุ 200 GB...

DS: มีเคสเด็ดมาก! ในการจัดเตรียมจะมีฐานข้อมูลผลิตภัณฑ์ที่ทำการเปลี่ยนแปลง และมีปุ่ม: “เปิดตัวสู่การใช้งานจริง” การเปลี่ยนแปลงเหล่านี้ - เดลต้า - ถูกเพิ่มเข้ามา (ดูเหมือนว่าจะซิงโครไนซ์ผ่าน API) ในการผลิต นี่เป็นตัวเลือกที่แปลกใหม่มาก

НС: ฉันเคยเห็นบริษัทสตาร์ทอัพในหุบเขาที่อยู่ใน RDS หรือแม้แต่ใน Heroku ซึ่งเป็นเรื่องราวเมื่อ 2-3 ปีที่แล้ว และพวกเขาดาวน์โหลดดัมพ์ลงในแล็ปท็อปของพวกเขา เนื่องจากฐานข้อมูลยังคงมีเพียง 80 GB และมีพื้นที่บนแล็ปท็อป จากนั้นพวกเขาก็ซื้อดิสก์เพิ่มเติมสำหรับทุกคนเพื่อให้พวกเขามี 3 ฐานข้อมูลเพื่อดำเนินการพัฒนาที่แตกต่างกัน นี่คือวิธีที่มันเกิดขึ้นเช่นกัน ฉันยังเห็นว่าพวกเขาไม่กลัวที่จะคัดลอกผลิตภัณฑ์ไปเป็นการแสดงละคร - มันขึ้นอยู่กับบริษัทเป็นอย่างมาก แต่ฉันยังเห็นว่าพวกเขากลัวมาก และมักมีเวลาและมือไม่เพียงพอ แต่ก่อนที่เราจะพูดถึงหัวข้อนี้ ฉันต้องการทราบเกี่ยวกับ Kubernetes ก่อน ฉันเข้าใจถูกต้องหรือไม่ว่ายังไม่มีใครอยู่ในผลิตภัณฑ์?

DS: เรามีฐานข้อมูลขนาดเล็กในผลิตภัณฑ์ เรากำลังพูดถึงปริมาณหลายสิบกิกะไบต์และบริการที่ไม่สำคัญซึ่งเราขี้เกียจเกินไปที่จะสร้างแบบจำลอง (และไม่มีความจำเป็นเช่นนั้น) และมีที่เก็บข้อมูลปกติภายใต้ Kubernetes ฐานข้อมูลนี้ทำงานในเครื่องเสมือน - ตามเงื่อนไขใน VMware ที่ด้านบนของระบบจัดเก็บข้อมูล เราก็ใส่มันเข้าไป PV และตอนนี้เราสามารถถ่ายโอนจากเครื่องหนึ่งไปอีกเครื่องหนึ่งได้

НС: ฐานข้อมูลขนาดนี้ สูงสุด 100 GB สามารถเผยแพร่บนดิสก์ที่ดีและเครือข่ายที่ดีได้ภายในไม่กี่นาที ใช่ไหม? ความเร็ว 1 GB ต่อวินาทีไม่ใช่เรื่องแปลกใหม่อีกต่อไป

DS: ใช่ สำหรับการทำงานเชิงเส้น นี่ไม่ใช่ปัญหา

НС: โอเค เราแค่ต้องคิดถึงผลิตภัณฑ์ และหากเรากำลังพิจารณา Kubernetes สำหรับสภาพแวดล้อมที่ไม่ใช่ผลิตภัณฑ์ เราควรทำอย่างไร ฉันเห็นสิ่งนั้นในซาลันโด ทำโอเปอเรเตอร์, ในกรุบกรอบ เลื่อยมีตัวเลือกอื่นอยู่บ้าง และมี ออนเกรส - นี่คือเพื่อนที่ดีของเรา อัลวาโร จากสเปน สิ่งที่พวกเขาทำไม่ใช่แค่เพียงเท่านั้น ผู้ประกอบการและการกระจายทั้งหมด (สแต็คเกรส) ซึ่งนอกเหนือจาก Postgres แล้ว พวกเขายังตัดสินใจที่จะสำรองข้อมูลด้วย พร็อกซี Envoy...

DS: ทูตเพื่ออะไร? ปรับสมดุลการรับส่งข้อมูล Postgres โดยเฉพาะหรือไม่

НС: ใช่. นั่นคือพวกเขามองว่าเป็น: หากคุณใช้การแจกจ่าย Linux และเคอร์เนล ดังนั้น PostgreSQL ปกติจะเป็นเคอร์เนล และพวกเขาต้องการสร้างการแจกจ่ายที่จะเป็นมิตรกับระบบคลาวด์และทำงานบน Kubernetes พวกเขารวบรวมส่วนประกอบต่างๆ (การสำรองข้อมูล ฯลฯ) และแก้ไขจุดบกพร่องเพื่อให้ทำงานได้ดี

DS: เจ๋งมาก! โดยพื้นฐานแล้ว นี่คือซอฟต์แวร์สำหรับสร้าง Postgres ที่มีการจัดการของคุณเอง

НС: การแจกแจง Linux มีปัญหานิรันดร์: วิธีสร้างไดรเวอร์เพื่อให้รองรับฮาร์ดแวร์ทั้งหมด และพวกเขามีความคิดที่จะทำงานใน Kubernetes ฉันรู้ว่าในตัวดำเนินการ Zalando เราเพิ่งเห็นการเชื่อมต่อกับ AWS และสิ่งนี้ไม่ดีอีกต่อไป ไม่ควรมีความเชื่อมโยงกับโครงสร้างพื้นฐานเฉพาะ - แล้วประเด็นคืออะไร?

DS: ฉันไม่รู้แน่ชัดว่า Zalando ตกอยู่ในสถานการณ์ใด แต่ตอนนี้พื้นที่เก็บข้อมูล Kubernetes ถูกสร้างขึ้นในลักษณะที่เป็นไปไม่ได้ที่จะสำรองข้อมูลดิสก์โดยใช้วิธีการทั่วไป ล่าสุดในมาตรฐาน - ในเวอร์ชันล่าสุด ข้อมูลจำเพาะของซีเอสไอ — เราทำให้สแน็ปช็อตเป็นไปได้ แต่จะนำไปใช้ที่ไหน? จริงๆ แล้ว ทุกอย่างยังดิบอยู่มาก... เรากำลังลองใช้ CSI ที่ด้านบนของ AWS, GCE, Azure, vSphere แต่ทันทีที่คุณเริ่มใช้งาน คุณจะเห็นว่ามันยังไม่พร้อม

НС: นั่นเป็นสาเหตุที่บางครั้งเราต้องพึ่งพาโครงสร้างพื้นฐาน ฉันคิดว่านี่ยังอยู่ในช่วงเริ่มต้น - ความเจ็บปวดที่เพิ่มมากขึ้น คำถาม: คุณจะให้คำแนะนำอะไรแก่มือใหม่ที่ต้องการลองใช้ PgSQL ใน K8 บางทีโอเปอเรเตอร์คนไหน?

DS: ปัญหาคือ Postgres อยู่ที่ 3% สำหรับเรา เรายังมีรายการซอฟต์แวร์ต่างๆ จำนวนมากใน Kubernetes ฉันจะไม่แสดงรายการทั้งหมดด้วยซ้ำ ตัวอย่างเช่น Elasticsearch มีผู้ปฏิบัติงานจำนวนมาก: บางคนกำลังพัฒนาอย่างแข็งขัน บางคนไม่ได้เป็นเช่นนั้น เราได้ร่างข้อกำหนดสำหรับตัวเราเองเกี่ยวกับสิ่งที่ผู้ปฏิบัติงานควรมีเพื่อให้เราดำเนินการอย่างจริงจัง ในโอเปอเรเตอร์สำหรับ Kubernetes โดยเฉพาะ - ไม่ใช่ใน "โอเปอเรเตอร์เพื่อทำบางสิ่งในเงื่อนไขของ Amazon"... อันที่จริง เราค่อนข้างแพร่หลาย (= ลูกค้าเกือบทั้งหมด) ใช้โอเปอเรเตอร์เดียว - สำหรับเรดิส (เราจะเผยแพร่บทความเกี่ยวกับเขาเร็ว ๆ นี้).

НС: และไม่ใช่สำหรับ MySQL เช่นกัน? ฉันรู้ว่า Percona... เนื่องจากตอนนี้พวกเขากำลังทำงานกับ MySQL, MongoDB และ Postgres พวกเขาจะต้องสร้างโซลูชันสากลบางประเภท: สำหรับฐานข้อมูลทั้งหมด สำหรับผู้ให้บริการคลาวด์ทั้งหมด

DS: เราไม่มีเวลาดูโอเปอเรเตอร์สำหรับ MySQL นี่ไม่ใช่จุดสนใจหลักของเราในขณะนี้ MySQL ทำงานได้ดีในแบบสแตนด์อโลน เหตุใดจึงต้องใช้โอเปอเรเตอร์หากคุณสามารถเปิดฐานข้อมูลได้... คุณสามารถเปิดคอนเทนเนอร์ Docker ด้วย Postrges หรือคุณสามารถเปิดใช้งานด้วยวิธีง่ายๆ

НС: มีคำถามเกี่ยวกับเรื่องนี้ด้วย ไม่มีโอเปอเรเตอร์เลยเหรอ?

DS: ใช่ พวกเรา 100% มี PostgreSQL ทำงานโดยไม่มีโอเปอเรเตอร์ จนถึงตอนนี้ เราใช้โอเปอเรเตอร์สำหรับ Prometheus และ Redis อย่างแข็งขัน เรามีแผนจะหาโอเปอเรเตอร์สำหรับ Elasticsearch ซึ่งเป็นโอเปอเรเตอร์ที่ “ลุกลาม” ที่สุด เพราะเราต้องการติดตั้งใน Kubernetes ในกรณี 100% เช่นเดียวกับที่เราต้องการให้แน่ใจว่า MongoDB ได้รับการติดตั้งใน Kubernetes อยู่เสมอ ความปรารถนาบางอย่างปรากฏขึ้นที่นี่ - มีความรู้สึกว่าในกรณีเหล่านี้บางสิ่งสามารถทำได้ และเราไม่ได้ดู Postgres ด้วยซ้ำ แน่นอนว่าเรารู้ว่ามีตัวเลือกที่แตกต่างกัน แต่จริงๆ แล้ว เรามีแบบสแตนด์อโลน

ฐานข้อมูลสำหรับการทดสอบใน Kubernetes

НС: มาดูหัวข้อการทดสอบกันดีกว่า วิธีเผยแพร่การเปลี่ยนแปลงในฐานข้อมูล - จากมุมมองของ DevOps มีไมโครเซอร์วิส ฐานข้อมูลมากมาย มีบางอย่างเปลี่ยนแปลงอยู่ตลอดเวลา วิธีตรวจสอบให้แน่ใจว่า CI/CD ปกติเพื่อให้ทุกอย่างเป็นไปตามลำดับจากมุมมองของ DBMS คุณมีแนวทางอย่างไร?

DS: ไม่มีคำตอบเดียว มีหลายตัวเลือก อย่างแรกคือขนาดของฐานที่เราต้องการแผ่ออก คุณเองก็พูดถึงว่าบริษัทต่างๆ มีทัศนคติที่แตกต่างกันต่อการมีสำเนาฐานข้อมูลผลิตภัณฑ์ทั้งในด้านการพัฒนาและขั้นตอน

НС: และภายใต้เงื่อนไขของ GDPR ฉันคิดว่าพวกเขากำลังระมัดระวังมากขึ้นเรื่อยๆ... ฉันสามารถพูดได้ว่าในยุโรปพวกเขาเริ่มเรียกเก็บค่าปรับแล้ว

DS: แต่บ่อยครั้งที่คุณสามารถเขียนซอฟต์แวร์ที่รับดัมพ์จากการผลิตและทำให้สับสนได้ ได้รับข้อมูลผลิตภัณฑ์ (สแนปชอต ดัมพ์ สำเนาไบนารี...) แต่ไม่มีการระบุชื่อ อาจมีสคริปต์การสร้างแทน: สิ่งเหล่านี้อาจเป็นโปรแกรมติดตั้งหรือเพียงสคริปต์ที่สร้างฐานข้อมูลขนาดใหญ่ ปัญหาคือ: ใช้เวลานานเท่าใดในการสร้างอิมเมจพื้นฐาน? และต้องใช้เวลานานเท่าใดในการปรับใช้ในสภาพแวดล้อมที่ต้องการ?

เรามาถึงรูปแบบ: หากไคลเอนต์มีชุดข้อมูลคงที่ (ฐานข้อมูลเวอร์ชันขั้นต่ำ) เราจะใช้ข้อมูลเหล่านั้นตามค่าเริ่มต้น หากเรากำลังพูดถึงสภาพแวดล้อมการตรวจสอบ เมื่อเราสร้างสาขา เราได้ปรับใช้อินสแตนซ์ของแอปพลิเคชัน - เราจะเปิดตัวฐานข้อมูลขนาดเล็กที่นั่น แต่มันก็ออกมาดี การเลือกเมื่อเราถ่ายโอนข้อมูลจากการผลิตวันละครั้ง (ตอนกลางคืน) และสร้างคอนเทนเนอร์ Docker ด้วย PostgreSQL และ MySQL โดยมีข้อมูลที่โหลดตามข้อมูลดังกล่าว หากคุณต้องการขยายฐานข้อมูล 50 เท่าจากรูปภาพนี้ ทำได้ค่อนข้างง่ายและรวดเร็ว

НС: โดยการคัดลอกง่ายๆ?

DS: ข้อมูลจะถูกจัดเก็บโดยตรงในอิมเมจ Docker เหล่านั้น. เรามีรูปภาพสำเร็จรูปแม้ว่าจะมีขนาด 100 GB ด้วยเลเยอร์ใน Docker เราจึงสามารถปรับใช้อิมเมจนี้ได้อย่างรวดเร็วบ่อยเท่าที่ต้องการ วิธีการนี้โง่ แต่ก็ใช้ได้ผลดี

НС: แล้วพอทดสอบแล้วมันเปลี่ยนข้างใน Docker เลยใช่ไหม? คัดลอกเมื่อเขียนภายใน Docker - ทิ้งมันไปและไปอีกครั้งทุกอย่างเรียบร้อยดี ระดับ! และคุณได้ใช้มันอย่างเต็มที่แล้วหรือยัง?

DS: เป็นเวลานาน.

НС: เราทำสิ่งที่คล้ายกันมาก มีเพียงเราเท่านั้นที่ไม่ได้ใช้การคัดลอกเมื่อเขียนของ Docker แต่เป็นอย่างอื่น

DS: มันไม่ธรรมดา. และนักเทียบท่าก็ทำงานได้ทุกที่

НС: ตามทฤษฎีแล้วใช่ แต่เราก็มีโมดูลอยู่ที่นั่นด้วย คุณสามารถสร้างโมดูลที่แตกต่างกันและทำงานกับระบบไฟล์ที่แตกต่างกันได้ สักครู่นี้ครับ. จากฝั่ง Postgres เรามองทั้งหมดนี้แตกต่างออกไป ตอนนี้ฉันมองจากฝั่งนักเทียบท่าและเห็นว่าทุกอย่างเหมาะกับคุณ แต่ถ้าฐานข้อมูลมีขนาดใหญ่ เช่น 1 TB ทั้งหมดนี้ก็จะใช้เวลานาน: การทำงานในเวลากลางคืนและการบรรจุทุกอย่างลงใน Docker... และถ้า 5 TB ถูกยัดลงใน Docker... หรือทุกอย่างเรียบร้อยดี?

DS: ความแตกต่างคืออะไร: สิ่งเหล่านี้คือหยด เป็นเพียงบิตและไบต์

НС: ความแตกต่างคือ: คุณทำผ่านการดัมพ์และกู้คืนหรือไม่?

DS: ไม่จำเป็นเลย. วิธีการสร้างภาพนี้อาจแตกต่างกัน

НС: สำหรับลูกค้าบางราย เราได้จัดทำขึ้นเพื่อที่แทนที่จะสร้างอิมเมจพื้นฐานเป็นประจำ เราจึงคอยอัปเดตอยู่เสมอ โดยพื้นฐานแล้วมันเป็นแบบจำลอง แต่ได้รับข้อมูลไม่ได้มาจากต้นแบบโดยตรง แต่ผ่านทางไฟล์เก็บถาวร ไฟล์เก็บถาวรแบบไบนารีที่มีการดาวน์โหลด WAL ทุกวัน โดยมีการสำรองข้อมูล... จากนั้น WAL เหล่านี้จะไปถึงอิมเมจพื้นฐานโดยมีความล่าช้าเล็กน้อย (ตามตัวอักษร 1-2 วินาที) เราโคลนจากมัน แต่อย่างใด - ตอนนี้เรามี ZFS เป็นค่าเริ่มต้น

DS: แต่ด้วย ZFS คุณจะถูกจำกัดไว้ที่หนึ่งโหนด

НС: ใช่. แต่ ZFS ก็มีมนต์ขลังเช่นกัน ส่ง: ด้วยมัน คุณสามารถส่งสแน็ปช็อตและแม้แต่ (ฉันยังไม่ได้ทดสอบสิ่งนี้จริงๆ แต่...) คุณสามารถส่งเดลต้าระหว่างสองสิ่งนี้ได้ PGDATA. จริงๆ แล้ว เรามีเครื่องมืออื่นที่เราไม่ได้คำนึงถึงสำหรับงานดังกล่าวจริงๆ PostgreSQL มี pg_rewindซึ่งทำงานเหมือนกับ rsync "อัจฉริยะ" โดยข้ามสิ่งที่คุณไม่ต้องดูไปมาก เนื่องจากไม่มีอะไรเปลี่ยนแปลงในนั้น เราสามารถทำการซิงโครไนซ์อย่างรวดเร็วระหว่างเซิร์ฟเวอร์ทั้งสองและย้อนกลับในลักษณะเดียวกัน

จากนี้ ในด้าน DBA เพิ่มเติม เรากำลังพยายามสร้างเครื่องมือที่ช่วยให้เราทำสิ่งเดียวกับที่คุณพูด: เรามีฐานข้อมูลเดียว แต่เราต้องการทดสอบบางอย่าง 50 ครั้ง เกือบจะพร้อมกัน

DS: 50 ครั้งหมายความว่าคุณต้องสั่งซื้ออินสแตนซ์ Spot 50 รายการ

НС: ไม่ เราทำทุกอย่างด้วยเครื่องเดียว

DS: แต่คุณจะขยาย 50 เท่าได้อย่างไรถ้าฐานข้อมูลเดียวนี้เป็นเทราไบต์ เป็นไปได้มากว่าเธอต้องการ RAM 256 GB แบบมีเงื่อนไขใช่ไหม

НС: ใช่ บางครั้งคุณต้องการหน่วยความจำมาก ซึ่งเป็นเรื่องปกติ แต่นี่คือตัวอย่างจากชีวิต เครื่องที่ใช้งานจริงมี 96 คอร์และ 600 GB ในเวลาเดียวกันฐานข้อมูลจะใช้ 32 คอร์ (บางครั้งถึง 16 คอร์ด้วยซ้ำ) และหน่วยความจำ 100-120 GB

DS: และมี 50 ชุดพอดีเลยเหรอ?

НС: มีเพียงสำเนาเดียวเท่านั้น จากนั้น Copy-on-Write (ZFS) ก็ใช้งานได้... ฉันจะบอกคุณในรายละเอียดเพิ่มเติม

ตัวอย่างเช่น เรามีฐานข้อมูลขนาด 10 TB พวกเขาสร้างดิสก์สำหรับมัน ZFS ยังบีบอัดขนาดของมัน 30-40 เปอร์เซ็นต์ เนื่องจากเราไม่ได้ทำการทดสอบโหลด เวลาตอบสนองที่แน่นอนจึงไม่สำคัญสำหรับเรา ปล่อยให้ช้าลงถึง 2 เท่าก็ไม่เป็นไร

เราให้โอกาสแก่โปรแกรมเมอร์, QA, DBA ฯลฯ ทำการทดสอบใน 1-2 เธรด ตัวอย่างเช่น พวกเขาอาจดำเนินการย้ายข้อมูลบางประเภท ไม่ต้องการ 10 คอร์ในคราวเดียว - ต้องการ 1 แบ็กเอนด์ Postgres, 1 คอร์ การโยกย้ายจะเริ่มขึ้น - อาจจะ เครื่องดูดฝุ่นอัตโนมัติ จะยังคงเริ่มต้นจากนั้นจะใช้แกนที่สอง เรามีการจัดสรรไว้ 16-32 คอร์ ดังนั้น 10 คนจึงสามารถทำงานพร้อมกันได้ไม่มีปัญหา

เพราะว่าทางร่างกาย PGDATA ในทำนองเดียวกันปรากฎว่าเรากำลังหลอกลวง Postgres จริงๆ เคล็ดลับก็คือ: ตัวอย่างเช่น 10 Postgres จะถูกเปิดใช้งานพร้อมกัน ปกติปัญหาคืออะไร? พวกเขาใส่ shared_buffersสมมุติว่า 25% ดังนั้นนี่คือ 200 GB คุณจะไม่สามารถเปิดได้มากกว่าสามรายการเหล่านี้ เนื่องจากหน่วยความจำจะหมด

แต่เมื่อถึงจุดหนึ่งเราก็ตระหนักว่าสิ่งนี้ไม่จำเป็น: ​​เราตั้งค่า shared_buffers เป็น 2 GB PostgreSQL มี มีประสิทธิภาพ_แคช_ขนาดและในความเป็นจริงมันเป็นสิ่งเดียวที่มีอิทธิพล แผนการ. เราตั้งค่าเป็น 0,5 TB และมันไม่สำคัญด้วยซ้ำว่ามันไม่มีอยู่จริง เขาวางแผนราวกับว่าพวกมันมีอยู่จริง

ดังนั้น เมื่อเราทดสอบการย้ายข้อมูลบางประเภท เราก็สามารถรวบรวมแผนทั้งหมดได้ เราจะมาดูกันว่าจะเกิดขึ้นจริงอย่างไร วินาทีจะแตกต่างกัน (ช้ากว่า) แต่ข้อมูลที่เราอ่านจริงและแผนเอง (มี JOIN อะไรบ้าง ฯลฯ ) กลับกลายเป็นเหมือนกับในการใช้งานจริงทุกประการ และคุณสามารถดำเนินการตรวจสอบดังกล่าวหลายรายการพร้อมกันได้ในเครื่องเดียว

DS: คุณไม่คิดว่าจะมีปัญหาเล็กน้อยที่นี่เหรอ? วิธีแรกคือโซลูชันที่ใช้งานได้กับ PostgreSQL เท่านั้น แนวทางนี้เป็นแบบส่วนตัวมาก ไม่ใช่เรื่องทั่วไป ประการที่สองคือ Kubernetes (และทุกสิ่งที่เทคโนโลยีคลาวด์กำลังดำเนินการอยู่) เกี่ยวข้องกับโหนดจำนวนมาก และโหนดเหล่านี้เป็นเพียงชั่วคราว และในกรณีของคุณ มันเป็นโหนดที่มีสถานะถาวร สิ่งเหล่านี้ทำให้ฉันขัดแย้ง

НС: ก่อนอื่น ฉันเห็นด้วย นี่เป็นเรื่องราวของ Postgres ล้วนๆ ฉันคิดว่าถ้าเรามี IO โดยตรงและพูลบัฟเฟอร์สำหรับหน่วยความจำเกือบทั้งหมด วิธีการนี้จะใช้งานไม่ได้ - แผนงานจะแตกต่างออกไป แต่สำหรับตอนนี้เราทำงานร่วมกับ Postgres เท่านั้น เราไม่ได้คิดถึงผู้อื่น

เกี่ยวกับคูเบอร์เนเทส คุณเองบอกเราทุกที่ว่าเรามีฐานข้อมูลถาวร หากอินสแตนซ์ล้มเหลว สิ่งสำคัญคือการบันทึกดิสก์ ที่นี่เรายังมีแพลตฟอร์มทั้งหมดใน Kubernetes และส่วนประกอบที่มี Postgres จะแยกจากกัน (แม้ว่าสักวันหนึ่งจะอยู่ที่นั่นก็ตาม) ดังนั้น ทุกอย่างจึงเป็นเช่นนี้: อินสแตนซ์ล่ม แต่เราบันทึก PV ของมันไว้ และเพียงเชื่อมต่อกับอินสแตนซ์อื่น (ใหม่) ราวกับว่าไม่มีอะไรเกิดขึ้น

DS: จากมุมมองของฉัน เราสร้างพ็อดใน Kubernetes K8s - ยางยืด: จัดเรียงปมตามต้องการ ภารกิจคือเพียงสร้างพ็อดและบอกว่าต้องการทรัพยากรจำนวน X จากนั้น K8 จะคิดออกเอง แต่การรองรับพื้นที่เก็บข้อมูลใน Kubernetes ยังคงไม่เสถียร: 1.16ใน 1.17 (รุ่นนี้ออกแล้ว. ของสัปดาห์ ที่ผ่านมา) คุณสมบัติเหล่านี้กลายเป็นเพียงรุ่นเบต้าเท่านั้น

จะผ่านไปหกเดือนถึงหนึ่งปี - มันจะมีเสถียรภาพไม่มากก็น้อยหรืออย่างน้อยก็จะถูกประกาศเช่นนั้น จากนั้นความเป็นไปได้ของสแนปชอตและการปรับขนาดจะช่วยแก้ปัญหาของคุณได้อย่างสมบูรณ์ เพราะคุณมีพื้นฐานแล้ว ใช่ อาจไม่เร็วมาก แต่ความเร็วขึ้นอยู่กับสิ่งที่ "ซ่อนเร้น" เนื่องจากการใช้งานบางอย่างสามารถคัดลอกและคัดลอกเมื่อเขียนได้ที่ระดับระบบย่อยของดิสก์

НС: จำเป็นสำหรับเอ็นจิ้นทั้งหมด (Amazon, Google...) เพื่อเริ่มรองรับเวอร์ชันนี้ ซึ่งต้องใช้เวลาพอสมควรเช่นกัน

DS: เรายังไม่ได้ใช้มัน. เราใช้ของเรา.

การพัฒนาท้องถิ่นสำหรับ Kubernetes

НС: คุณเคยเจอความปรารถนาเช่นนี้เมื่อคุณต้องการติดตั้งพ็อดทั้งหมดลงในเครื่องเดียวและทำการทดสอบเล็กๆ น้อยๆ เช่นนี้หรือไม่ หากต้องการพิสูจน์แนวคิดอย่างรวดเร็ว โปรดดูว่าแอปพลิเคชันทำงานใน Kubernetes โดยไม่ต้องใช้เครื่องจำนวนมาก มีมินิคูเบด้วยใช่ไหม?

DS: สำหรับฉันแล้วดูเหมือนว่ากรณีนี้ - ใช้งานบนโหนดเดียว - เป็นเรื่องเกี่ยวกับการพัฒนาในท้องถิ่นเท่านั้น หรืออาการบางอย่างของรูปแบบดังกล่าว กิน มินิคุเบะที่นั่น k3s, ชนิด. เรากำลังก้าวไปสู่การใช้ Kubernetes IN Docker ตอนนี้เราเริ่มทำงานกับมันเพื่อทดสอบ

НС: ฉันเคยคิดว่านี่เป็นความพยายามที่จะรวมพ็อดทั้งหมดไว้ในอิมเมจ Docker เดียว แต่ปรากฎว่านี่เป็นเรื่องเกี่ยวกับบางสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิง อย่างไรก็ตาม มีคอนเทนเนอร์แยก พ็อดแยกกัน อยู่ใน Docker เท่านั้น

DS: ใช่. และมีการเลียนแบบที่ค่อนข้างตลก แต่ความหมายก็คือ... เรามียูทิลิตี้สำหรับการปรับใช้ - เวร. เราต้องการทำให้มันเป็นโหมดที่มีเงื่อนไข werf up: “ขอ Kubernetes ในพื้นที่ให้ฉันหน่อย” แล้วรันเงื่อนไขตรงนั้น werf follow. จากนั้นนักพัฒนาจะสามารถแก้ไข IDE ได้ และกระบวนการจะเปิดตัวในระบบที่เห็นการเปลี่ยนแปลงและสร้างอิมเมจขึ้นใหม่ และปรับใช้ซ้ำกับ K8 ในเครื่อง นี่คือวิธีที่เราต้องการพยายามแก้ไขปัญหาการพัฒนาท้องถิ่น

สแนปชอตและการโคลนฐานข้อมูลในความเป็นจริงของ K8

НС: ถ้าเรากลับไปใช้การคัดลอกเมื่อเขียน ฉันสังเกตเห็นว่าเมฆก็มีภาพสแนปชอตด้วย พวกเขาทำงานแตกต่างกัน ตัวอย่างเช่น ใน GCP คุณมีอินสแตนซ์แบบหลายเทราไบต์บนชายฝั่งตะวันออกของสหรัฐอเมริกา คุณถ่ายภาพสแนปชอตเป็นระยะ คุณรับสำเนาของดิสก์บนชายฝั่งตะวันตกจากสแน็ปช็อต - ภายในไม่กี่นาทีทุกอย่างก็พร้อมทำงานเร็วมากเพียงต้องเติมแคชในหน่วยความจำเท่านั้น แต่โคลน (สแนปชอต) เหล่านี้มีไว้เพื่อ 'จัดเตรียม' วอลุ่มใหม่ นี่เป็นเรื่องที่ยอดเยี่ยมเมื่อคุณต้องการสร้างอินสแตนซ์จำนวนมาก

แต่สำหรับการทดสอบ สำหรับฉันแล้วดูเหมือนว่าสแน็ปช็อตที่คุณพูดถึงใน Docker หรือที่ฉันพูดถึงใน ZFS, btrfs และแม้แต่ LVM... - มันทำให้คุณไม่ต้องสร้างข้อมูลใหม่จริงๆ บนเครื่องเดียว ในระบบคลาวด์ คุณจะยังคงจ่ายเงินให้พวกเขาทุกครั้งและรอไม่ใช่วินาทีแต่เป็นนาที (และในกรณีนี้ โหลดขี้เกียจอาจเป็นนาฬิกา)

แต่คุณสามารถรับข้อมูลนี้ได้ภายในหนึ่งหรือสองวินาที ทำการทดสอบแล้วโยนทิ้งไป สแน็ปช็อตเหล่านี้แก้ปัญหาที่แตกต่างกัน ในกรณีแรก - เพื่อขยายขนาดและรับแบบจำลองใหม่ และในกรณีที่สอง - สำหรับการทดสอบ

DS: ฉันไม่เห็นด้วย. การทำให้การโคลนวอลุ่มทำงานอย่างถูกต้องเป็นหน้าที่ของระบบคลาวด์ ฉันไม่ได้ดูการใช้งานของพวกเขา แต่ฉันรู้ว่าเราทำอย่างไรกับฮาร์ดแวร์ เรามี Ceph ซึ่งอนุญาตให้มีฟิสิคัลวอลุ่มใด ๆ (RBD) พูด โคลน และรับโวลุ่มที่สองที่มีลักษณะเหมือนกันในสิบมิลลิวินาที IOPS'อามิ ฯลฯ คุณต้องเข้าใจว่ามีการคัดลอกเมื่อเขียนที่ยุ่งยากอยู่ภายใน ทำไมคลาวด์ไม่ควรทำเช่นเดียวกัน? ฉันแน่ใจว่าพวกเขากำลังพยายามทำเช่นนี้ไม่ทางใดก็ทางหนึ่ง

НС: แต่ก็ยังต้องใช้เวลาไม่กี่วินาทีหรือหลายสิบวินาทีในการยกอินสแตนซ์ นำ Docker ไปที่นั่น ฯลฯ

DS: เหตุใดจึงจำเป็นต้องยกอินสแตนซ์ทั้งหมด? เรามีอินสแตนซ์ที่มี 32 คอร์ 16... และสามารถรองรับได้ เช่น XNUMX คอร์ เมื่อเราสั่งอันที่ห้า อินสแตนซ์จะถูกยกขึ้นแล้ว และจากนั้นก็จะถูกลบ

НС: ใช่ น่าสนใจ Kubernetes กลายเป็นอีกเรื่องหนึ่ง ฐานข้อมูลของเราไม่ได้อยู่ใน K8 และเรามีหนึ่งอินสแตนซ์ แต่การโคลนฐานข้อมูลหลายเทราไบต์ใช้เวลาไม่เกินสองวินาที

DS: นี่มันเยี่ยมมาก แต่จุดเริ่มต้นของฉันคือว่านี่ไม่ใช่วิธีแก้ปัญหาทั่วไป ใช่ มันเยี่ยมมาก แต่เหมาะสำหรับ Postgres และบนโหนดเดียวเท่านั้น

НС: ไม่เพียงแต่เหมาะสำหรับ Postgres เท่านั้น แต่แผนเหล่านี้ตามที่ฉันอธิบายไว้จะใช้งานได้เท่านั้น แต่ถ้าเราไม่สนใจเรื่องแผน และเราแค่ต้องการข้อมูลทั้งหมดสำหรับการทดสอบการทำงาน สิ่งนี้ก็เหมาะสำหรับ DBMS ใดๆ

DS: เมื่อหลายปีก่อนเราทำสิ่งที่คล้ายกันกับสแน็ปช็อต LVM นี่คือคลาสสิก แนวทางนี้ถูกใช้อย่างแข็งขันมาก โหนดที่มีสถานะเป็นเพียงความเจ็บปวด เพราะไม่ควรทิ้งควรจำไว้เสมอ...

НС: คุณเห็นความเป็นไปได้ของไฮบริดที่นี่หรือไม่? สมมติว่า stateful เป็นพ็อดชนิดหนึ่ง ซึ่งใช้ได้กับหลายคน (ผู้ทดสอบหลายคน) เรามีโวลุ่มเดียว แต่ด้วยระบบไฟล์ โคลนจึงอยู่ในเครื่อง หากพ็อดตก แต่ดิสก์ยังคงอยู่ พ็อดจะเพิ่มขึ้น นับข้อมูลเกี่ยวกับโคลนทั้งหมด เลือกทุกอย่างขึ้นมาอีกครั้งแล้วพูดว่า: "นี่คือโคลนของคุณที่ทำงานบนพอร์ตเหล่านี้ ทำงานกับพวกมันต่อไป"

DS: ในทางเทคนิคแล้ว นี่หมายความว่าภายใน Kubernetes เป็นพ็อดเดียวที่เราเรียกใช้ Postgres จำนวนมาก

НС: ใช่. เขามีขีดจำกัด: สมมติว่ามีคนทำงานร่วมกับเขาในเวลาเดียวกันได้ไม่เกิน 10 คน หากคุณต้องการ 20 เราจะเปิดตัวพ็อดดังกล่าวตัวที่สอง เราจะทำการโคลนมันโดยสมบูรณ์ เมื่อได้รับวอลุ่มเต็มครั้งที่สอง มันจะมีโคลน "บาง" 10 โคลนเหมือนกัน คุณไม่เห็นโอกาสนี้เหรอ?

DS: เราจำเป็นต้องเพิ่มปัญหาด้านความปลอดภัยที่นี่ องค์กรประเภทนี้บอกเป็นนัยว่าพ็อดนี้มีสิทธิ์ (ความสามารถ) สูงเนื่องจากสามารถดำเนินการที่ไม่ได้มาตรฐานบนระบบไฟล์... แต่ฉันขอย้ำ: ฉันเชื่อว่าในระยะกลางพวกเขาจะแก้ไขที่เก็บข้อมูลใน Kubernetes และใน เมฆ พวกเขาจะแก้ไขเรื่องราวทั้งหมดด้วยปริมาณ - ทุกอย่างจะ "ได้ผล" จะมีการปรับขนาด การโคลน... มีวอลุ่ม - เราพูดว่า: "สร้างอันใหม่ตามนั้น" และหลังจากนั้นครึ่งวินาทีเราก็ได้สิ่งที่ต้องการ

НС: ฉันไม่เชื่อในหนึ่งวินาทีครึ่งสำหรับหลายเทราไบต์ บน Ceph คุณทำเอง แต่คุณพูดถึงเมฆ ไปที่ระบบคลาวด์ สร้างโคลนโวลุ่ม EBS แบบหลายเทราไบต์บน EC2 และดูว่าประสิทธิภาพจะเป็นอย่างไร ใช้เวลาไม่กี่วินาที ฉันสนใจมากว่าเมื่อไรพวกเขาจะถึงระดับนี้ ฉันเข้าใจสิ่งที่คุณพูด แต่ฉันขอแย้ง

DS: โอเค แต่ผมบอกในระยะกลาง ไม่ใช่ระยะสั้น เป็นเวลาหลายปี.

เกี่ยวกับโอเปอเรเตอร์สำหรับ PostgreSQL จาก Zalando

ในระหว่างการประชุมนี้ Alexey Klyukin อดีตนักพัฒนาจาก Zalando ได้เข้าร่วมและพูดคุยเกี่ยวกับประวัติความเป็นมาของตัวดำเนินการ PostgreSQL:

เป็นเรื่องดีที่มีการกล่าวถึงหัวข้อนี้โดยทั่วไป: ทั้ง Postgres และ Kubernetes เมื่อเราเริ่มทำที่ Zalando ในปี 2017 เป็นหัวข้อที่ทุกคนอยากทำ แต่ไม่มีใครทำ ทุกคนมี Kubernetes อยู่แล้ว แต่เมื่อถามว่าจะทำอย่างไรกับฐานข้อมูล แม้แต่คนก็ชอบด้วย เคลซีย์ ไฮทาวเวอร์ผู้ที่เทศนา K8 พูดประมาณนี้:

“ไปที่บริการที่มีการจัดการแล้วใช้งาน ไม่ต้องรันฐานข้อมูลใน Kubernetes มิฉะนั้น K8 ของคุณจะตัดสินใจ เช่น ทำการอัปเกรด ปิดโหนดทั้งหมด และข้อมูลของคุณจะบินไปไกลแสนไกล”

เราตัดสินใจสร้างโอเปอเรเตอร์ที่จะเปิดตัวฐานข้อมูล Postgres ใน Kubernetes ซึ่งตรงกันข้ามกับคำแนะนำนี้ และเรามีเหตุผลที่ดี - ผู้มีพระคุณ. นี่เป็นการเฟลโอเวอร์อัตโนมัติสำหรับ PostgreSQL ซึ่งทำอย่างถูกต้อง เช่น ใช้ etcd, กงสุล หรือ ZooKeeper เป็นที่เก็บข้อมูลเกี่ยวกับคลัสเตอร์ พื้นที่เก็บข้อมูลดังกล่าวจะให้ทุกคนที่ถามเช่นว่าผู้นำคนปัจจุบันคืออะไรข้อมูลเดียวกัน - แม้ว่าเราจะแจกทุกอย่างแล้วก็ตาม - เพื่อไม่ให้สมองแตกแยก แถมเรามีด้วย ภาพนักเทียบท่า สำหรับเขา.

โดยทั่วไป ความต้องการเฟลโอเวอร์อัตโนมัติของบริษัทปรากฏขึ้นหลังจากย้ายจากศูนย์ข้อมูลฮาร์ดแวร์ภายในไปยังระบบคลาวด์ ระบบคลาวด์ใช้โซลูชัน PaaS (Platform-as-a-Service) ที่เป็นกรรมสิทธิ์ มันเป็นโอเพ่นซอร์ส แต่ต้องใช้เวลาทำงานมากในการเริ่มต้นใช้งาน มันถูกเรียกว่า สตัปส์.

ในตอนแรกไม่มี Kubernetes แม่นยำยิ่งขึ้น เมื่อมีการปรับใช้โซลูชันของเราเอง K8 ก็มีอยู่แล้ว แต่มันหยาบมากจนไม่เหมาะสำหรับการผลิต ในความคิดของฉันคือปี 2015 หรือ 2016 ภายในปี 2017 Kubernetes เติบโตขึ้นไม่มากก็น้อย ดังนั้นจึงมีความจำเป็นต้องย้ายไปยังที่นั่น

และเรามีคอนเทนเนอร์ Docker อยู่แล้ว มี PaaS ที่ใช้ Docker ทำไมไม่ลอง K8s? ทำไมไม่เขียนโอเปอเรเตอร์ของคุณเองล่ะ? Murat Kabilov ซึ่งมาหาเราจาก Avito เริ่มต้นโครงการนี้ด้วยความคิดริเริ่มของเขาเอง - "เล่น" - และโครงการ "เริ่มต้นขึ้น"

แต่โดยทั่วไปแล้ว ฉันอยากจะพูดถึง AWS เหตุใดจึงมีโค้ดที่เกี่ยวข้องกับ AWS ในอดีต...

เมื่อคุณใช้งานบางอย่างใน Kubernetes คุณต้องเข้าใจว่า K8 เป็นงานที่อยู่ระหว่างดำเนินการ มีการพัฒนา ปรับปรุง และพังทลายลงเป็นระยะๆ คุณต้องจับตาดูการเปลี่ยนแปลงทั้งหมดใน Kubernetes อย่างใกล้ชิด คุณต้องพร้อมที่จะเจาะลึกหากมีอะไรเกิดขึ้น และเรียนรู้วิธีการทำงานโดยละเอียด - อาจจะมากกว่าที่คุณต้องการ โดยหลักการแล้ว สิ่งนี้จะนำไปใช้กับแพลตฟอร์มใด ๆ ที่คุณใช้งานฐานข้อมูลของคุณ...

ดังนั้น เมื่อเราดำเนินการตามคำสั่ง เรามี Postgres ที่ทำงานบนวอลลุมภายนอก (EBS ในกรณีนี้ เนื่องจากเรากำลังทำงานกับ AWS) ฐานข้อมูลเติบโตขึ้น ณ จุดหนึ่งจำเป็นต้องปรับขนาด เช่น ขนาดเริ่มต้นของ EBS คือ 100 TB ฐานข้อมูลขยายไปถึงตอนนี้ เราต้องการสร้าง EBS 200 TB ยังไง? สมมติว่าคุณสามารถถ่ายโอนข้อมูล/กู้คืนบนอินสแตนซ์ใหม่ได้ แต่จะใช้เวลานานและเกี่ยวข้องกับการหยุดทำงาน

ดังนั้น ฉันจึงต้องการปรับขนาดที่จะขยายพาร์ติชัน EBS แล้วบอกให้ระบบไฟล์ใช้พื้นที่ใหม่ และเราก็ทำได้ แต่ในเวลานั้น Kubernetes ไม่มี API ใด ๆ สำหรับการดำเนินการปรับขนาด เนื่องจากเราทำงานกับ AWS เราจึงเขียนโค้ดสำหรับ API ของมัน

ไม่มีใครหยุดคุณไม่ให้ทำเช่นเดียวกันกับแพลตฟอร์มอื่น ไม่มีคำใบ้ในข้อความว่าสามารถทำงานได้บน AWS เท่านั้น และจะไม่สามารถใช้งานได้กับสิ่งอื่นทั้งหมด โดยทั่วไป นี่เป็นโปรเจ็กต์โอเพ่นซอร์ส หากใครต้องการเร่งให้เกิดการใช้ API ใหม่ ก็ยินดีต้อนรับ กิน GitHubดึงคำขอ - ทีม Zalando พยายามตอบกลับอย่างรวดเร็วและส่งเสริมผู้ปฏิบัติงาน เท่าที่ผมทราบโครงการ. เข้าร่วม ที่ Google Summer of Code และโครงการริเริ่มอื่นๆ ที่คล้ายกัน Zalando กำลังทำงานอย่างกระตือรือร้นกับเรื่องนี้

ป.ล. โบนัส!

หากคุณสนใจหัวข้อของ PostgreSQL และ Kubernetes โปรดทราบว่า Postgres วันอังคารครั้งถัดไปจัดขึ้นเมื่อสัปดาห์ที่แล้ว ซึ่งฉันได้พูดคุยกับ Nikolai Alexander Kukushkin จาก Zalando. วิดีโอจากมันสามารถใช้ได้ ที่นี่.

PPS

อ่านเพิ่มเติมในบล็อกของเรา:

ที่มา: will.com

เพิ่มความคิดเห็น