مرحبا بالجميع!
اليوم نريد أن نتحدث عن كيفية حل فريق خدمة حجز الفنادق في
Ostrovok.ru لمشكلة نمو الخدمات المصغرة ، والتي تتمثل مهمتها في تبادل المعلومات مع موردينا. حول تجربته يروي
undying ، DevOps Team Lead في Ostrovok.ru.
في البداية ، كانت الخدمة الصغيرة صغيرة الحجم وكانت تؤدي الوظائف التالية:
- قبول طلب من خدمة محلية ؛
- تقديم طلب إلى شريك ؛
- تطبيع الاستجابة ؛
- إرجاع النتيجة إلى خدمة الاستعلام.
ومع ذلك ، بمرور الوقت ، نمت الخدمة مع عدد الشركاء والطلبات المقدمة لهم.
مع نمو الخدمة ، بدأت أنواع مختلفة من المشاكل في الظهور. طرح الموردون المختلفون قواعدهم الخاصة: يحد شخص ما من الحد الأقصى لعدد الاتصالات ، ويقيد شخص ما العملاء بالقوائم البيضاء.
نتيجة لذلك ، كان علينا حل المشاكل التالية:
- من المستحسن أن يكون لديك عدة عناوين IP خارجية ثابتة حتى تتمكن من توفيرها للشركاء لإضافتها إلى القوائم البيضاء ،
- لدينا مجموعة واحدة من الوصلات لجميع الموردين بحيث يظل عدد الوصلات عند الحد الأدنى ،
- إنهاء SSL والحفاظ على
keepalive
في مكان واحد ، وبالتالي تقليل العبء على الشركاء أنفسهم.
لم يفكروا لفترة طويلة وتساءلوا على الفور عما يجب اختياره: Nginx أو Haproxy.
في البداية ، تأرجح البندول نحو Nginx ، نظرًا لأنني حللت معظم المشكلات المرتبطة بـ HTTP / HTTPS بمساعدتها وكنت راضيًا دائمًا عن النتيجة.
كان المخطط بسيطًا: تم تقديم طلب إلى Proxy Server الجديد على Nginx مع مجال من النموذج
<partner_tag>.domain.local
، في Nginx كانت هناك
map
حيث
<partner_tag>
تقابل عنوان الشريك. تم أخذ عنوان من
map
وتم إجراء
proxy_pass
على هذا العنوان.
فيما يلي مثال
map
نقوم بتحليل النطاق بها وتحديد المنبع من القائمة:
وهنا يبدو شكل "
snippet.d/upstreams_map
":
“one” “http://one.domain.net”; “two” “https://two.domain.org”;
هنا لدينا
server{}
:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_address$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
كل شيء رائع ، كل شيء يعمل. من الممكن إنهاء هذه المقالة ، إن لم يكن بفارق بسيط.
عند استخدام proxy_pass ، ينتقل الطلب مباشرة إلى العنوان المرغوب ، كقاعدة عامة ، باستخدام بروتوكول HTTP / 1.0 دون
keepalive
ويغلق فورًا بعد اكتمال الاستجابة. حتى لو قمنا
proxy_http_version 1.1
، فلن يتغير أي شيء دون تغيير
المصدر (
proxy_http_version ).
ما يجب القيام به الفكر الأول هو جعل جميع الموردين في المنبع ، حيث سيكون الخادم هو عنوان المورد الذي نحتاجه ، وفي
map
احتفظ
"tag" "upstream_name"
.
أضف
map
أخرى لتحليل المخطط:
وإنشاء
upstreams
مع أسماء العلامات:
upstream one { keepalive 64; server one.domain.com; } upstream two { keepalive 64; server two.domain.net; }
تم تعديل الخادم نفسه قليلاً ليأخذ في الاعتبار المخطط واستخدام اسم المنبع بدلاً من العنوان:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_scheme$upstream_prefix$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
عظيم يعمل الحل ، قم بإضافة التوجيه
keepalive
في كل منبع ، قم بتعيين
proxy_http_version 1.1
، والآن لدينا تجمع اتصال ، وكل شيء يعمل كما يجب.
هذه المرة ، يمكنك بالتأكيد الانتهاء من المقالة والذهاب شرب الشاي. أم لا؟
في الواقع ، بينما نشرب الشاي ، قد يقوم أحد الموردين بتغيير عنوان IP أو مجموعة من العناوين ضمن نفس المجال (مرحبًا ، Amazon) ، وبالتالي قد يسقط أحد الموردين في ذروة حفلة الشاي الخاصة بنا.
حسنا ، ماذا تفعل؟ تتميز Nginx بفارق بسيط: أثناء إعادة التحميل ، يمكنها أن تفيق الخوادم داخل
upstream
إلى عناوين جديدة وتضع زيارات عليها. بشكل عام ، أيضا الحل. رمي في
cron reload nginx
كل 5 دقائق والاستمرار في شرب الشاي.
لكن لا يزال الأمر يبدو لي قرارًا جيدًا ، لذلك بدأت أتطلع إلى هابروكسي.
لدى Haproxy القدرة على تحديد
dns resolvers
dns cache
وتكوين
dns cache
لنظام
dns resolvers
dns cache
. وبالتالي ، ستقوم Haproxy بتحديث
dns cache
لنظام
dns cache
إذا انتهت صلاحية الإدخالات الموجودة بها ، وستستبدل العناوين الخاصة بالتيار الرئيسي إذا كانت قد تغيرت.
عظيم! الآن الأمر متروك للإعدادات.
فيما يلي مثال تكوين قصير لـ Haproxy:
frontend http bind *:80 http-request del-header X-Forwarded-For http-request del-header X-Forwarded-Port http-request del-header X-Forwarded-Proto capture request header Host len 32 capture request header Referer len 128 capture request header User-Agent len 128 acl host_present hdr(host) -m len gt 0 use_backend %[req.hdr(host),lower,field(1,'.')] if host_present default_backend default resolvers dns hold valid 1s timeout retry 100ms nameserver dns1 1.1.1.1:53 backend one http-request set-header Host one.domain.com server one--one.domain.com one.domain.com:80 resolvers dns check backend two http-request set-header Host two.domain.net server two--two.domain.net two.domain.net:443 resolvers dns check ssl verify none check-sni two.domain.net sni str(two.domain.net)
يبدو أن كل شيء يعمل كما يجب هذه المرة. الشيء الوحيد الذي لا يعجبني في Haproxy هو تعقيد وصف التكوين. تحتاج إلى إنشاء الكثير من النص لإضافة واحد يعمل المنبع. لكن الكسل هو محرك التقدم: إذا كنت لا تريد أن تكتب نفس الشيء ، فاكتب مولدًا.
كان لديّ بالفعل خريطة من Nginx تحتوي على تنسيق
"tag" "upstream"
، لذلك قررت أن أعتبرها أساسًا ، وقم بتحليل وإنشاء الواجهة الخلفية لـ haproxy استنادًا إلى هذه القيم.
الآن كل ما نحتاج إليه هو إضافة مضيف جديد في nginx_map ، وبدء المولد والحصول على تهيئة haproxy الجاهزة.
هذا ربما كل شيء لهذا اليوم. تشير هذه المقالة إلى المادة التمهيدية وتم تكريسها لمشكلة اختيار الحل وإدماجه في البيئة الحالية.
في المقالة التالية ، سوف أخبركم أكثر عن المخاطر التي واجهناها عند استخدام Haproxy ، والتي تبين أن المقاييس مفيدة لرصدها ، وما ينبغي تحسينه بالضبط في النظام من أجل الحصول على أقصى أداء من الخوادم.
شكرا لكم جميعا على اهتمامكم ، أراك!