ทั้งหมดดี!
ฉันชื่อ Nikita เป็นหัวหน้าทีมของทีมวิศวกร Cian ความรับผิดชอบประการหนึ่งของฉันที่บริษัทคือการลดจำนวนเหตุการณ์ที่เกี่ยวข้องกับโครงสร้างพื้นฐานในการผลิตให้เป็นศูนย์
สิ่งที่จะกล่าวถึงด้านล่างทำให้เราเจ็บปวดอย่างมาก และจุดประสงค์ของบทความนี้คือเพื่อป้องกันไม่ให้ผู้อื่นทำผิดซ้ำหรืออย่างน้อยก็ลดผลกระทบของพวกเขาให้เหลือน้อยที่สุด
คำนำ
นานมาแล้ว เมื่อ Cian ประกอบด้วยหินใหญ่ก้อนเดียว และยังไม่มีคำแนะนำเกี่ยวกับไมโครเซอร์วิส เราวัดความพร้อมใช้งานของทรัพยากรโดยการตรวจสอบ 3-5 หน้า
พวกเขาตอบ - ทุกอย่างเรียบร้อยดีหากไม่ตอบเป็นเวลานาน - แจ้งเตือน พวกเขาต้องหยุดงานนานแค่ไหนจึงจะถือว่าเหตุการณ์ดังกล่าวถูกตัดสินโดยคนในที่ประชุม ทีมวิศวกรมีส่วนร่วมในการสืบสวนเหตุการณ์นี้อยู่เสมอ เมื่อการสืบสวนเสร็จสิ้น พวกเขาก็เขียนชันสูตรศพ ซึ่งเป็นรายงานประเภทหนึ่งทางอีเมลในรูปแบบ: เกิดอะไรขึ้น เกิดขึ้นนานแค่ไหน สิ่งที่เราทำในขณะนั้น สิ่งที่เราจะทำในอนาคต
หน้าหลักของเว็บไซต์หรือวิธีที่เราเข้าใจว่าเราถึงจุดต่ำสุดแล้ว
เพื่อให้เข้าใจถึงลำดับความสำคัญของข้อผิดพลาด เราได้ระบุหน้าไซต์ที่สำคัญที่สุดสำหรับฟังก์ชันทางธุรกิจ เมื่อใช้สิ่งเหล่านี้ เราจะนับจำนวนคำขอและการหมดเวลาที่สำเร็จ/ไม่สำเร็จ นี่คือวิธีที่เราวัดสถานะการออนไลน์
สมมติว่าเราพบว่ามีส่วนสำคัญอย่างยิ่งหลายส่วนในไซต์ที่รับผิดชอบบริการหลัก นั่นคือ การค้นหาและส่งโฆษณา หากจำนวนคำขอที่ล้มเหลวเกิน 1% นี่เป็นเหตุการณ์ร้ายแรง หากภายใน 15 นาทีในช่วงเวลาไพรม์ไทม์ อัตราข้อผิดพลาดเกิน 0,1% ก็ถือเป็นเหตุการณ์วิกฤติเช่นกัน เกณฑ์เหล่านี้ครอบคลุมเหตุการณ์ส่วนใหญ่ ส่วนที่เหลืออยู่นอกเหนือขอบเขตของบทความนี้
เหตุการณ์ที่ดีที่สุดยอดนิยม Cian
ดังนั้นเราจึงได้เรียนรู้ที่จะระบุข้อเท็จจริงของเหตุการณ์ที่เกิดขึ้นอย่างแน่นอน
ตอนนี้ทุกเหตุการณ์ได้รับการอธิบายอย่างละเอียดและสะท้อนให้เห็นในมหากาพย์จิระ อย่างไรก็ตาม: สำหรับสิ่งนี้เราได้เริ่มโปรเจ็กต์แยกต่างหากเรียกว่า FAIL - มีเพียงมหากาพย์เท่านั้นที่สามารถสร้างได้
หากคุณรวบรวมความล้มเหลวทั้งหมดในช่วงไม่กี่ปีที่ผ่านมา ผู้นำได้แก่:
- เหตุการณ์ที่เกี่ยวข้องกับ mssql;
- เหตุการณ์ที่เกิดจากปัจจัยภายนอก
- ข้อผิดพลาดของผู้ดูแลระบบ
มาดูรายละเอียดเพิ่มเติมเกี่ยวกับข้อผิดพลาดของผู้ดูแลระบบรวมถึงความล้มเหลวที่น่าสนใจอื่น ๆ
อันดับที่ห้า - “วางสิ่งต่าง ๆ ตามลำดับใน DNS”
มันเป็นวันอังคารที่มีพายุ เราตัดสินใจกู้คืนคำสั่งซื้อในคลัสเตอร์ DNS
ฉันต้องการถ่ายโอนเซิร์ฟเวอร์ DNS ภายในจากการเชื่อมโยงไปยัง powerdns โดยจัดสรรเซิร์ฟเวอร์ที่แยกจากกันโดยสิ้นเชิงสำหรับสิ่งนี้ โดยที่ไม่มีอะไรนอกจาก DNS
เราวางเซิร์ฟเวอร์ DNS หนึ่งเครื่องไว้ในแต่ละตำแหน่งของ DC ของเรา และถึงเวลาแล้วที่ต้องย้ายโซนจากการเชื่อมโยงไปยัง powerdns และเปลี่ยนโครงสร้างพื้นฐานไปยังเซิร์ฟเวอร์ใหม่
ในระหว่างการย้าย เซิร์ฟเวอร์ทั้งหมดที่ระบุไว้ในการเชื่อมโยงแคชภายในเครื่องบนเซิร์ฟเวอร์ทั้งหมด มีเพียงเซิร์ฟเวอร์เดียวเท่านั้นที่ยังคงอยู่ในศูนย์ข้อมูลในเซนต์ปีเตอร์สเบิร์ก ในตอนแรก DC นี้ได้รับการประกาศว่าไม่สำคัญสำหรับเรา แต่จู่ๆ ก็กลายเป็นจุดล้มเหลวเพียงจุดเดียว
ในช่วงเวลาของการย้ายถิ่นฐานนี้เองที่คลองระหว่างมอสโกวและเซนต์ปีเตอร์สเบิร์กพังทลายลง จริงๆ แล้วเราถูกทิ้งไว้โดยไม่มี DNS เป็นเวลาห้านาทีและกลับมาใช้งานได้เมื่อโฮสต์แก้ไขปัญหา
สรุป:
หากก่อนหน้านี้เราละเลยปัจจัยภายนอกในการเตรียมตัวทำงาน ตอนนี้ก็รวมอยู่ในรายการสิ่งที่เรากำลังเตรียมการด้วย และตอนนี้เรามุ่งมั่นที่จะตรวจสอบให้แน่ใจว่าส่วนประกอบทั้งหมดถูกสงวนไว้ n-2 และในระหว่างทำงานเราสามารถลดระดับนี้เป็น n-1 ได้
- เมื่อจัดทำแผนปฏิบัติการ ให้ทำเครื่องหมายจุดที่บริการอาจล้มเหลว และคิดถึงสถานการณ์ที่ทุกอย่าง "จากแย่ไปสู่แย่ลง" ล่วงหน้า
- กระจายเซิร์ฟเวอร์ DNS ภายในไปยังตำแหน่งทางภูมิศาสตร์/ศูนย์ข้อมูล/แร็ค/สวิตช์/อินพุตต่างๆ
- ในแต่ละเซิร์ฟเวอร์ ให้ติดตั้งเซิร์ฟเวอร์ DNS สำหรับแคชในเครื่อง ซึ่งจะเปลี่ยนเส้นทางคำขอไปยังเซิร์ฟเวอร์ DNS หลัก และหากไม่พร้อมใช้งาน ก็จะตอบกลับจากแคช
อันดับที่สี่ - “วางสิ่งต่าง ๆ ตามลำดับใน Nginx”
วันหนึ่ง ทีมของเราตัดสินใจว่า "เราพอแล้ว" และกระบวนการปรับเปลี่ยนการกำหนดค่า nginx ก็เริ่มต้นขึ้น เป้าหมายหลักคือการนำการกำหนดค่ามาสู่โครงสร้างที่ใช้งานง่าย ก่อนหน้านี้ ทุกอย่าง "ได้รับการสถาปนาตามประวัติศาสตร์" และไม่มีตรรกะใดๆ ตอนนี้แต่ละ server_name ได้ถูกย้ายไปยังไฟล์ชื่อเดียวกันและการกำหนดค่าทั้งหมดได้ถูกกระจายไปยังโฟลเดอร์แล้ว อย่างไรก็ตาม การกำหนดค่าประกอบด้วย 253949 บรรทัดหรือ 7836520 อักขระ และใช้พื้นที่เกือบ 7 เมกะไบต์ โครงสร้างระดับบนสุด:
โครงสร้าง Nginx
├── access
│ ├── allow.list
...
│ └── whitelist.conf
├── geobase
│ ├── exclude.conf
...
│ └── geo_ip_to_region_id.conf
├── geodb
│ ├── GeoIP.dat
│ ├── GeoIP2-Country.mmdb
│ └── GeoLiteCity.dat
├── inc
│ ├── error.inc
...
│ └── proxy.inc
├── lists.d
│ ├── bot.conf
...
│ ├── dynamic
│ └── geo.conf
├── lua
│ ├── cookie.lua
│ ├── log
│ │ └── log.lua
│ ├── logics
│ │ ├── include.lua
│ │ ├── ...
│ │ └── utils.lua
│ └── prom
│ ├── stats.lua
│ └── stats_prometheus.lua
├── map.d
│ ├── access.conf
│ ├── ..
│ └── zones.conf
├── nginx.conf
├── robots.txt
├── server.d
│ ├── cian.ru
│ │ ├── cian.ru.conf
│ │ ├── ...
│ │ └── my.cian.ru.conf
├── service.d
│ ├── ...
│ └── status.conf
└── upstream.d
├── cian-mcs.conf
├── ...
└── wafserver.conf
มันดีขึ้นมาก แต่ในกระบวนการเปลี่ยนชื่อและกระจายการกำหนดค่า บางส่วนมีส่วนขยายที่ไม่ถูกต้องและไม่รวมอยู่ในคำสั่ง include *.conf เป็นผลให้บางโฮสต์ไม่สามารถใช้งานได้และส่งคืน 301 ไปที่หน้าหลัก เนื่องจากรหัสตอบกลับไม่ใช่ 5xx/4xx จึงไม่ได้สังเกตเห็นทันที แต่เฉพาะในตอนเช้าเท่านั้น หลังจากนั้น เราเริ่มเขียนการทดสอบเพื่อตรวจสอบส่วนประกอบโครงสร้างพื้นฐาน
สรุป:
- วางโครงสร้างการกำหนดค่าของคุณอย่างถูกต้อง (ไม่ใช่แค่ nginx) และคิดทบทวนโครงสร้างตั้งแต่ระยะเริ่มต้นของโปรเจ็กต์ วิธีนี้จะทำให้ทีมเข้าใจได้มากขึ้น ซึ่งจะลด TTM ไปด้วย
- เขียนการทดสอบสำหรับส่วนประกอบโครงสร้างพื้นฐานบางอย่าง ตัวอย่างเช่น: การตรวจสอบว่าคีย์ server_names ทั้งหมดให้สถานะที่ถูกต้อง + เนื้อความการตอบสนอง แค่มีสคริปต์ไม่กี่ตัวที่จะตรวจสอบฟังก์ชั่นพื้นฐานของส่วนประกอบก็เพียงพอแล้วเพื่อไม่ให้จำอย่างเมามันตอนตี 3 จะต้องตรวจสอบอะไรอีกบ้าง
อันดับที่สาม - “พื้นที่ในคาสซานดราหมดทันใด”
ข้อมูลเติบโตอย่างต่อเนื่อง และทุกอย่างเรียบร้อยดีจนกระทั่งการซ่อมแซมเคสสเปซขนาดใหญ่เริ่มล้มเหลวในคลัสเตอร์ Cassandra เนื่องจากการบดอัดไม่สามารถทำงานได้
วันหนึ่งพายุกระหน่ำ คลัสเตอร์เกือบจะกลายเป็นฟักทอง กล่าวคือ:
- มีพื้นที่เหลือประมาณ 20% ของพื้นที่ทั้งหมดในกลุ่ม
- ไม่สามารถเพิ่มโหนดได้อย่างสมบูรณ์ เนื่องจากการล้างข้อมูลไม่สำเร็จหลังจากเพิ่มโหนดเนื่องจากไม่มีพื้นที่บนพาร์ติชัน
- ผลผลิตค่อยๆ ลดลงเนื่องจากการบดอัดไม่ทำงาน
- คลัสเตอร์อยู่ในโหมดฉุกเฉิน
ออก - เราเพิ่มโหนดอีก 5 โหนดโดยไม่มีการล้างข้อมูล หลังจากนั้นเราเริ่มลบโหนดออกจากคลัสเตอร์อย่างเป็นระบบและกลับเข้าไปใหม่ เช่น โหนดว่างที่ไม่มีพื้นที่เหลือ ใช้เวลาไปมากกว่าที่เราต้องการ มีความเสี่ยงที่คลัสเตอร์จะไม่พร้อมใช้งานบางส่วนหรือทั้งหมด
สรุป:
- บนเซิร์ฟเวอร์ Cassandra ทั้งหมด ไม่ควรเกิน 60% ของพื้นที่ในแต่ละพาร์ติชัน
- ควรโหลดด้วย CPU ไม่เกิน 50%
- คุณไม่ควรลืมเกี่ยวกับการวางแผนกำลังการผลิตและต้องคิดทบทวนสำหรับแต่ละส่วนประกอบโดยพิจารณาจากข้อมูลเฉพาะของมัน
- ยิ่งมีโหนดในคลัสเตอร์มากเท่าไรก็ยิ่งดีเท่านั้น เซิร์ฟเวอร์ที่มีข้อมูลจำนวนเล็กน้อยจะถูกโอเวอร์โหลดเร็วขึ้น และคลัสเตอร์ดังกล่าวจะฟื้นคืนชีพได้ง่ายกว่า
อันดับที่สอง - “ข้อมูลหายไปจากการจัดเก็บคีย์-ค่ากงสุล”
สำหรับการค้นหาบริการ เราก็ใช้กงสุลเช่นเดียวกับหลายๆ คน แต่เรายังใช้คีย์-ค่าของมันสำหรับเลย์เอาต์สีน้ำเงิน-เขียวของหินใหญ่ก้อนเดียวด้วย โดยจะจัดเก็บข้อมูลเกี่ยวกับอัพสตรีมที่ใช้งานและไม่ได้ใช้งาน ซึ่งมีการเปลี่ยนแปลงระหว่างการใช้งาน เพื่อจุดประสงค์นี้ บริการการปรับใช้งานจึงถูกเขียนขึ้นโดยโต้ตอบกับ KV เมื่อถึงจุดหนึ่ง ข้อมูลจาก KV ก็หายไป กู้คืนจากหน่วยความจำ แต่มีข้อผิดพลาดหลายประการ เป็นผลให้ในระหว่างการอัปโหลด โหลดบนอัปสตรีมมีการกระจายไม่สม่ำเสมอ และเราได้รับข้อผิดพลาด 502 จำนวนมากเนื่องจากการโอเวอร์โหลดบน CPU ของแบ็กเอนด์ ด้วยเหตุนี้ เราจึงย้ายจากกงสุล KV ไปยัง postgres จากที่ซึ่งการถอดออกไม่ใช่เรื่องง่ายอีกต่อไป
สรุป:
- บริการที่ไม่ได้รับอนุญาตไม่ควรมีข้อมูลที่สำคัญต่อการดำเนินงานของไซต์ ตัวอย่างเช่น หากคุณไม่ได้รับอนุญาตใน ES จะเป็นการดีกว่าถ้าปฏิเสธการเข้าถึงในระดับเครือข่ายจากทุกที่ที่ไม่จำเป็น เหลือเฉพาะที่จำเป็นเท่านั้น และตั้งค่า action.destructive_requires_name: true ด้วย
- ฝึกฝนกลไกการสำรองและกู้คืนของคุณล่วงหน้า ตัวอย่างเช่น สร้างสคริปต์ล่วงหน้า (เช่น ใน Python) ที่สามารถสำรองและกู้คืนได้
ที่หนึ่ง - “กัปตันไม่ชัดเจน”
ในบางจุด เราสังเกตเห็นการกระจายโหลดที่ไม่สม่ำเสมอบนอัปสตรีม nginx ในกรณีที่มีเซิร์ฟเวอร์มากกว่า 10 เซิร์ฟเวอร์ในระบบแบ็กเอนด์ เนื่องจากข้อเท็จจริงที่ว่า Round-robin ส่งคำขอตั้งแต่วันที่ 1 ถึงอัปสตรีมสุดท้ายตามลำดับและการโหลด nginx แต่ละครั้งเริ่มต้นใหม่ อัปสตรีมแรกจะได้รับคำขอมากกว่าที่เหลือเสมอ ผลก็คือ พวกเขาทำงานช้าลงและทั้งไซต์ก็ประสบปัญหา สิ่งนี้เริ่มสังเกตเห็นได้ชัดเจนยิ่งขึ้นเมื่อปริมาณการรับส่งข้อมูลเพิ่มขึ้น การอัปเดต nginx เพื่อเปิดใช้งานการสุ่มไม่ทำงาน - เราจำเป็นต้องทำซ้ำรหัส lua จำนวนมากที่ไม่ได้นำไปใช้กับเวอร์ชัน 1.15 (ในขณะนั้น) เราต้องแพตช์ nginx 1.14.2 ของเรา โดยแนะนำการรองรับแบบสุ่ม วิธีนี้ช่วยแก้ปัญหาได้ จุดบกพร่องนี้ชนะหมวดหมู่ "กัปตันไม่ชัดเจน"
สรุป:
การสำรวจข้อบกพร่องนี้น่าสนใจและน่าตื่นเต้นมาก)
- จัดระเบียบการติดตามของคุณเพื่อช่วยให้คุณค้นหาความผันผวนดังกล่าวได้อย่างรวดเร็ว ตัวอย่างเช่น คุณสามารถใช้ ELK เพื่อตรวจสอบ rps ในแต่ละแบ็กเอนด์ของแต่ละอัปสตรีม ตรวจสอบเวลาตอบสนองจากมุมมองของ nginx ในกรณีนี้ สิ่งนี้ช่วยให้เราระบุปัญหาได้
ด้วยเหตุนี้ ความล้มเหลวส่วนใหญ่จึงสามารถหลีกเลี่ยงได้ด้วยแนวทางที่รอบคอบมากขึ้นกับสิ่งที่คุณกำลังทำอยู่ เราต้องจำกฎของเมอร์ฟี่ไว้เสมอ: สิ่งใดผิดพลาดได้ย่อมผิดพลาดไป และสร้างส่วนประกอบตามนั้น
ที่มา: will.com