Jenkins Pipeline: ملاحظات التحسين. الجزء 1





اسمي إيليا غوليايف ، أنا مهندس أتمتة الاختبار في فريق التحقق بعد النشر في DINS.



في DINS ، نستخدم Jenkins في العديد من العمليات: من إنشاءات البناء إلى تشغيل عمليات النشر والاختبارات التلقائية. في فريقي ، نستخدم Jenkins كمنصة لإطلاق عمليات فحص الدخان بشكل موحد بعد نشر كل خدمة من خدماتنا من بيئات التطوير إلى الإنتاج.



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



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



يا له من خط أنابيب جنكينز الوحش



Jenkins Pipeline هي أداة قوية تسمح لك بأتمتة العمليات المختلفة. Jenkins Pipeline عبارة عن مجموعة من المكونات الإضافية التي تسمح لك بوصف الإجراءات في شكل Groovy DSL ، وهي خليفة المكون الإضافي Build Flow.



تم تنفيذ البرنامج النصي للمكوِّن الإضافي Build Flow مباشرةً على البرنامج الرئيسي في سلسلة Java منفصلة نفذت كود Groovy بدون حواجز تمنع الوصول إلى Jenkins API الداخلي. شكل هذا النهج خطرًا أمنيًا ، والذي أصبح فيما بعد أحد أسباب التخلي عن Build Flow ، وكان بمثابة شرط أساسي لإنشاء أداة آمنة وقابلة للتطوير لتشغيل البرامج النصية - Jenkins Pipeline.



يمكنك معرفة المزيد حول تاريخ إنشاء Jenkins Pipeline من مقالة المؤلف Build Flow أوحديث أوليج نيناشيف عن Groovy DSL في جينكينز .



كيف يعمل جينكينز بايبلاين



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



أوجه التشابه بين خطوط الأنابيب والوظائف الحرة



  • يتم تخزين وصف الوظيفة (وليس الخطوات) في ملف config.xml
  • يتم تخزين المعلمات في config.xml
  • يتم أيضًا تخزين المشغلات في config.xml
  • وحتى بعض الخيارات مخزنة في config.xml


وبالتالي. قف. تشير الوثائق الرسمية إلى أنه يمكن تعيين المعلمات والمشغلات والخيارات مباشرة في خط الأنابيب. اين الحقيقة؟



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



الفروق بين وظائف خطوط الأنابيب والوظائف الحرة



  • في وقت بدء الوظيفة ، لم يكن جينكينز يعرف أي شيء عن الوكيل لتنفيذ المهمة.
  • الإجراءات موصوفة في نص واحد رائع.


إطلاق خط أنابيب جينكينز التصريحي



تتكون عملية بدء تشغيل Jenkins Pipeline من الخطوات التالية:



  1. قم بتحميل وصف الوظيفة من ملف config.xml
  2. ابدأ خيطًا منفصلاً (أداء خفيف الوزن) لإكمال المهمة
  3. تحميل البرنامج النصي لخط الأنابيب
  4. بناء والتحقق من شجرة النحو
  5. تحديثات تكوين الوظائف
  6. الجمع بين المعلمات والخصائص المحددة في الوصف الوظيفي وفي البرنامج النصي
  7. حفظ الأوصاف الوظيفية في نظام الملفات
  8. تنفيذ برنامج نصي في صندوق رمل رائع
  9. طلب وكيل لوظيفة كاملة أو خطوة واحدة






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



لدعم هذا النهج ، يتم استخدام تجمع خيوط Jenkins خاص (منفذون خفيف الوزن). يمكنك أن ترى أنه يتم تنفيذها على المستوى الرئيسي ، لكن لا تؤثر على مجموعة المنفذين المعتادة:







عدد الخيوط في هذا التجمع غير محدود (في وقت كتابة هذا التقرير).



معلمات العمل في خط الأنابيب. وكذلك المشغلات وبعض الخيارات



يمكن وصف معالجة المعلمات بواسطة الصيغة:







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



كيف يعمل من الداخل الى الخارج؟



لنلقِ نظرة على مثال config.xml (الملف الذي يخزن ضبط الوظيفة):



<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job@2.35">
  <actions>
    <org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobAction plugin="pipeline-model-definition@1.5.0"/>
    <org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobPropertyTrackerAction plugin="pipeline-model-definition@1.5.0">
      <jobProperties>
        <string>jenkins.model.BuildDiscarderProperty</string>
      </jobProperties>
      <triggers/>
      <parameters>
        <string>parameter_3</string>
      </parameters>
    </org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobPropertyTrackerAction>
  </actions>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <hudson.model.ParametersDefinitionProperty>
      <parameterDefinitions>
        <hudson.model.StringParameterDefinition>
          <name>parameter_1</name>
          <description></description>
          <defaultValue></defaultValue>
          <trim>false</trim>
        </hudson.model.StringParameterDefinition>
        <hudson.model.StringParameterDefinition>
          <name>parameter_2</name>
          <description></description>
          <defaultValue></defaultValue>
          <trim>false</trim>
        </hudson.model.StringParameterDefinition>
        <hudson.model.StringParameterDefinition>
          <name>parameter_3</name>
          <description></description>
          <defaultValue></defaultValue>
          <trim>false</trim>
        </hudson.model.StringParameterDefinition>
      </parameterDefinitions>
    </hudson.model.ParametersDefinitionProperty>
    <jenkins.model.BuildDiscarderProperty>
      <strategy class="org.jenkinsci.plugins.BuildRotator.BuildRotator" plugin="buildrotator@1.2">
        <daysToKeep>30</daysToKeep>
        <numToKeep>10000</numToKeep>
        <artifactsDaysToKeep>-1</artifactsDaysToKeep>
        <artifactsNumToKeep>-1</artifactsNumToKeep>
      </strategy>
    </jenkins.model.BuildDiscarderProperty>
    <com.sonyericsson.rebuild.RebuildSettings plugin="rebuild@1.28">
      <autoRebuild>false</autoRebuild>
      <rebuildDisabled>false</rebuildDisabled>
    </com.sonyericsson.rebuild.RebuildSettings>
  </properties>
  <definition class="org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition" plugin="workflow-cps@2.80">
    <scm class="hudson.plugins.filesystem_scm.FSSCM" plugin="filesystem_scm@2.1">
      <path>/path/to/jenkinsfile/</path>
      <clearWorkspace>true</clearWorkspace>
    </scm>
    <scriptPath>Jenkinsfile</scriptPath>
    <lightweight>true</lightweight>
  </definition>
  <triggers/>
  <disabled>false</disabled>
</flow-definition>


يحتوي قسم الخصائص على المعلمات والمشغلات والخيارات التي سيتم بدء العمل بها. يتم استخدام قسم إضافي ، DeclarativeJobPropertyTrackerAction ، لتخزين المعلمات المحددة فقط في خط الأنابيب.



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



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



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



تنفيذ خط أنابيب جينكينز



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



تنفيذ كود Groovy يتم تنفيذ



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







تحتوي خطوط الأنابيب على مكون إضافي رائع يسمح لك بإجراء طلبات HTTP . بالإضافة إلى ذلك ، يمكن حفظ الإجابة في ملف.



httpRequest  url: 'http://localhost:8080/jenkins/api/json?pretty=true', outputFile: 'result.json'


في البداية ، قد يبدو أنه يجب تنفيذ هذا الرمز بالكامل على الوكيل ، وإرسال طلب من الوكيل ، وحفظ الرد على ملف result.json. لكن كل شيء يحدث في الاتجاه المعاكس ، ويتم تنفيذ الطلب من Jenkins نفسه ، ويتم نسخ محتويات الملف إلى الوكيل لحفظ محتويات الملف. إذا لم تكن هناك حاجة إلى معالجة إضافية للاستجابة في خط الأنابيب ، فإنني أنصحك باستبدال هذه الطلبات بـ curl:



sh  'curl "http://localhost:8080/jenkins/api/json?pretty=true" -o "result.json"'


العمل مع السجلات والقطع الأثرية

بغض النظر عن العامل الذي يتم تنفيذ الأوامر عليه ، تتم معالجة السجلات والتحف وحفظها في نظام الملفات الرئيسي في الوقت الفعلي.



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







خطوات التوفير (متانة خط الأنابيب)



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







اعتمادًا على متانة خط الأنابيب ، سيتم تخزين الخطوات في الرسم البياني لخط الأنابيب في ملف واحد أو أكثر لكل عملية تشغيل. مقتطفات من الوثائق :



يستخدم المكون الإضافي لدعم سير العمل الخاص بتخزين الخطوات (FlowNode) فئة FlowNodeStorage وتطبيقات SimpleXStreamFlowNodeStorage و BulkFlowNodeStorage.



  • يستخدم FlowNodeStorage التخزين المؤقت في الذاكرة لتجميع عمليات الكتابة على القرص. يتم كتابة المخزن المؤقت تلقائيًا في وقت التشغيل. بشكل عام ، لا داعي للقلق بشأن هذا الأمر ، ولكن ضع في اعتبارك أن حفظ FlowNode لا يضمن كتابتها على القرص على الفور.
  • يستخدم SimpleXStreamFlowNodeStorage ملف XML واحدًا صغيرًا لكل FlowNode - على الرغم من أننا نستخدم ذاكرة تخزين مؤقت مرجعية في الذاكرة للعقد ، إلا أن هذا يؤدي إلى أداء أسوأ بكثير عند اجتياز FlowNodes لأول مرة.
  • يستخدم BulkFlowNodeStorage ملف XML واحدًا أكبر يحتوي على جميع FlowNodes فيه. يتم استخدام هذه الفئة في وضع التشغيل PERFORMANCE_OPTIMIZED ، والذي يكتب بشكل أقل تكرارًا. هذا بشكل عام أكثر فاعلية لأن سجل دفق كبير واحد أسرع من مجموعة من السجلات الصغيرة ويقلل الحمل على نظام التشغيل لإدارة جميع الملفات الصغيرة.




أصلي
Storage: in the workflow-support plugin, see the 'FlowNodeStorage' class and the SimpleXStreamFlowNodeStorage and BulkFlowNodeStorage implementations.



  • FlowNodeStorage uses in-memory caching to consolidate disk writes. Automatic flushing is implemented at execution time. Generally, you won't need to worry about this, but be aware that saving a FlowNode does not guarantee it is immediately persisted to disk.
  • The SimpleXStreamFlowNodeStorage uses a single small XML file for every FlowNode — although we use a soft-reference in-memory cache for the nodes, this generates much worse performance the first time we iterate through the FlowNodes (or when)
  • The BulkFlowNodeStorage uses a single larger XML file with all the FlowNodes in it. This is used in the PERFORMANCE_OPTIMIZED durability mode, which writes much less often. It is generally much more efficient because a single large streaming write is faster than a bunch of small writes, and it minimizes the system load of managing all the tiny files.


يمكن العثور على الخطوات المحفوظة في الدليل:



$JENKINS_HOME/jobs/$JOB_NAME/builds/$BUILD_ID/workflow/




ملف مثال:



<?xml version='1.1' encoding='UTF-8'?>
<Tag plugin="workflow-support@3.5">
  <node class="cps.n.StepStartNode" plugin="workflow-cps@2.82">
    <parentIds>
      <string>4</string>
    </parentIds>
    <id>5</id>
    <descriptorId>org.jenkinsci.plugins.workflow.support.steps.StageStep</descriptorId>
  </node>
  <actions>
    <s.a.LogStorageAction/>
    <cps.a.ArgumentsActionImpl plugin="workflow-cps@2.82">
      <arguments>
        <entry>
          <string>name</string>
          <string>Declarative: Checkout SCM</string>
        </entry>
      </arguments>
      <isUnmodifiedBySanitization>true</isUnmodifiedBySanitization>
    </cps.a.ArgumentsActionImpl>
    <wf.a.TimingAction plugin="workflow-api@2.40">
      <startTime>1600855071994</startTime>
    </wf.a.TimingAction>
  </actions>
</Tag>


النتيجة



آمل أن تكون هذه المادة مثيرة للاهتمام وساعدت على فهم أفضل لخطوط الأنابيب وكيف تعمل من الداخل. إذا كان لا يزال لديك أسئلة - شاركها أدناه ، فسيسعدني الإجابة!



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



All Articles