استخدام القنصل لتوسيع نطاق الخدمات الحكومية

في 22 سبتمبر ، عقدنا أول برنامج مصور غير قياسي لمطوري الأنظمة عالية التحميل. كان الأمر رائعًا جدًا ، وكان هناك الكثير من التعليقات الإيجابية حول التقارير ، وبالتالي قرر ليس فقط تحميلها ، ولكن أيضًا فك تشفيرها لـ Habr. ننشر اليوم خطابًا بإيفان بوبنوف ، DevOps من BIT.GAMES. وتحدث عن تنفيذ خدمة Consul discovery في مشروع يعمل بالفعل عالي الحمل لإمكانية التوسع السريع وتجاوز الخدمات الحكومية. وكذلك حول تنظيم مساحة اسم مرنة للتطبيقات الخلفية والمزالق. الآن كلمة لإيفان.


أنا أدير البنية التحتية للإنتاج في استوديو BIT.GAMES وأخبر قصة مقدمة القنصل من Hashicorp في مشروعنا "Guild of Heroes" - لعبة تقمص الأدوار الخيالية مع pvp غير متزامن للأجهزة المحمولة. متوفر على Google Play و App Store و Samsung و Amazon. DAU حوالي 100000 ، عبر الإنترنت من 10 إلى 13 ألفًا. نصنع اللعبة في Unity ، لذلك نكتب العميل في C # ونستخدم لغة البرمجة النصية BHL الخاصة بنا لمنطق اللعبة. نكتب جزء الخادم في Golang (تحولت إليه من PHP). التالي هو الهيكل التخطيطي لمشروعنا.


في الواقع ، هناك الكثير من الخدمات ، هناك فقط أساسيات منطق اللعبة.

لذا ما لدينا. من الخدمات عديمي الجنسية ، هذه:

  • nginx ، التي نستخدمها كموازين للواجهة الأمامية وتحميل وتوزع العملاء على واجهاتنا الخلفية بواسطة معاملات الوزن ؛
  • gamed - الخلفية ، التطبيقات المترجمة من Go. هذا هو المحور المركزي لهندستنا المعمارية ، فهم يؤدون نصيب الأسد من العمل ويتواصلون مع جميع خدمات الواجهة الخلفية الأخرى.

من الخدمات الحكومية ، الخدمات الرئيسية لدينا هي:

  • Redis ، الذي نستخدمه لتخزين المعلومات الساخنة (نستخدمها أيضًا لتنظيم الدردشة داخل اللعبة وتخزين الإشعارات للاعبين لدينا) ؛
  • Percona Server for Mysql هو مستودع للمعلومات المستمرة (ربما يكون الأكبر والأبطأ في أي بنية). نحن نستخدم شوكة MySQL وهنا سنتحدث عنها بمزيد من التفصيل اليوم.

خلال عملية التصميم ، كنا (مثل أي شخص آخر) نأمل أن ينجح المشروع وأن يتم توفيره لآلية تقسيم. وهو يتألف من كيانين قاعدة بيانات MAINDB وشظايا أنفسهم.



MAINDB هو نوع من جدول المحتويات - فهو يخزن معلومات حول أي بيانات جزئية محددة حول تقدم اللاعب يتم تخزينها عليه. وبالتالي ، تبدو السلسلة الكاملة لاسترداد المعلومات شيئًا مثل هذا: يصل العميل إلى الواجهة الأمامية ، والتي بدورها تقوم بإعادة توزيعها بالوزن إلى إحدى الخلفيات ، وتنتقل الخلفية إلى MAINDB ، وتحدّد جزء اللاعب ، ثم تحدد البيانات مباشرة من الجزء نفسه.

ولكن عندما صممنا ، لم نكن مشروعًا كبيرًا ، لذلك قررنا عمل شظايا شظايا بشكل رمزي فقط. كانت جميعها موجودة على نفس الخادم الفعلي وعلى الأرجح تقسيم قاعدة البيانات داخل نفس الخادم.

للنسخ الاحتياطي استخدمنا النسخ الرقيق الرئيسي الكلاسيكي. لم يكن حلًا جيدًا جدًا (سأقول لماذا بعد ذلك بقليل) ، لكن العيب الرئيسي لهذه البنية كان أن جميع الخلفيات لدينا كانوا يعرفون عن خدمات الواجهة الخلفية الأخرى حصريًا بواسطة عناوين IP. وفي حالة وقوع حادث مثير للسخرية في مركز البيانات من النوع " عذرًا ، قام مهندسنا بضرب الكابل على الخادم الخاص بك أثناء خدمة خادم آخر واستغرقنا وقتًا طويلاً لمعرفة سبب عدم اتصال خادمك " بالحركات الكبيرة المطلوبة منا. أولاً ، هذا هو إعادة إنشاء الخلفية وتثبيتها مسبقًا من خادم النسخ الاحتياطي IP لمكان الذي فشل. ثانيًا ، بعد الحادث ، من الضروري استعادة سيدنا من النسخ الاحتياطي من الاحتياطي ، لأنه كان في حالة غير متناسقة وإحضاره إلى حالة منسقة باستخدام نفس النسخ المتماثل. ثم قمنا مرة أخرى بإعادة تجميع الواجهات الخلفية وإعادة تحميلها. كل هذا ، بالطبع ، تسبب في التوقف.

جاء وقت قال فيه مديرنا الفني (الذي أشكره كثيرًا عليه): "أيها الرجال ، توقفوا عن المعاناة ، نحتاج إلى تغيير شيء ما ، فلنبحث عن سبل للخروج." بادئ ذي بدء ، أردنا تحقيق عملية بسيطة ومفهومة ، والأهم من ذلك - إدارة التدرج والترحيل بسهولة من مكان إلى آخر من قواعد بياناتنا إذا لزم الأمر. بالإضافة إلى ذلك ، أردنا تحقيق إمكانية توفر عالية من خلال أتمتة عمليات تجاوز الفشل.



أصبح المحور المركزي لأبحاثنا قنصلًا من Hashicorp. أولاً ، تم إخطارنا ، وثانيًا ، لقد انجذبنا للغاية إلى بساطتها ، وودها ، ومجموعتها التقنية الممتازة في صندوق واحد: خدمة الاكتشاف مع التحقق من الصحة ، وتخزين القيمة الرئيسية وأهم شيء أردنا استخدامه كان DNS الذي يحل لنا عناوين من المجال service.consul.

يوفر القنصل أيضًا واجهات مستخدم ويب رائعة وواجهات برمجة تطبيقات REST لإدارة كل هذا.

أما بالنسبة للتوافر العالي ، فقد اخترنا فائدتين للمساعدة في تجاوز الفشل التلقائي:

  • MHA لـ MySQL
  • Redis-sentinel



في حالة MHA لـ MySQL ، قمنا بصب الوكلاء في العقد بقواعد البيانات ، والذين قاموا برصد حالتهم. كان هناك مهلة معينة لفشل السيد ، وبعد ذلك تم عمل إيقاف لعبد للحفاظ على الاتساق ولم يقم سيدنا الاحتياطي من السيد الذي ظهر في حالة غير متناسقة بالتقاط البيانات. وأضفنا ربطًا على الويب إلى هؤلاء الوكلاء ، الذين سجلوا هناك IP الجديد للسيد الاحتياطي في القنصل نفسه ، وبعد ذلك دخل في إصدار DNS.

مع Redis-sentinel ، أصبح كل شيء أبسط. نظرًا لأنه هو نفسه يقوم بنصيب الأسد من العمل ، كل ما تبقى لنا هو أن نأخذ في الاعتبار في الفحص الصحي أن Redis-sentinel يجب أن يتم حصريًا على العقدة الرئيسية.

في البداية كان كل شيء يعمل بشكل مثالي ، مثل الساعة. لم تكن لدينا مشاكل في منصة الاختبار. ولكن كان من الجدير الانتقال إلى البيئة الطبيعية لنقل البيانات لمركز بيانات محمل ، وتذكر بعض عمليات قتل OOM (هذا نفاد في الذاكرة ، حيث يتم قتل العملية بواسطة قلب النظام) واستعادة الخدمة أو أشياء أكثر تعقيدًا تؤثر على توفر الخدمة - كيف حصلنا على الفور على خطر خطير من النتائج الإيجابية الزائفة أو عدم وجود استجابة مضمونة على الإطلاق (إذا حاولت تحريف بعض الشيكات في محاولة للهروب من الإيجابيات الزائفة).



بادئ ذي بدء ، كل شيء يعتمد على صعوبة كتابة الفحص الصحي الصحيح. يبدو أن المهمة تافهة تمامًا - تحقق من أن الخدمة تعمل على الخادم ومنفذ pingani. ولكن ، كما أظهرت الممارسة اللاحقة ، فإن كتابة فحص صحي عند تنفيذ القنصل عملية معقدة للغاية وتستغرق وقتًا طويلاً. لأنه لا يمكن توقع العديد من العوامل التي تؤثر على توفر الخدمة في مركز البيانات - يتم اكتشافها فقط بعد وقت معين.

بالإضافة إلى ذلك ، فإن مركز البيانات ليس هيكلًا ثابتًا تغمره ويعمل على النحو المنشود. لكننا ، للأسف (أو لحسن الحظ) ، اكتشفنا هذا الأمر لاحقًا فقط ، ولكن في الوقت الحالي تم إلهامنا وكامل الثقة بأننا سنطبق كل شيء في الإنتاج.



أما فيما يتعلق بالقياس ، فسأقول بإيجاز: لقد حاولنا العثور على دراجة منتهية ، ولكن جميعها مصممة لمعماريات محددة. وكما هو الحال في Jetpants ، لم نتمكن من تلبية الشروط التي فرضها على بنية التخزين المستمر للمعلومات.

لذلك ، فكرنا في ربط النص الخاص بنا وأرجأنا هذا السؤال. قررنا العمل بثبات والبدء في تنفيذ القنصل.



القنصل هو مجموعة لا مركزية وموزعة تعمل على أساس بروتوكول القيل والقال وخوارزمية الإجماع رافت.

لدينا عدد مستقل من الخوادم (خمسة لتجنب وضع تقسيم الدماغ). لكل عقدة ، ننقل وكيل القنصل في وضع الوكيل وننقل كل فحص الصحة (أي لم يكن هناك مثل هذا نقوم بتحميل فحص صحي واحد إلى خادم معين وأخرى إلى خوادم محددة). تمت كتابة فحص الصحة بحيث يمر فقط حيث توجد خدمة.

استخدمنا أيضًا أداة مساعدة أخرى حتى لا نضطر إلى معرفة الواجهة الخلفية لدينا لحل العناوين من مجال معين على منفذ غير قياسي. استخدمنا Dnsmasq - فهو يوفر القدرة على حل العناوين الموجودة على العقد العنقودية التي نحتاجها بشفافية كاملة (والتي في الواقع ، إذا جاز التعبير ، غير موجودة ، ولكنها موجودة حصريًا داخل المجموعة). لقد قمنا بإعداد نص تلقائي لملء Ansible ، وتحميله كله للإنتاج ، وضبط مساحة الاسم ، والتأكد من اكتمال كل شيء. وعبر أصابعهم ، أعادوا تحميل واجهاتنا الخلفية ، التي لم يتم الوصول إليها من خلال عناوين IP ، ولكن من خلال هذه الأسماء من مجال server.consul.

بدأ كل شيء في المرة الأولى ، ولم يكن فرحنا يعرف حدودًا. ولكن كان من السابق لأوانه أن نفرح ، لأنه في غضون ساعة لاحظنا أنه في جميع العقد التي تقع فيها واجهاتنا الخلفية ، ارتفع مؤشر متوسط ​​الحمولة من 0.7 إلى 1.0 ، وهو مؤشر إلى حد ما.



صعدت إلى الخادم لمعرفة ما كان يحدث وأصبح من الواضح أن وحدة المعالجة المركزية كانت تأكل القنصل. هنا بدأنا نكتشف الأمر ، بدأنا في التشهير بالحيوية (وهي أداة لأنظمة يونكس تسمح لك بتتبع أي مكالمة تجريها العملية) ، وإلقاء إحصائيات Dnsmasq لفهم ما يحدث في هذه العقدة ، وتبين لنا أننا فاتنا نقطة مهمة جدًا. عند التخطيط للتكامل ، فقدنا التخزين المؤقت لسجلات DNS واتضح أن الواجهة الخلفية سحبت Dnsmasq لكل من تحركاتها ، وهذا بدوره تحول إلى القنصل وكل هذا أدى إلى 940 استفسارا DNS في الثانية.

بدا المخرج واضحًا - فقط قم بتحويل ttl وسيتحسن كل شيء. ولكن هنا كان من المستحيل أن نكون متعصبين ، لأننا أردنا تنفيذ هذا الهيكل من أجل الحصول على مساحة اسم ديناميكية يمكن التحكم فيها بسهولة وسرعة التغيير (لذلك ، لم نتمكن من تعيين ، على سبيل المثال ، 20 دقيقة). قمنا بلف ttl إلى القيم المثلى القصوى بالنسبة لنا ، وتمكنا من تقليل معدل الاستعلام في الثانية إلى 540 ، ولكن هذا لم يؤثر على مؤشر استهلاك وحدة المعالجة المركزية.

ثم قررنا الخروج بطريقة صعبة ، باستخدام ملف hosts-custom.



من الجيد أن لدينا كل شيء لهذا: نظام قالب ممتاز من القنصل ، والذي ، بناءً على حالة المجموعة والنص البرمجي للنموذج ، ينشئ ملفًا من أي نوع ، أي تكوين هو كل ما تريده. بالإضافة إلى ذلك ، يحتوي Dnsmasq على معلمة تكوين addn-hosts تسمح لك باستخدام ملف hosts غير تابع للنظام كملف المضيف الإضافي نفسه.

ما فعلناه ، أعدنا البرنامج النصي مرة أخرى في Ansible ، وحملناه على الإنتاج وبدأ في ظهور شيء مثل هذا:



كان هناك عنصر إضافي وملف ثابت على القرص ، يتم إعادة إنشائه بسرعة كبيرة. الآن تبدو السلسلة بسيطة للغاية: تحولت الألعاب إلى Dnsmasq ، وهذا بدوره (بدلاً من سحب وكيل Consula ، الذي سيسأل الخوادم حيث كانت لدينا هذه العقدة أو تلك) نظر إلى الملف. هذا حل مشكلة استهلاك وحدة المعالجة المركزية بواسطة القنصل.

الآن بدأ كل شيء يبدو كما خططنا - شفاف تمامًا لإنتاجنا ، عمليًا دون استهلاك الموارد.

لقد تعرضنا للتعذيب الشديد في ذلك اليوم ، وبتفهم كبير ، عدنا إلى المنزل. لم يكونوا خائفين دون جدوى ، لأنه في الليل أيقظني تنبيه من المراقبة وأبلغني أن لدينا سلسلة من الأخطاء واسعة النطاق (وإن كانت قصيرة المدى).



عند التعامل مع السجلات في الصباح ، رأيت أن جميع الأخطاء كانت من نفس النوع مضيف غير معروف. لم يكن من الواضح لماذا لم تتمكن Dnsmasq من استخدام خدمة أو أخرى من ملف - يبدو أنها لم تكن موجودة على الإطلاق. لمحاولة فهم ما يجري ، أضفت مقياسًا مخصصًا لإعادة إنشاء الملف - الآن عرفت بالضبط الوقت الذي سيتم فيه إعادة إنشائه. بالإضافة إلى ذلك ، يحتوي قالب القنصل نفسه على خيار نسخ احتياطي ممتاز ، أي يمكنك رؤية الحالة السابقة للملف المعاد إنشاؤه.

خلال النهار ، تكرر الحادث عدة مرات وأصبح من الواضح أنه في وقت ما (على الرغم من أنه كان متقطعًا وغير نظامي بطبيعته) ، سيتم إعادة تقديم ملف المضيفين بدون أي خدمات محددة. اتضح أنه في مركز بيانات معين (لن أقوم بمكافحة الإعلانات) هناك شبكات غير مستقرة إلى حد ما - بسبب الشبكات المتقلبة ، توقفنا بشكل غير متوقع على الإطلاق عن اجتياز الفحص الصحي ، أو حتى العُقد سقطت من المجموعة. بدا شيء من هذا القبيل:



سقطت العقدة خارج الكتلة ، وتم إبلاغ وكيل القنصل على الفور بهذا الأمر ، وأعاد قالب القنصل على الفور إنشاء ملف المضيفين بدون الخدمة التي نحتاجها. كان هذا غير مقبول بشكل عام ، لأن المشكلة سخيفة: إذا كانت الخدمة غير متاحة لبضع ثوانٍ ، فقم بإعداد المهلات وإعادة التدريب (لم يتصلوا مرة واحدة ، ولكن في المرة الثانية التي ظهرت فيها). ولكننا أثارنا بنية جديدة في البائع عندما تختفي الخدمة ببساطة من العرض ولا توجد طريقة للاتصال بها.

بدأنا نفكر في ما يجب فعله ولف معلمة المهلة في القنصل ، وبعد ذلك يتم تحديدها بعد مقدار سقوط العقدة. تمكنا من حل هذه المشكلة بمؤشر صغير إلى حد ما ، توقفت العقد عن السقوط ، لكن هذا لم يساعد في الفحص الصحي.

بدأنا نفكر في اختيار معلمات مختلفة لفحص الصحة ، محاولين فهم بطريقة أو بأخرى متى وكيف يحدث هذا. ولكن نظرًا لحقيقة أن كل شيء حدث بشكل متقطع وغير متوقع ، لم نتمكن من القيام بذلك.

ثم ذهبنا إلى قالب القنصل وقررنا تحديد مهلة له ، وبعد ذلك يتفاعل مع تغيير في حالة الكتلة. مرة أخرى ، كان من المستحيل أن نكون متعصبين ، لأننا قد نصل إلى موقف لن تكون النتيجة فيه أفضل من DNS الكلاسيكي ، عندما كنا نهدف إلى نظام مختلف تمامًا.

ومرة أخرى جاء مديرنا الفني للإنقاذ وقال: "يا شباب ، دعنا نحاول التخلي عن كل هذا التفاعل ، لدينا كل شيء في الإنتاج وليس لدينا الوقت للبحث ، نحن بحاجة إلى حل هذه المشكلة. دعونا نستفيد من أشياء بسيطة ومفهومة ". لذا توصلنا إلى مفهوم استخدام تخزين القيمة الرئيسية كمصدر لإنشاء ملف hosts.



كيف يبدو: نحن نرفض جميع عمليات التحقق من الصحة الديناميكية ، ونعيد كتابة نص القالب الخاص بنا بحيث يقوم بإنشاء ملف بناءً على البيانات المسجلة في تخزين القيمة الرئيسية. في تخزين القيمة الرئيسية ، نحن نصف بنيتنا التحتية الكاملة في شكل اسم المفتاح (هذا هو اسم الخدمة التي نحتاجها) والقيمة الرئيسية (هذا هو اسم العقدة في المجموعة). على سبيل المثال إذا كانت العقدة موجودة في المجموعة ، فإننا نحصل بسهولة على عنوان IP الخاص بها ونكتبها في ملف hosts.

اختبرنا كل شيء ، ملأناه في الإنتاج ، وأصبح رصاصة فضية في حالة معينة. مرة أخرى ، تعذبنا إلى حد كبير طوال اليوم ذهبنا إلى المنزل ، لكننا عدنا للراحة بالفعل ، شجعنا ، لأن هذه المشاكل لم تتكرر ولم تتكرر خلال العام الماضي. استنتج منه شخصيا أن هذا كان القرار الصحيح (خاصة بالنسبة لنا).

لذا حصلنا أخيرًا على ما أردنا ونظمنا مساحة اسم ديناميكية لخلفيةنا. علاوة على ذلك ذهبنا نحو ضمان توافر عالية.



لكن الحقيقة هي أنه خائف جدًا من تكامل القنصل وبسبب المشاكل التي واجهناها ، فكرنا وقررنا أن تنفيذ الفشل التلقائي ليس حلاً جيدًا ، لأننا مرة أخرى نخاطر بإيجابيات خاطئة أو الفشل. هذه العملية غير شفافة ولا يمكن السيطرة عليها.

لذلك ، ذهبنا في مسار أبسط (أو معقد): قررنا ترك الفشل على ضمير المسؤول في الخدمة ، لكننا أعطيناه أداة إضافية أخرى. لقد استبدلنا النسخ المتماثل للرقيق الرئيسي بالنسخ المتماثل الرئيسي في وضع القراءة فقط. هذا يزيل كمية كبيرة من الصداع في عملية الفشل - عندما تحصل على معالج ، كل ما عليك فعله هو تغيير القيمة في k / v-storage باستخدام واجهة مستخدم الويب أو أمر في واجهة برمجة التطبيقات وقبل ذلك ، وضع القراءة فقط سيد النسخ الاحتياطي.

بعد انتهاء الحادثة ، تصل جهات الاتصال الرئيسية تلقائيًا إلى حالة منسقة دون أي إجراءات غير ضرورية. لقد توقفنا عند هذا الخيار ونستخدمه كما كان من قبل - بالنسبة لنا فهو مريح قدر الإمكان ، والأهم من ذلك أنه بسيط وواضح ويمكن التحكم فيه قدر الإمكان.


واجهة الويب القنصل

على اليمين يوجد التخزين k / v وخدماتنا مرئية ، والتي نستخدمها في الألعاب ؛ value هي اسم العقدة.

أما بالنسبة للتحجيم - فقد بدأنا في تنفيذه عندما أصبحت الشظايا مزدحمة بالفعل على خادم واحد ، ونمت القواعد ، وأصبحت بطيئة ، وزاد عدد اللاعبين ، وقمنا بالتبديل وكان لدينا مهمة توزيع جميع الأجزاء على خوادمنا المختلفة المختلفة.



كيف بدا الأمر: باستخدام الأداة المساعدة XtraBackup ، استعدنا نسخة احتياطية لدينا على زوج جديد من الخوادم ، وبعد ذلك تم تعليق السيد الجديد مع وجود خادم على خادم قديم. وصل الأمر إلى حالة متسقة ، قمنا بتغيير القيمة الرئيسية في التخزين k / v من اسم عقدة السيد القديم إلى اسم عقدة السيد الجديد. ثم (عندما اعتقدنا أن كل شيء سار بشكل صحيح وجميع الألعاب مع تحديداتهم وتحديثاتهم وإدخالاتهم ذهبت إلى المعلم الجديد) ، كان علينا فقط أن نقتل النسخ المتماثل وننشئ قاعدة بيانات قطرة مرغوبة عند الإنتاج ، كما نحب جميعًا القيام بقواعد بيانات غير ضرورية.



لذا تحطمت الشظايا. استغرقت عملية الانتقال بأكملها من 40 دقيقة إلى ساعة ولم تتسبب في توقف أي شخص ، فقد كانت شفافة تمامًا لواجهاتنا الخلفية وكانت في حد ذاتها شفافة تمامًا للاعبين (باستثناء أنه بمجرد انتقالهم أصبح الأمر أسهل وأكثر متعة بالنسبة لهم للعب).



أما بالنسبة لعمليات تجاوز الفشل ، فإن وقت التبديل هنا من 20 إلى 40 ثانية ، بالإضافة إلى وقت رد فعل مسؤول النظام المناوب. هذا عن كيف يبدو كل شيء معنا الآن.

ما أود أن أقوله في الختام - لسوء الحظ ، فقد تحطمت آمالنا في التشغيل الآلي المطلق والشامل في الواقع القاسي لوسيط نقل البيانات في مركز بيانات محمّل وعوامل عشوائية لم نتمكن من توقعها.

ثانيًا ، علمتنا مرة أخرى أن الحلمة البسيطة والمثبتة في يد مسؤول النظام أفضل من الرافعة الذاتية الجديدة وذات التدرج الذاتي في مكان ما وراء السحاب ، حتى أنك لا تفهم ما إذا كانت تنهار أم لا بدأت في التوسع.

يجب ألا يسبب إدخال أي بنية تحتية ، والأتمتة في إنتاجك ، صداعًا غير ضروري للموظفين الذين يخدمونه ؛ لا ينبغي أن يزيد بشكل كبير من تكلفة الحفاظ على إنتاج البنية التحتية - يجب أن يكون الحل بسيطًا وواضحًا وشفافًا لعملائك ، مناسبًا ومراقبًا.

أسئلة من الجمهور


كيف تكتب k / v مع الخوادم - برنامج نصي أم تقوم فقط بتصحيحه؟

K/v- Consul- - , http- RESTful API Web UI.

, - , , .

, Redis?

, - .

-, backend. -, backend', — . على سبيل المثال , MAINDB , . . - , .

- , inmemory key-value -.

?

MySQL — Percona server.

? Maria, MHA for MySQL, Galera.

Galera. - « » Galera , . , .

, — , , - , , , .

Pixonic DevGAMM Talks


Source: https://habr.com/ru/post/ar424777/


All Articles