كيفية إعداد سير عمل CI / CD للتطبيقات Node.js مع Jenkins و Kubernetes

كونستانتين فيوفانتوف

المقدمة

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

تستخدم مسارات العمل هذه محركات الأتمتة المعدلة حسب المهام. سأشرح في هذا البرنامج التعليمي كيفية إعداد سير عمل CI / CD لتطبيق Node.js مستضاف على Kubernetes. بالنسبة لأتمتة الجزء الأول ، سأستخدم جينكينز ، وهو أحد أدوات CI / CD الأكثر شعبية اليوم.

سأفترض أن لديك بالفعل كتلة Kubernetes مع خادم تثبيت هيلم / تيلر. سأفترض أيضًا أنك معتاد على Git وأن لديك مستودعًا. في هذه المقالة سوف نستخدم Bitbucket ، لكن يمكنك استخدام أي منصة Git أخرى مثل Github أو Gitlab على سبيل المثال.

سير العمل والهندسة المعمارية

سوف نستخدم سير العمل التالي:

لاحظ أننا سنستخدم مساحات أسماء Kubernetes كبيئات نشر. من أجل تبسيط هذا البرنامج التعليمي ، سأستخدم بنية النشر من مستويين مع بيئات التكامل والإنتاج ، ولكن يمكنك توسيع النظام إلى بنية ثلاثية الطبقات أو 4 مستويات بسهولة.

لإنشاء مساحات الأسماء هذه ، قم بتشغيل الأوامر في kubectl:

kubectl إنشاء مساحة الاسم myapp- التكامل
kubectl إنشاء مساحة الاسم myapp الإنتاج

تحقق من إنشاء مساحات الأسماء:

kubectl الحصول على مساحات الأسماء

إعداد سير العمل

سيتألف التطبيق الذي سنطلقه من جزأين:

  • وكيل Nginx العكسي
  • تطبيق NodeJS

تتم استضافة رمز التطبيق في مستودع Git بهيكل الدليل التالي:

/
   -src /
     index.js
   -tests /
     integration-tests.sh
     production-tests.sh
   -نشر/
     إنجن إكس-reverseproxy.yaml
     nodejs.yaml
   Jenkinsfile

دعونا نلقي نظرة على هذه الملفات بالتفصيل.

يحتوي دليل src على تطبيق Node.js أساسي يقوم بإنشاء خادم http على المنفذ 8080 وإرجاع رسالة وفقًا للمسار الذي تمت زيارته:

  • "هذه هي الصفحة الرئيسية" عند زيارة "/"
  • "مرحبًا بكم في dir1 ، كيف يمكنني مساعدتك؟" عند زيارة "/ dir1"
  • "المعلومات حول الشخص ذي المعرف 1 هي X" عند زيارة "/ dir2 / person / 1"
// index.js
فار http = تتطلب ('http') ؛
var url = require ('url') ؛
var server = http.createServer (function (req، res) {
 var page = url.parse (req.url) .pathname؛
 console.log (صفحة)؛
 res.writeHead (200، {"Content-Type": "text / plain"})؛
 
 إذا (الصفحة == '/') {
   res.write ('هذه الصفحة الرئيسية') ؛
 }
 عدا ذلك (الصفحة == '/ dir1') {
  res.write ("مرحبًا بك في dir1 ، كيف يمكنني مساعدتك؟") ؛
 }
 عدا ذلك (الصفحة == '/ dir2 / person / 1') {
  res.write ('المعلومات حول الشخص ذي المعرف 1 هي X') ؛
 }
 إعادة إرسال()؛
})؛
server.listen (8080)؛

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

في هذه الحالة ، سوف نستخدم curl لاختبار المسارات الثلاثة على تطبيق Node.js. إذا أرجع الخادم الاستجابة الصحيحة ، فسوف ينجح الاختبار. خلاف ذلك ، سيتم إنهاء البرنامج النصي مع وجود خطأ.

اختبارات التكامل.

#! / بن / سحق
# التكامل- الاختبارات
صدى "بدء اختبارات التكامل ..."
صدى "اختبار مسار الجذر ..."
res1 = $ (حليقة http: // $ 1 /)
إذا ["$ res1"! = "هذه هي الصفحة الرئيسية"] ؛ ثم
 صدى "فشل مسار / اختبار. إحباط ..."
 خروج 1
فاي
صدى "مسار الاختبار / dir1 ..."
res2 = $ (حليقة http: // $ 1 / dir1)
إذا ["$ res2"! = "مرحبًا بك في dir1 ، كيف يمكنني مساعدتك؟" ]. ثم
 صدى "فشل اختبار المسار / dir1. إحباط ..."
 خروج 1
فاي
صدى "اختبار مسار الجذر / dir2 / شخص / 1 ..."
res3 = $ (حليقة http: // $ 1 / dir2 / person / 1)
إذا ["$ res3"! = "المعلومات حول الشخص ذي المعرف 1 هي X"] ؛ ثم
 صدى "فشل اختبار المسار / dir1. إحباط ..."
 خروج 1
فاي
صدى "اختبارات التكامل نجحت".

اختبارات الإنتاج.

#! / بن / سحق
# الإنتاج - الاختبارات
صدى "بدء اختبارات الإنتاج ..."
صدى "اختبار مسار الجذر ..."
res1 = $ (حليقة http: // $ 1 /)
إذا ["$ res1"! = "هذه هي الصفحة الرئيسية"] ؛ ثم
 صدى "فشل مسار / اختبار. إحباط ..."
 خروج 1
فاي
صدى "مسار الاختبار / dir1 ..."
res2 = $ (حليقة http: // $ 1 / dir1)
إذا ["$ res2"! = "مرحبًا بك في dir1 ، كيف يمكنني مساعدتك؟" ]. ثم
 صدى "فشل اختبار المسار / dir1. إحباط ..."
 خروج 1
فاي
صدى "اختبار مسار الجذر / dir2 / شخص / 1 ..."
res3 = $ (حليقة http: // $ 1 / dir2 / person / 1)
إذا ["$ res3"! = "المعلومات حول الشخص ذي المعرف 1 هي X"] ؛ ثم
 صدى "فشل اختبار المسار / dir1. إحباط ..."
 خروج 1
فاي
صدى "نجحت اختبارات الإنتاج".

يحتوي دليل النشر على جميع ملفات yaml اللازمة لنشر التطبيق على Kubernetes. يحتوي yaml أدناه على موارد Kubernetes لنشر الوكيل العكسي Nginx.

# إنجن إكس-reverseproxy.yaml
apiVersion: v1
النوع: الخدمة
البيانات الوصفية:
  الاسم: خدمة إنجنكس-ريفيربوكسي
المواصفات:
 محدد:
    التطبيق: nginx-reverseproxy
 اكتب: LoadBalancer #LB لفضح الخدمة والحصول على عنوان IP خارجي
 الموانئ:
  - الاسم: http
    الميناء: 80
    البروتوكول: TCP
---
apiVersion: ملحقات / v1beta1
النوع: النشر
البيانات الوصفية:
  تسميات:
    التطبيق: nginx-reverseproxy
  الاسم: nginx-reverseproxy- النشر
المواصفات:
  النسخ المتماثلة: 1
  قالب:
    البيانات الوصفية:
      تسميات:
        التطبيق: nginx-reverseproxy
    المواصفات:
      حاويات:
      - الصورة: nginx: 1.13
        الاسم: kubecont-nginx
        الموانئ:
        - حاوية المنفذ: 80
        volumeMounts:
        - الاسم: التكوين الحجم
          mountPath: /etc/nginx/conf.d
      أحجام:
        - الاسم: التكوين الحجم
          configMap:
            الاسم: nginx-reverseproxy-config
---
apiVersion: v1
النوع: ConfigMap
البيانات الوصفية:
  الاسم: nginx-reverseproxy-config
البيانات:
  default.conf: | -
    الخادم {
     server_name yourhostname.com ؛
     الاستماع 80 ؛
     #deny الوصول إلى ملفات htaccess ، إذا جذر المستند Apache
     #Ccurs مع واحد nginx
     #
     الموقع ~ /\.ht {
         نفي جميع؛
     }
موقعك / {
         proxy_pass http: // nodejs-service: 8080؛ # هذه هي الخدمة الموضحة في nodejs.yaml
     }
    }

وأخيرًا ، إليكم إطلاق Yode.js على Kubernetes. لاحظ أنه يتم إنشاء CongigMap المشار إليها بواسطة nodejs النشر بشكل حيوي أثناء تنفيذ خط أنابيب كما أنا سوف expain أدناه.

# nodejs.yaml
apiVersion: v1
النوع: الخدمة
البيانات الوصفية:
  الاسم: nodejs الخدمة
المواصفات:
 محدد:
    التطبيق: nodejs
 الموانئ:
  - الاسم: http
    الميناء: 8080
    البروتوكول: TCP
---
apiVersion: ملحقات / v1beta1
النوع: النشر
البيانات الوصفية:
  تسميات:
    التطبيق: nodejs
  الاسم: نشر nodejs
المواصفات:
  النسخ المتماثلة: 1
  قالب:
    البيانات الوصفية:
      تسميات:
        التطبيق: nodejs
    المواصفات:
      حاويات:
      - الصورة: العقدة: 9.11
        الاسم: kubecont-nodejs
        الأمر: ["العقدة" ، "/usr/src/app/index.js"]
        الموانئ:
        - حاوية المنفذ: 8080
        volumeMounts:
        - الاسم: حجم التطبيق
          mountPath: / usr / src / app
      أحجام:
        - الاسم: حجم التطبيق
          configMap:
            الاسم: nodejs التطبيق

وأخيرا ، لدينا Jenkinsfile الذي يصف سير عمل CI / CD في جنكينز. يتكون سير العمل من ثلاث مراحل:

  • مرحلة الإعداد: تم تثبيت kubectl ويتم استنساخ مستودع التطبيق
  • مرحلة التكامل: يتم إنشاء ConfigMap من تطبيق Node.js ، ويتم إنشاء موارد Kubernetes. ثم يتم اختبار التطبيق وأخيرا يتم تنظيف البيئة
  • مرحلة الإنتاج: يتم تنفيذ نفس الخطوات كما في مرحلة التكامل ، باستثناء التنظيف حيث يجب أن تبقى موارد Kubernet في الإنتاج.

لذلك هذا هو جينكينسفيل:

// Jenkinsfile
عقدة {
المرحلة ("الإعداد") {
      // تثبيت kubectl في وكيل جنكينز
      sh 'curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/release/release/stable.txt)/bin/linux / AMD64 / kubectl "
  sh 'chmod + x ./kubectl && mv kubectl / usr / local / sbin'
// استنساخ مستودع بوابة
  git url: 'https: //bitbucket.org/advatys/jenkins-pipeline.git'
   }
المرحلة ("التكامل") {
 
      withKubeConfig ([credentialsId: 'jenkins-publisher-credentials'، serverUrl: 'https://104.155.31.202']) {
      
         sh 'kubectl إنشاء cm nodejs-app - من ملف - = src / - مساحة الاسم = myapp-integration -o = yaml --dry-run> publish / cm.yaml'
         sh 'kubectl Apply -f publish / --namespace = myapp-integration'
         محاولة{
          // Gathering Node.js عنوان IP الخارجي للتطبيق
          def ip = ''
          عدد مواطنه = 0
          def countLimit = 10
       
          // حلقة انتظار لتوفير عنوان IP
          println ("في انتظار عنوان IP")
          بينما (ip == '' && count 
}
   }
 المرحلة ("الإنتاج") {
      withKubeConfig ([credentialsId: 'jenkins-publisher-credentials'، serverUrl: 'https://104.155.31.202']) {
      
       sh 'kubectl create cm nodejs-app - من ملف - = src / --namespace = myapp-production -o = yaml --dry-run> publish / cm.yaml'
sh 'kubectl Apply -f publish / --namespace = myapp-production'
      
  
      // Gathering Node.js عنوان IP الخارجي للتطبيق
         def ip = ''
         عدد مواطنه = 0
         def countLimit = 10
        
         // حلقة انتظار لتوفير عنوان IP
         println ("في انتظار عنوان IP")
         بينما (ip == '' && count 

تثبيت جنكينز

لتثبيت Jenkins ، سنستخدم مخطط Helm المتوفر في المستودع الرسمي الثابت. لنشر Jenkins في Kubernetes مع المكونات الإضافية اللازمة ، استخدم الأمر التالي:

helm install --name مستقرة-جنكينز-نشر مستقر / جنكينز - الإصدار 0.16.1 - القيم jenkins-params.yaml

حيث jenkins-params.yaml كما يلي:

# جنكينز-params.yaml
رئيس:
  الصورة: جنكينز / جنكينز
  ImageTag: 2.121
  نوع الخدمة: LoadBalancer
  ServicePort: 80
  كلمة مرور المسؤول: admin_313
  InstallPlugins:
    - kubernetes: 1.5.2
    - مجمع سير العمل: 2.5
    - سير العمل - الوظيفة: 2.21
    - أوراق اعتماد ملزمة: 1.16
    - بوابة: 3.9.0
    - kubernetes-cli: 1.0.0
    - أدوات مخصصة المساعد: 0.5
    - bitbucket: 1.1.7
RBAC:
  تثبيت: صحيح
  apiVersion: v1
وكيل:
  الصورة: جنكينز / jnlp-slave
  ImageTag: 3.19-1
  أحجام:
  - النوع: EmptyDir
    mountPath: / usr / local / sbin

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

printf $ (kubectl get secret - مساحة الاسم الافتراضية my-jenkins-Publishing -o jsonpath = "{. data.jenkins-admin-password}" | base64 - الكود) ؛ صدى

إذا كان كل شيء على ما يرام ، يجب أن تشاهد إصدار helm النشر:

رأس ليرة لبنانية

بعد فترة من الوقت (حوالي 30 ثانية إلى دقيقة واحدة) ، قم بتشغيل الأمر أدناه للحصول على عنوان IP خارجي حيث يمكنك الوصول إلى مثيل Jenkins الخاص بك.

kubectl الحصول على الخدمات

في هذه الحالة ، 35.189.215.166. انتقل إلى عنوان IP هذا في المتصفح الخاص بك وقم بتسجيل الدخول كمشرف باستخدام كلمة المرور التي حصلت عليها سابقًا (لا تنسَ تغيير بيانات الاعتماد هذه :)).

تكوين kubectl في جنكينز للنشر المستمر

الآن ، دعونا نقوم بتكوين بيانات اعتماد Kubernetes حتى يمكن لـ Jenkins النشر في مجموعة Kubernetes الخاصة بنا.

علينا إنشاء ServiceAccount في Kubernetes ليتم استخدامها من قبل Jenkins للنشر.

kubectl خلق سا جنكينز ناشر
kubectl create jenkins-publisher-role-cluster - clusterrole = cluster-admin - serviceaccount = jenkins-publisher

ثم قم بتشغيل الأمر

kubectl الحصول على أسرار

يجب عليك تحديد السر الذي يبدأ بـ "jenkins-publisher" والحصول على بيانات الاعتماد المرتبطة به:

وصف kubectl سر جنكينز-ناشر-رمز-jvdmf

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

املأ النموذج كما يلي:

  • النوع: نص سري
  • النطاق: عالمي
  • سر: الرمز المميز المنسوخ من jenkins-publisher-token-jvdmf (سلسلة طويلة)
  • المعرف: بيانات اعتماد jenkins-publisher (كما هو مشار إليه في الوظيفة withKubeConfig في Jenkinsfile)

خلق جنكينز الوظيفة

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

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

أخيرًا ، انتقل إلى قسم خطوط الأنابيب وقم بتكوينه كما يلي:

  • التعريف: خط أنابيب من SCM
  • SCM: بوابة
  • المستودعات: عنوان URL الخاص بالمستودع: عنوان URL الخاص بمستودع التخزين الخاص بك

وهذا كل شيء ، ما عليك سوى حفظ الإعدادات الخاصة بك.

إطلاق سير العمل

لبدء سير العمل ، حدد خط الأنابيب الذي تم إنشاؤه مؤخرًا وانقر على "إنشاء الآن" في القائمة اليمنى. سيبدأ خط الأنابيب في بضع ثوان.

من الممكن أيضًا تشغيل سير العمل تلقائيًا عندما يرتكب المستخدم تغييرًا في مستودع git. هذه ممارسة موصى بها وفقًا لمبادئ CI / CD.

للقيام بذلك ، يجب عليك إنشاء webhook في مستودع git الخاص بك. بالنسبة إلى Bitbucket ، يجب اتباع الإرشادات الموضحة هنا. لاحظ أن عنوان URL الذي يجب أن تشير إليه في Bitbucket هو JENKINS_URL / bitbucket-hook /

في حالتنا:

http://35.189.215.166/bitbucket-hook/

خاتمة

لقد أظهرت سير عمل CI / CD بسيط مع Jenkins و Kubernetes. الفائدة الأساسية من هذه المجموعة هي المرونة حيث إنها تتيح لك تنفيذ أي نوع من سير العمل بشكل عملي. يمكن تمديد أو تعقيد سير العمل هذا حسب احتياجات التطوير الخاصة بك. في أي حال ، ستكون العملية أكثر كفاءة بكثير من الطريقة التقليدية.

أتمنى أن تجد هذا المنشور مفيدًا. يرجى التعليق وطرح الأسئلة - أحب أن أحصل على تعليقاتك. لا تنسَ متابعتنا على Twitter والانضمام إلى دردشة Telegram الخاصة بنا لتتنبه!

قد ترغب أيضًا في التحقق من مشروع Containerum على GitHub. نحتاج إلى تعليقات لجعلها أقوى - يمكنك إرسال مشكلة أو مجرد دعم المشروع عن طريق إعطائه a. دعمكم يهم حقا لنا!

Containerum هو مصدر المعرفة الخاصة بك على Kubernetes و Docker.