مرحبا بالجميع!
سنتحدث اليوم عن تجربة أحد مشروعات DevOps. قررنا تنفيذ تطبيق جديد لنظام التشغيل Linux باستخدام .Net Core على بنية خدمة متناهية الصغر.
نتوقع أن المشروع سوف يتطور بنشاط ، وسيكون هناك المزيد والمزيد من المستخدمين. لذلك ، يجب أن يكون قابلاً للتطوير بسهولة من حيث الوظائف والأداء.
نحتاج إلى نظام يتحمل الأخطاء - إذا لم تعمل إحدى مجموعات الوظائف ، فيجب أن يعمل الباقي. نريد أيضًا ضمان التكامل المستمر ، بما في ذلك نشر الحل على خوادم العميل.
لذلك ، استخدمنا التقنيات التالية:
- .نت كور لتنفيذ الخدمات الصغيرة. استخدم مشروعنا الإصدار 2.0 ،
- Kubernetes لتنسيق الخدمات الصغيرة ،
- عامل إرساء لإنشاء صور الخدمات الصغيرة ،
- حافلة التكامل الأرنب MQ و Mass Transit ،
- Elasticsearch and Kibana للتسجيل ،
- TFS لتنفيذ خط أنابيب CI / CD.
ستشارك هذه المقالة تفاصيل حلنا.

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

إليك كيفية عمل حلنا:
1. يقوم المشرف بوضع استبيان - هذه قائمة مرجعية لما تحتاجه للتحقق من عمل التاجر.
2. بعد ذلك ، يختار المشرف الموظف الذي سيتم فحص عمله. تم تحديد تاريخ الاستجواب.
3. بعد ذلك ، يتم إرسال النشاط إلى الجهاز المحمول للمشرف.
4. ثم يتم تعبئة الاستبيان وإرساله إلى البوابة الإلكترونية.
5. تولد البوابة نتائج وتقارير مختلفة.
ستساعدنا Microservices في حل ثلاث مشاكل:
1. في المستقبل ، نريد توسيع الوظائف بسهولة ، نظرًا لوجود العديد من عمليات الأعمال المماثلة في الشركة.
2. نريد أن يكون الحل متسامحا مع الخطأ. إذا توقف أي جزء عن العمل ، فسيكون الحل قادرًا على استعادة عمله من تلقاء نفسه ، ولن يؤثر فشل جزء واحد بشكل كبير على تشغيل الحل ككل.
3. الشركة التي ننفذ الحل لها لها فروع عديدة. وبالتالي ، فإن عدد مستخدمي الحل في تزايد مستمر. لذلك ، أردت أن لا يؤثر ذلك على الأداء.
ونتيجة لذلك ، قررنا استخدام الخدمات الدقيقة في هذا المشروع ، الأمر الذي تطلب عددًا من القرارات غير التافهة.
ما التقنيات التي ساعدت على تنفيذ هذا الحل:
• Docker يبسط توزيع توزيع الحل. التوزيع في حالتنا هو مجموعة من صور الخدمات الصغيرة
• نظرًا لوجود العديد من الخدمات الدقيقة في حلنا ، فإننا بحاجة إلى إدارتها. لهذا نستخدم Kubernetes.
• نقوم بتنفيذ خدمات متناهية الصغر باستخدام .Net Core.
• من أجل تحديث الحل بسرعة على العميل ، يجب علينا تنفيذ التكامل والتوصيل المستمر المريح.
إليك مجموعتنا الكاملة من التقنيات:
• صافي النواة الذي نستخدمه لإنشاء خدمات دقيقة ،
• يتم تغليف الخدمات المصغرة في صورة Docker ،
• يتم تنفيذ التكامل المستمر والتسليم المستمر باستخدام TFS ،
• يتم تنفيذ الواجهة الأمامية في الزاوي ،
• لرصد وتسجيل نستخدم Elasticsearch و Kibana ،
• يتم استخدام RabbitMQ و MassTransit كحافلة تكامل.
.NET Core لحلول Linux
نعلم جميعًا ما هو إطار .Net الكلاسيكي. العيب الرئيسي للمنصة هو أنها ليست عبر منصة. وفقًا لذلك ، لا يمكننا تشغيل حلول على .Net Framework لنظام Linux في Docker.
لتوفير القدرة على استخدام C # في Docker ، أعادت Microsoft التفكير في .Net Framework وأنشأت .Net Core. ولاستخدام نفس المكتبات ، أنشأت Microsoft مواصفات .Net Standard Library. يمكن استخدام تجميعات .Net Standart Library في كل من .Net Framework و .Net Core.

Kubernetes - لتنسيق الخدمات الصغرى
يستخدم Kubernetes لإدارة وتجميع حاويات Docker. فيما يلي المزايا الرئيسية لـ Kubernetes التي استفدنا منها:
- يوفر القدرة على تهيئة بيئة الخدمات الدقيقة بسهولة ،
- تبسيط الإدارة البيئية (Dev ، QA ، Stage) ،
- من خارج منطقة الجزاء يوفر القدرة على تكرار الخدمات الصغيرة وموازنة الحمل على النسخ المتماثلة.

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

خيارات للتفاعل مع العملاء الخارجيين
مايكروسوفت في كتابه حول الخدمات المصغرة “
.NET Microservices. تقدم .NET Container Application Architecture "ثلاثة تطبيقات محتملة للتفاعل مع الخدمات الدقيقة. استعرضنا كل ثلاثة واختيار الأنسب.
• خدمة بوابة API
واجهة برمجة تطبيقات خدمة Gateway هي عبارة عن تطبيق للواجهة لطلبات المستخدمين للخدمات الأخرى. مشكلة الحل هي أنه إذا لم تعمل الواجهة ، فعندئذٍ سيتوقف الحل بالكامل عن العمل. قرروا التخلي عن هذا النهج للتسامح مع الخطأ.
• بوابة API مع إدارة Azure API
توفر Microsoft القدرة على استخدام واجهة سحابية في Azure. لكن هذا الحل لم يكن مناسبًا ، لأننا سننشر الحل ليس في السحابة ، ولكن على خوادم العميل.
• الاتصال المباشر بالعميل إلى الخدمات المصغرة
ونتيجة لذلك ، لدينا الخيار الأخير المتبقي - تفاعل المستخدم المباشر مع الخدمات الصغيرة. اخترنا له.

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

العقدة 1 ، العقدة 2 والعقدة 3 هي نسخ طبق الأصل من الخدمة الصغيرة نفسها. في حالة فشل إحدى النسخ المتماثلة ، سيقوم موازن التحميل بإعادة توجيه الحمل تلقائيًا إلى الخدمات الصغيرة الأخرى.
العمارة الفيزيائية
إليك كيفية تنظيم البنية التحتية للحلول لدينا:
• لكل خدمة مايكرو قاعدة بيانات خاصة بها (إذا احتاجها بالطبع) ، فإن الخدمات الأخرى لا تصل إلى قاعدة بيانات خدمة مايكرو أخرى.
• تتواصل الخدمات الصغيرة مع بعضها البعض فقط عبر ناقل RabbitMQ + Mass Transit ، وكذلك باستخدام طلبات HTTP.
• لكل خدمة مسؤوليتها المحددة بوضوح.
• لتسجيل الدخول نستخدم Elasticsearch و Kibana والمكتبة للعمل معها
Serilog .

تم نشر خدمة قاعدة البيانات على جهاز ظاهري منفصل ، وليس في Kubernetes ، لأن Microsoft DBMS لا ينصح باستخدام Docker في بيئات المنتج.
تم نشر خدمة التسجيل أيضًا على جهاز افتراضي منفصل لأسباب تحمل الخطأ - إذا كانت لدينا مشاكل مع Kubernetes ، فيمكننا معرفة المشكلة.
النشر: كيف نظمنا بيئات التطوير والمنتج
تحتوي بنيتنا التحتية على 3 مساحات أسماء في Kubernetes. تصل البيئات الثلاث إلى خدمة قاعدة بيانات واحدة وخدمة تسجيل واحدة. وبالطبع ، تنظر كل بيئة في قاعدة البيانات الخاصة بها.

على البنية التحتية للعميل ، لدينا أيضًا بيئتان - ما قبل الإنتاج والإنتاج. عند الإنتاج ، لدينا خوادم قاعدة بيانات منفصلة لبيئة ما قبل البيع وبيئة المنتج. للتسجيل ، قمنا بتخصيص خادم ELK واحد على بنيتنا التحتية وعلى البنية التحتية للعميل.
كيف تنشر 5 بيئات لكل منها 10 خدمات صغيرة؟
في المتوسط ، لدينا 10 خدمات لكل مشروع وثلاث بيئات: QA ، DEV ، Stage ، حيث يتم نشر حوالي 30 خدمة مايكرو في المجموع. وهذا فقط على البنية التحتية للتنمية! أضف بيئتين إضافيتين على البنية التحتية للعميل ، وسوف نحصل على 50 خدمة صغيرة.

من الواضح أنه يجب إدارة هذا العدد من الخدمات بطريقة أو بأخرى. Kubernetes يساعدنا في ذلك.
من أجل نشر خدمة متناهية الصغر ، يجب عليك
• توسيع السرية ،
• نشر النشر ،
• توسيع الخدمة.
حول الكتابة السرية أدناه.
النشر هو تعليمات لـ Kubernetes ، والتي ستطلق على أساسها حاوية Docker من خدماتنا الصغيرة. فيما يلي الأمر الذي يتم نشر النشر فيه:
kubectl apply -f .\(yaml deployment-) --namespace=DEV
apiVersion: apps/v1beta1 kind: Deployment metadata: name: imtob-etr-it-dictionary-api spec: replicas: 1 template: metadata: labels: name: imtob-etr-it-dictionary-api spec: containers: - name: imtob-etr-it-dictionary-api image: nexus3.company.ru:18085/etr-it-dictionary-api:18289 resources: requests: memory: "256Mi" limits: memory: "512Mi" volumeMounts: - name: secrets mountPath: /app/secrets readOnly: true volumes: - name: secrets secret: secretName: secret-appsettings-dictionary
يصف هذا الملف ما يسمى النشر (imtob-etr-it-Dictionary-api) ، والصورة التي يحتاجها للتنفيذ ، بالإضافة إلى الإعدادات الأخرى. في القسم السري ، سنقوم بتخصيص بيئتنا.
بعد نشر النشر ، نحتاج إلى نشر الخدمة ، إذا لزم الأمر.
هناك حاجة إلى الخدمات عند الحاجة إلى الوصول إلى الخدمات الصغيرة من الخارج. على سبيل المثال ، عندما تريد أن يتمكن مستخدم أو خدمة أخرى من تقديم طلب الحصول على خدمة أخرى.
kubectl apply -f .\imtob-etr-it-dictionary-api.yml --namespace=DEV
apiVersion: v1 kind: Service metadata: name: imtob-etr-it-dictionary-api-services spec: ports: - name: http port: 80 targetPort: 80 protocol: TCP selector: name: imtob-etr-it-dictionary-api
عادة ما يكون وصف الخدمة صغيرًا. نرى فيه اسم الخدمة ، وكيف يمكن الوصول إليها ورقم المنفذ.
نتيجة لذلك ، لنشر البيئة ، نحتاج
• مجموعة من الملفات بأسرار جميع الخدمات الصغيرة ،
• مجموعة من الملفات مع نشر جميع الخدمات الصغيرة ،
• مجموعة من الملفات مع جميع خدمات microservices.
نقوم بتخزين كل هذه النصوص في مستودع git.
لنشر الحل ، حصلنا على مجموعة من ثلاثة أنواع من البرامج النصية:
• مجلد مع أسرار - هذه تكوينات لكل بيئة ،
• مجلد مع نشر جميع الخدمات الدقيقة ،
• مجلد يحتوي على خدمات لبعض الخدمات الدقيقة ،
في كل منها - حوالي عشرة فرق ، واحد لكل خدمة مايكرو. من أجل الراحة ، قمنا بإنشاء صفحة تحتوي على نصوص برمجية في Confluence ، مما يساعدنا على نشر بيئة جديدة بسرعة.
في ما يلي نص نشر للنشر (هناك مجموعات مماثلة للسرية وللخدمة):
برنامج النشرتطبيق kubectl -f. \ imtob-etr-it-image-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-mobile-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-planning-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-result-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-web.yml --namespace = DEV
تطبيق kubectl -f. \ imtob-etr-it-report-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-template-buildor-api.yml --namespace = DEV
تطبيق kubectl -f. \ imtob-etr-it-Dictionary-api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-تكامل- api.yml - مساحة الاسم = DEV
تطبيق kubectl -f. \ imtob-etr-it-الهوية-api.yml - مساحة الاسم = DEV
تنفيذ CI / CD
كل خدمة موجودة في مجلدها الخاص ، بالإضافة إلى أن لدينا مجلد واحد يحتوي على مكونات مشتركة.

هناك أيضًا تعريف البناء وتعريف الإصدار لكل خدمة صغيرة. قمنا بتهيئة إطلاق تعريف البناء عند الالتزام بالخدمة المناسبة أو عند الالتزام بالمجلد المناسب. إذا تم تحديث محتويات المجلد الذي يحتوي على مكونات مشتركة ، فسيتم نشر جميع الخدمات الدقيقة.
ما هي مزايا منظمة البناء هذه؟
1. الحل في مستودع بوابة واحد ،
2. عند التغيير في عدة خدمات دقيقة ، يبدأ التجميع بالتوازي مع عوامل التجميع المجانية ،
3. يقدم كل تعريف بناء نصًا بسيطًا من إنشاء الصورة ودفعها إلى Nexus Registry.
بناء تعريف وتعريف الإصدار
كيفية نشر وكيل VSTS ، وصفنا سابقًا
في هذه المقالة .

يأتي أولاً تعريف البناء. في الأمر TFS VSTS ، يبدأ العامل بناء ملف Dockerfile. نتيجة لذلك ، نحصل على صورة خدمة متناهية الصغر. يتم حفظ هذه الصورة محليًا في البيئة التي يعمل فيها وكيل VSTS.
بعد البناء ، يتم تشغيل Push ، الذي يرسل الصورة التي تلقيناها في الخطوة السابقة إلى Nexus Registry. الآن يمكن استخدامه خارجيا. Nexus Registry هو نوع من Nuget ، ليس للمكتبات فقط ، ولكن لصور Docker والمزيد.
بعد أن تصبح الصورة جاهزة ويمكن الوصول إليها من الخارج ، تحتاج إلى نشرها. لهذا لدينا تعريف الإصدار. كل شيء بسيط هنا - نقوم بتنفيذ الأمر set image:
kubectl set image deployment/imtob-etr-it-dictionary-api imtob-etr-it-dictionary-api=nexus3.company.ru:18085/etr-it-dictionary-api:$(Build.BuildId)
بعد ذلك ، سوف يقوم بتحديث الصورة الخاصة بالخدمة الدقيقة المطلوبة وإطلاق حاوية جديدة. نتيجة لذلك ، تم تحديث خدمتنا.
دعنا الآن نقارن البناء مع Dockerfile وبدونه.

بدون ملف Dockerfile ، نحصل على الكثير من الخطوات ، والتي تحتوي على الكثير من تفاصيل .Net. على اليمين نرى بناء صورة Docker. كل شيء أصبح أسهل بكثير.
يتم وصف العملية الكاملة لبناء الصورة في ملف Dockerfile. يمكن تصحيح هذا التجميع محليًا.

الإجمالي: حصلنا على قرص مدمج بسيط وشفاف
1. فصل التطوير والنشر. يتم وصف التجميع في Dockerfile ويقع على أكتاف المطور.
2. عند تكوين CI / CD ، لا تحتاج إلى معرفة تفاصيل وميزات التجميع - يتم العمل فقط مع ملف Dockerfile.
3. نقوم بتحديث فقط الخدمات الصغيرة التي تم تغييرها.
بعد ذلك ، تحتاج إلى تكوين RabbitMQ في K8S: كتبنا
مقالة منفصلة حول هذا الموضوع.
إعداد البيئة
بطريقة أو بأخرى ، نحتاج إلى تكوين الخدمات الدقيقة. يتم تكوين الجزء الرئيسي من البيئة في ملف التكوين الجذر Appsettings.json. يحتوي هذا الملف على إعدادات مستقلة عن البيئة.
يتم تخزين تلك الإعدادات التي تعتمد على البيئة في مجلد الأسرار في ملف appsettings.secret.json. اتخذنا النهج الموصوف في المقالة
إدارة إعدادات تطبيقات ASP.NET الأساسية على Kubernetes .
var configuration = new ConfigurationBuilder() .AddJsonFile($"appsettings.json", true) .AddJsonFile("secrets/appsettings.secrets.json", optional: true) .Build();
يحتوي ملف appsettings.secrets.json على إعدادات فهارس البحث المرن وسلسلة اتصال قاعدة البيانات.
{ "Serilog": { "WriteTo": [ { "Name": "Elasticsearch", "Args": { "nodeUris": "http://192.168.150.114:9200", "indexFormat": "dev.etr.it.ifield.api.dictionary-{0:yyyy.MM.dd}", "templateName": "dev.etr.it.ifield.api.dictionary", "typeName": "dev.etr.it.ifield.api.dictionary.event" } } ] }, "ConnectionStrings": { "DictionaryDbContext": "Server=192.168.154.162;Database=DEV.ETR.IT.iField.Dictionary;User Id=it_user;Password=PASSWORD;" } }
إضافة ملف التكوين إلى Kubernetes
لإضافة هذا الملف ، تحتاج إلى نشره في حاوية Docker. يتم ذلك في ملف نشر Kubernetis. يصف النشر في المجلد الذي يجب إنشاء الملف السري فيه وأي السر الذي من الضروري إقرانه بالملف.
apiVersion: apps/v1beta1 kind: Deployment metadata: name: imtob-etr-it-dictionary-api spec: replicas: 1 template: metadata: labels: name: imtob-etr-it-dictionary-api spec: containers: - name: imtob-etr-it-dictionary-api image: nexus3.company.ru:18085/etr-it-dictionary-api:18289 resources: requests: memory: "256Mi" limits: memory: "512Mi" volumeMounts: - name: secrets mountPath: /app/secrets readOnly: true volumes: - name: secrets secret: secretName: secret-appsettings-dictionary
يمكنك إنشاء سر في Kubernetes باستخدام أداة kubectl. نرى هنا اسم السر ومسار الملف. نشير أيضًا إلى اسم البيئة التي نخلق لها سرًا.
kubectl create secret generic secret-appsettings-dictionary
--from-file=./Dictionary/appsettings.secrets.json --namespace=DEMO
الاستنتاجات
سلبيات النهج المختار
1. عتبة دخول عالية. إذا كنت تقوم بمثل هذا المشروع لأول مرة ، فسيكون هناك الكثير من المعلومات الجديدة.
2. Microservices → تصميم أكثر تعقيدا. من الضروري تطبيق العديد من الحلول غير الواضحة لأننا لا نملك حلاً متجانساً ، ولكن لدينا خدمة صغيرة.
3. لا يتم تنفيذ كل شيء لـ Docker. لا يمكن تشغيل كل شيء في بنية الخدمات المصغرة. على سبيل المثال ، في حين أن SSRS ليس في عامل الميناء.
إيجابيات نهج اختبار ذاتي
1. البنية التحتية كرمز
يتم تخزين وصف البنية التحتية في التحكم بالمصادر. في وقت النشر ، لا تحتاج إلى تكييف البيئة.
2. التحجيم على مستوى الوظائف ومستوى الأداء خارج منطقة الجزاء.
3. يتم عزل الخدمات الدقيقة بشكل جيد
لا توجد أجزاء حاسمة عمليًا ، يؤدي فشلها إلى عدم عمل النظام ككل.
4. سرعة تسليم التغييرات
يتم تحديث الخدمات الدقيقة فقط التي حدثت فيها تحديثات. إذا كنت لا تأخذ في الاعتبار وقت التنسيق وأشياء أخرى تتعلق بالعامل البشري ، فإن تحديث خدمة مايكرو واحدة يتم في دقيقتين أو أقل.
الاستنتاجات بالنسبة لنا
1. على .NET Core ، يمكنك ويجب عليك تنفيذ الحلول الصناعية.
2. جعلت K8S الحياة أسهل بالفعل ، وبسطت تحديث البيئات ، وتسهل تكوين الخدمات.
3. يمكن استخدام TFS لتنفيذ CI / CD لنظام Linux.