การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes

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

การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes

เป็นเวลาประมาณหนึ่งปีแล้วที่แผนกโครงสร้างพื้นฐานของเราได้ย้ายบริการทั้งหมดที่ทำงานบน GitLab.com ไปยัง Kubernetes ในช่วงเวลานี้ เราพบกับความท้าทายที่เกี่ยวข้องไม่เฉพาะกับการย้ายบริการไปยัง Kubernetes เท่านั้น แต่ยังรวมถึงการจัดการการปรับใช้แบบไฮบริดในระหว่างการเปลี่ยนแปลงด้วย บทความนี้จะพิจารณาบทเรียนอันมีค่าที่เราได้เรียนรู้

จากจุดเริ่มต้นของ GitLab.com เซิร์ฟเวอร์ของบริษัททำงานในระบบคลาวด์บนเครื่องเสมือน เครื่องเสมือนเหล่านี้ได้รับการจัดการโดย Chef และติดตั้งโดยใช้ของเรา แพ็คเกจ Linux อย่างเป็นทางการ. กลยุทธ์การปรับใช้ ในกรณีที่จำเป็นต้องอัปเดตแอปพลิเคชัน ประกอบด้วยการอัปเดตฟลีตเซิร์ฟเวอร์ในลักษณะที่มีการประสานงานและต่อเนื่องกันโดยใช้ไปป์ไลน์ CI วิธีนี้ - แม้ว่าจะช้าและน้อยก็ตาม น่าเบื่อ - ตรวจสอบให้แน่ใจว่า GitLab.com ใช้แนวทางปฏิบัติในการติดตั้งและการกำหนดค่าเช่นเดียวกับผู้ใช้ออฟไลน์ (จัดการด้วยตนเอง) การติดตั้ง GitLab โดยใช้แพ็คเกจ Linux ของเราสำหรับสิ่งนี้

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

ก้าวแรกสู่ Kubernetes และ GitLab บนคลาวด์

โครงการนี้ถูกสร้างขึ้นในปี 2017 แผนภูมิ GitLab เพื่อเตรียม GitLab สำหรับการใช้งานบนคลาวด์ และเพื่อให้ผู้ใช้สามารถติดตั้ง GitLab บนคลัสเตอร์ Kubernetes ตอนนั้นเรารู้แล้วว่าการย้าย GitLab ไปยัง Kubernetes จะเพิ่มความสามารถในการปรับขนาดของแพลตฟอร์ม SaaS ลดความซับซ้อนในการปรับใช้ และปรับปรุงประสิทธิภาพของทรัพยากรการประมวลผล ในเวลาเดียวกัน ฟังก์ชันหลายอย่างของแอปพลิเคชันของเราขึ้นอยู่กับพาร์ติชัน NFS ที่ติดตั้ง ซึ่งทำให้การเปลี่ยนจากเครื่องเสมือนช้าลง

การผลักดันไปสู่ระบบคลาวด์เนทีฟและ Kubernetes ทำให้วิศวกรของเราสามารถวางแผนการเปลี่ยนแปลงทีละน้อย ในระหว่างนั้นเราได้ละทิ้งการพึ่งพาแอปพลิเคชันบางส่วนในพื้นที่จัดเก็บข้อมูลเครือข่าย ในขณะเดียวกันก็พัฒนาคุณสมบัติใหม่ๆ ต่อไป นับตั้งแต่เราเริ่มวางแผนการย้ายข้อมูลในช่วงฤดูร้อนปี 2019 ข้อจำกัดหลายประการเหล่านี้ได้รับการแก้ไขแล้ว และกระบวนการย้าย GitLab.com ไปยัง Kubernetes ก็กำลังดำเนินการไปด้วยดี!

คุณสมบัติของ GitLab.com ใน Kubernetes

สำหรับ GitLab.com เราใช้คลัสเตอร์ GKE ระดับภูมิภาคเดียวที่จัดการการรับส่งข้อมูลแอปพลิเคชันทั้งหมด เพื่อลดความซับซ้อนของการโยกย้าย (ที่ยุ่งยากอยู่แล้ว) เราจึงมุ่งเน้นไปที่บริการที่ไม่ต้องอาศัยพื้นที่จัดเก็บในตัวเครื่องหรือ NFS GitLab.com ใช้โค้ดเบส Rails แบบเสาหินเป็นส่วนใหญ่ และเรากำหนดเส้นทางการรับส่งข้อมูลตามลักษณะภาระงานไปยังปลายทางที่แตกต่างกันซึ่งแยกออกเป็น Node Pool ของตัวเอง

ในกรณีของส่วนหน้า ประเภทเหล่านี้จะแบ่งออกเป็นคำขอไปยังเว็บ, API, Git SSH/HTTPS และรีจิสทรี ในกรณีแบ็กเอนด์เราจะแบ่งงานเป็นคิวตามลักษณะต่างๆขึ้นอยู่กับ ขอบเขตทรัพยากรที่กำหนดไว้ล่วงหน้าซึ่งช่วยให้เราสามารถตั้งค่าวัตถุประสงค์ระดับบริการ (SLO) สำหรับปริมาณงานต่างๆ

บริการ GitLab.com ทั้งหมดเหล่านี้ได้รับการกำหนดค่าโดยใช้แผนภูมิ GitLab Helm ที่ยังไม่ได้แก้ไข การกำหนดค่าจะดำเนินการในแผนภูมิย่อย ซึ่งสามารถเลือกเปิดใช้งานได้เมื่อเราค่อยๆ ย้ายบริการไปยังคลัสเตอร์ แม้ว่าเราจะตัดสินใจไม่รวมบริการ stateful บางส่วนในการโยกย้าย เช่น Redis, Postgres, GitLab Pages และ Gitaly แต่การใช้ Kubernetes ช่วยให้เราสามารถลดจำนวน VM ที่ Chef จัดการอยู่ได้อย่างมาก

การมองเห็นและการจัดการการกำหนดค่า Kubernetes

การตั้งค่าทั้งหมดได้รับการจัดการโดย GitLab เอง สำหรับสิ่งนี้ จะใช้โปรเจ็กต์การกำหนดค่าสามโปรเจ็กต์ที่ใช้ Terraform และ Helm เราพยายามใช้ GitLab ทุกครั้งที่เป็นไปได้เพื่อเรียกใช้ GitLab แต่สำหรับงานด้านการปฏิบัติงาน เรามีการติดตั้ง GitLab แยกต่างหาก สิ่งนี้จำเป็นเพื่อให้แน่ใจว่าคุณจะไม่ขึ้นอยู่กับความพร้อมใช้งานของ GitLab.com เมื่อดำเนินการปรับใช้และอัปเดต GitLab.com

แม้ว่าไปป์ไลน์ของเราสำหรับคลัสเตอร์ Kubernetes จะทำงานบนการติดตั้ง GitLab แยกต่างหาก แต่ก็ยังมีมิเรอร์ของที่เก็บโค้ดที่เปิดเผยต่อสาธารณะตามที่อยู่ต่อไปนี้:

  • k8s-ปริมาณงาน/gitlab-com — กรอบการกำหนดค่า GitLab.com สำหรับแผนภูมิ GitLab Helm
  • k8s-ปริมาณงาน/gitlab-helmfiles - มีการกำหนดค่าสำหรับบริการที่ไม่เกี่ยวข้องโดยตรงกับแอปพลิเคชัน GitLab ซึ่งรวมถึงการกำหนดค่าสำหรับการบันทึกและการตรวจสอบคลัสเตอร์ รวมถึงเครื่องมือแบบรวม เช่น PlantUML
  • Gitlab-com-โครงสร้างพื้นฐาน — การกำหนดค่า Terraform สำหรับ Kubernetes และโครงสร้างพื้นฐาน VM ดั้งเดิม ที่นี่ คุณจะกำหนดค่าทรัพยากรทั้งหมดที่จำเป็นในการเรียกใช้คลัสเตอร์ รวมถึงตัวคลัสเตอร์เอง พูลโหนด บัญชีบริการ และการจองที่อยู่ IP

การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes
มุมมองสาธารณะจะแสดงเมื่อมีการเปลี่ยนแปลง สรุปสั้น ๆ พร้อมลิงก์ไปยังส่วนต่างโดยละเอียดที่ SRE วิเคราะห์ก่อนทำการเปลี่ยนแปลงคลัสเตอร์

สำหรับ SRE ลิงก์จะนำไปสู่ความแตกต่างโดยละเอียดในการติดตั้ง GitLab ซึ่งใช้สำหรับการผลิตและการเข้าถึงซึ่งมีจำกัด ซึ่งช่วยให้พนักงานและชุมชนที่ไม่สามารถเข้าถึงโครงการดำเนินงาน (ซึ่งเปิดให้เฉพาะ SRE เท่านั้น) สามารถดูการเปลี่ยนแปลงการกำหนดค่าที่เสนอได้ ด้วยการรวมอินสแตนซ์ GitLab สาธารณะสำหรับโค้ดเข้ากับอินสแตนซ์ส่วนตัวสำหรับไปป์ไลน์ CI เราจะรักษาเวิร์กโฟลว์เดียวในขณะเดียวกันก็รับประกันความเป็นอิสระจาก GitLab.com สำหรับการอัปเดตการกำหนดค่า

สิ่งที่เราพบระหว่างการย้ายถิ่นฐาน

ในระหว่างการย้าย เราได้เรียนรู้ประสบการณ์ในการนำไปใช้กับการย้ายและการปรับใช้ใหม่ใน Kubernetes

1. ต้นทุนที่เพิ่มขึ้นเนื่องจากการรับส่งข้อมูลระหว่างโซนความพร้อมใช้งาน

การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes
สถิติขาออกรายวัน (ไบต์ต่อวัน) สำหรับฟลีตพื้นที่เก็บข้อมูล Git บน GitLab.com

Google แบ่งเครือข่ายออกเป็นภูมิภาค สิ่งเหล่านี้จะถูกแบ่งออกเป็นโซนการเข้าถึง (AZ) Git โฮสติ้งเกี่ยวข้องกับข้อมูลจำนวนมาก ดังนั้นจึงเป็นเรื่องสำคัญสำหรับเราในการควบคุมเครือข่ายขาออก สำหรับการรับส่งข้อมูลภายใน ทางออกจะฟรีก็ต่อเมื่ออยู่ภายใน Availability Zone เดียวกัน ในขณะที่เขียนบทความนี้ เรากำลังให้บริการข้อมูลประมาณ 100 TB ในวันทำงานปกติ (และนั่นเป็นเพียงสำหรับพื้นที่เก็บข้อมูล Git เท่านั้น) บริการที่อยู่ในเครื่องเสมือนเดียวกันในโทโพโลยีบน VM เก่าของเราตอนนี้ทำงานในพ็อด Kubernetes ที่แตกต่างกัน ซึ่งหมายความว่าการรับส่งข้อมูลบางส่วนที่เคยอยู่ในเครื่อง VM ก่อนหน้านี้อาจเดินทางออกนอกโซนความพร้อมใช้งานได้

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

2. ขีดจำกัด การร้องขอทรัพยากร และการปรับขนาด

การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes
จำนวนเรพลิกาที่ประมวลผลการรับส่งข้อมูลที่ใช้งานจริงบน register.gitlab.com ปริมาณการเข้าชมสูงสุดในเวลา ~15:00 UTC

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

พบว่าในกรณีของแอปพลิเคชันที่การใช้หน่วยความจำเพิ่มขึ้นเมื่อเวลาผ่านไป ค่าคำขอที่ต่ำ (การสำรองหน่วยความจำสำหรับแต่ละพ็อด) ประกอบกับการจำกัดการใช้งานอย่างหนักที่ "ใจกว้าง" นำไปสู่ความอิ่มตัว (ความอิ่มตัว) โหนดและการขับไล่ในระดับสูง เพื่อจัดการกับปัญหานี้ก็คือ มีการตัดสินใจที่จะเพิ่มคำขอและขีดจำกัดล่าง. วิธีนี้ช่วยลดแรงกดดันต่อโหนดและทำให้แน่ใจว่าพ็อดมีวงจรชีวิตที่ไม่สร้างแรงกดดันต่อโหนดมากเกินไป ตอนนี้เราเริ่มการย้ายข้อมูลด้วยคำขอที่มีน้ำใจ (และเกือบจะเหมือนกัน) และจำกัดค่าต่างๆ โดยปรับเปลี่ยนตามความจำเป็น

3. ตัวชี้วัดและบันทึก

การค้นพบของเราจากหนึ่งปีของการย้าย GitLab.com ไปยัง Kubernetes
แผนกโครงสร้างพื้นฐานมุ่งเน้นไปที่เวลาแฝง อัตราข้อผิดพลาด และความอิ่มตัวของการติดตั้ง เป้าหมายระดับการให้บริการ (SLO) เชื่อมโยงกับ ความพร้อมใช้งานทั่วไปของระบบของเรา.

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

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

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

4. การเปลี่ยนการรับส่งข้อมูลไปยังคลัสเตอร์ใหม่

สำหรับ GitLab.com ส่วนหนึ่งของเซิร์ฟเวอร์มีไว้สำหรับ เวทีนกขมิ้น. Canary Park ให้บริการโครงการภายในของเราและยังสามารถ เปิดใช้งานโดยผู้ใช้. แต่ได้รับการออกแบบมาเพื่อทดสอบการเปลี่ยนแปลงที่เกิดขึ้นกับโครงสร้างพื้นฐานและแอปพลิเคชันเป็นหลัก บริการที่ย้ายครั้งแรกเริ่มต้นด้วยการยอมรับการรับส่งข้อมูลภายในในจำนวนที่จำกัด และเรายังคงใช้วิธีนี้ต่อไปเพื่อให้แน่ใจว่าเป็นไปตาม SLO ก่อนที่จะส่งการรับส่งข้อมูลทั้งหมดไปยังคลัสเตอร์

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

5. ความจุสำรองของฝักและการใช้งาน

พบปัญหาต่อไปนี้เกือบจะในทันที: พ็อดสำหรับบริการ Registry เริ่มต้นอย่างรวดเร็ว แต่การเปิดตัวพ็อดสำหรับ Sidekiq ใช้เวลาถึง สองนาที. เวลาเริ่มต้นที่ยาวนานสำหรับพ็อด Sidekiq กลายเป็นปัญหาเมื่อเราเริ่มย้ายปริมาณงานไปยัง Kubernetes สำหรับผู้ปฏิบัติงานที่ต้องการประมวลผลงานอย่างรวดเร็วและปรับขนาดได้อย่างรวดเร็ว

ในกรณีนี้ บทเรียนก็คือแม้ว่า Horizon Pod Autoscaler (HPA) ของ Kubernetes จะจัดการกับการเติบโตของการรับส่งข้อมูลได้ดี แต่สิ่งสำคัญคือต้องคำนึงถึงลักษณะของปริมาณงานและจัดสรรความจุสำรองให้กับพ็อด (โดยเฉพาะอย่างยิ่งเมื่อมีการกระจายความต้องการไม่สม่ำเสมอ) ในกรณีของเรา มีงานเพิ่มขึ้นอย่างกะทันหัน ซึ่งนำไปสู่การปรับขนาดอย่างรวดเร็ว ซึ่งทำให้ทรัพยากร CPU อิ่มตัวก่อนที่เราจะมีเวลาปรับขนาด Node Pool

มักมีความอยากที่จะบีบออกจากคลัสเตอร์ให้ได้มากที่สุด แต่เมื่อประสบปัญหาด้านประสิทธิภาพในตอนแรก ตอนนี้เราจึงเริ่มต้นด้วยงบประมาณพ็อดที่พอเหมาะและลดลงในภายหลัง โดยคอยจับตาดู SLO อย่างใกล้ชิด การเปิดตัวพ็อดสำหรับบริการ Sidekiq มีความรวดเร็วขึ้นอย่างมาก และตอนนี้ใช้เวลาประมาณ 40 วินาทีโดยเฉลี่ย จากการลดเวลาการเปิดตัวของพ็อด ได้รับรางวัลทั้ง GitLab.com และผู้ใช้การติดตั้งแบบจัดการด้วยตนเองของเราที่ทำงานร่วมกับแผนภูมิ GitLab Helm อย่างเป็นทางการ

ข้อสรุป

หลังจากย้ายแต่ละบริการแล้ว เรายินดีกับประโยชน์ของการใช้ Kubernetes ในการผลิต: การปรับใช้แอปพลิเคชันที่รวดเร็วและปลอดภัยยิ่งขึ้น การปรับขนาด และการจัดสรรทรัพยากรที่มีประสิทธิภาพมากขึ้น นอกจากนี้ ข้อดีของการย้ายข้อมูลยังมีมากกว่าบริการ GitLab.com การปรับปรุงแผนภูมิ Helm อย่างเป็นทางการทุกครั้งจะเป็นประโยชน์ต่อผู้ใช้

ฉันหวังว่าคุณจะสนุกกับเรื่องราวของการผจญภัยในการย้ายถิ่นของ Kubernetes ของเรา เรายังคงย้ายบริการใหม่ทั้งหมดไปยังคลัสเตอร์ต่อไป ข้อมูลเพิ่มเติมสามารถพบได้ในสิ่งพิมพ์ต่อไปนี้:

ปล.จากผู้แปล

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

ที่มา: will.com

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