كيفية الأتمتة مع بناء Jenkins والتحف المتدرجة لعناصر نموذج البيانات الوصفية للجداول في المستودع

بدأ كل شيء بحقيقة أننا واجهنا الحاجة إلى تشكيل هياكل EDWEX و JSON و DDL بسرعة وبشكل صحيح ثم تدويرها على خطوط مختلفة من قواعد البيانات العلائقية. أقصد بالخطوط المختصرة الاختصارات التي يعرفها الجميع - DEV ، TST ، UAT ، PRD.



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

قليلا عن البنية التحتية


قبل أن نملأ البيانات في جداول قواعد البيانات العلائقية ، نحتاج إلى قبولها من المصدر - بأي تنسيق ، على سبيل المثال ، في Excel. يتم نقل البيانات من المصدر باستخدام الآلية الداخلية إلى Hadoop (Data Lake). يحتوي Hive على الوظيفة الإضافية Hive مثبتة - وبمساعدته يمكننا عرض محتويات الملفات باستخدام بناء جملة يشبه SQL.

لنقل البيانات في Data Lake في شكل جداول ، نحتاج إلى json ، والتي يتم تشكيلها على أساس البيانات الوصفية. لتحليل البيانات بتنسيق Excel ، نحتاج إلى إنشاء ملفات EDWEX. ملفات EDWEX (EDW_EXTRACTOR) هي مجموعة معينة من القطع الأثرية التي تحتوي على مجموعات من الجداول بالاسم ، بالإضافة إلى مجموعات من الحقول لكل من هذه الجداول ، والتي يتم تشكيلها على أساس البيانات الوصفية. بناءً على إصدار النموذج ومعرف المصدر ، تختلف مجموعة الحقول. إن تكوين DDL ضروري لإنشاء الجداول نفسها في قاعدة بيانات Hive على مستوى البيانات التشغيلية وفي قاعدة بيانات Greenplum على مستوى البيانات التفصيلية والمجمعة. بمعنى ، يتم نقل البيانات في المقام الأول إلى الخلية ، إذا لزم الأمر ، يتم تصفيتها ونقلها إلى Greenplum لمعالجة البيانات لاحقًا وإنشاء واجهات متاجر بناءً عليها.

مثال قطعة أثرية Edwex
حزمة - تحتوي على مجموعة من الجداول
البيانات - تحتوي على مجموعة من الحقول

pack.edwex:

1 Table_1 User Table_1 bid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 2 Table_2 User Table_2 curbid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 3 Table_3 User Table_3 bid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 


data.edwex:

 1 1 CHARGE_ID NUMBER 38 0 1 2 SVC_ID NUMBER 38 0 1 3 VND_ID NUMBER 38 0 1 4 PRICE NUMBER 38 5 1 5 QUANTITY NUMBER 38 5 1 6 BASE NUMBER 38 5 1 7 TAX NUMBER 38 5 1 8 TOTAL NUMBER 38 5 1 9 TAX_RATE NUMBER 38 5 1 10 TAX_IN VARCHAR 1 1 11 CHARGE_KIND VARCHAR 3 1 12 PRIVILEGE_ID NUMBER 38 0 1 13 CHARGE_REF_ID NUMBER 38 0 1 14 EBID NUMBER 38 0 1 15 INVOICE_ID NUMBER 38 0 1 16 ZERO_STATE_ID NUMBER 38 0 1 17 USER_ID NUMBER 38 0 1 18 BID NUMBER 38 0 1 19 QUANTITY_REAL NUMBER 38 5 2 1 CURBID NUMBER 38 0 2 2 USER_ID NUMBER 38 0 2 3 VND_ID NUMBER 38 0 2 4 APPBID NUMBER 38 0 2 5 SVC_ID NUMBER 38 0 2 6 DEBT NUMBER 38 5 2 7 INSTDEBT NUMBER 38 5 3 1 INVOICE_ID NUMBER 38 0 3 2 INVOICE_DATE DATE 3 3 INVOICE_NUM VARCHAR 64 3 4 INVOICE_NUM_N NUMBER 38 5 3 5 BASE NUMBER 38 5 3 6 TAX NUMBER 38 5 3 7 TOTAL NUMBER 38 5 3 8 PREPAID VARCHAR 1 3 9 EXPLICIT VARCHAR 1 3 10 VND_ID NUMBER 38 0 3 11 ADV_PAYMENT_ID NUMBER 38 0 3 12 MDBID NUMBER 38 0 3 13 BID NUMBER 38 0 3 14 USER_ID NUMBER 38 0 3 15 ZERO_STATE_ID NUMBER 38 0 3 16 ACTIVE_SUM NUMBER 38 5 3 17 SPLIT_VND NUMBER 38 5 3 18 PRECREATED VARCHAR 1 


مثال قطعة أثرية Json
 Table.json: { "metadata": [ { "colOrder":"1", "name":"charge_id", "dataType":"DECIMAL", "precision":"0", "requied":"true", "keyFile":"" }, { "colOrder":"2", "name":"svc_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"3", "name":"vnd_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"4", "name":"price", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"5", "name":"quantity", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"6", "name":"base", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"7", "name":"tax", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"8", "name":"total", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"9", "name":"tax_rate", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"10", "name":"tax_in", "dataType":"STRING", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"11", "name":"charge_kind", "dataType":"STRING", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"12", "name":"privilege_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"13", "name":"charge_ref_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"14", "name":"ebid", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"15", "name":"invoice_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"16", "name":"zero_state_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"17", "name":"user_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"18", "name":"bid", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"19", "name":"quantity_real", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" } ], "ctlPath":"  ctl", "errPath":"    ", "inPath":"    hdfs", "outSchema":" ", "outTable":" ", "isPartitioned":" ", "sourceId":"id  " } 


مثال التحف DDL
تغيير مخطط الجدول. GP_000001_TABLE إعادة التسمية إلى Z_GP_000001_TABLE_20180807 ؛
إنشاء مخطط جدول. GP_000001_TABLE
(

`charge_id` DECIMAL (38.0) ،
`svc_id` عشري (38.0) ،
`vnd_id` DECIMAL (38.0) ،
`السعر` العشري (38.5) ،
`الكمية` العشرية (38.5) ،
`الأساس` العشري (38.5) ،
`الضريبة` العشرية (38.5) ،
`المجموع` العشري (38.5) ،
`tax_rate` العشري (38.5) ،
`tax_in` STRING ،
`charge_kind` STRING ،
`ivilege_id` DECIMAL (38.0) ،
`charge_ref_id` عشري (38.0) ،
`عبيد` عشري (38.0) ،
`invoice_id` عشري (38.0) ،
`zero_state_id` عشري (38،0) ،
`user_id` DECIMAL (38.0) ،
`bid` DECIMAL (38.0) ،
`Quant_real` عشرية (38.5) ،
`load_dttm` TIMESTAMP ،
`src_id` SMALLINT ،
`package_id` BIGINT ،
`wf_run_id` BIGINT ،
`md5` STRING
)
تنسيق حقول الصفوف المحددة بواسطة "\ t"
يتم تخزينها كـ INGUT.Opache.hadache.hadive.qive.ql.io.HiveIgnoreKeyTextOutputFormat ". org.apache.hadoop.mapred.TextInputFormat
LOCATION "طريقة للتكوين بتنسيق hdfs"
TBLPROPERTIES ('auto.purge' = 'true'، 'transient_lastDlastDdlTime' = '1489060201')؛
أدخل في المخطط. GP_000001_TABLE (`charge_id` ،
`svc_id` ،
`vnd_id` ،
`السعر` ،
"الكمية" ،
`القاعدة` ،
"ضريبة" ،
"الإجمالي" ،
`tax_rate` ،
"الضريبة" ،
"تهمة_نوع" ،
"الامتياز" ،
`charge_ref_id` ،
"عبيد" ،
`invoice_id` ،
"zero_state_id" ،
"user_id` ،
محاولة
`Quant_real` ،
`load_dttm` ،
`src_id` ،
package_id
`wf_run_id` ،
`MD5`)

حدد `charge_id` ،
`svc_id` ،
`vnd_id` ،
`السعر` ،
"الكمية" ،
`القاعدة` ،
"ضريبة" ،
"الإجمالي" ،
`tax_rate` ،
"الضريبة" ،
"تهمة_نوع" ،
"الامتياز" ،
`charge_ref_id` ،
"عبيد" ،
`invoice_id` ،
"zero_state_id" ،
"user_id` ،
محاولة
`Quant_real` ،
load_dttm ،
src_id
package_id ،
wf_run_id ،
MD5 من المخطط. Z_GP_000001_TABLE_20180807 ؛

تغيير مخطط الجدول. GP_000001_TABLE إعادة التسمية إلى Z_GP_000001_TABLE_20180807 ؛
إنشاء مخطط جدول. GP_000001_TABLE
(

`charge_id` DECIMAL (38.0) ،
`svc_id` عشري (38.0) ،
`vnd_id` DECIMAL (38.0) ،
`السعر` العشري (38.5) ،
`الكمية` العشرية (38.5) ،
`الأساس` العشري (38.5) ،
`الضريبة` العشرية (38.5) ،
`المجموع` العشري (38.5) ،
`tax_rate` العشري (38.5) ،
`tax_in` STRING ،
`charge_kind` STRING ،
`ivilege_id` DECIMAL (38.0) ،
`charge_ref_id` عشري (38.0) ،
`عبيد` عشري (38.0) ،
`invoice_id` عشري (38.0) ،
`zero_state_id` عشري (38،0) ،
`user_id` DECIMAL (38.0) ،
`bid` DECIMAL (38.0) ،
`Quant_real` عشرية (38.5) ،
`load_dttm` TIMESTAMP ،
`src_id` SMALLINT ،
`package_id` BIGINT ،
`wf_run_id` BIGINT ،
`md5` STRING
)
تنسيق حقول الصفوف المحددة بواسطة "\ t"
يتم تخزينها كـ INGUT.Opache.hadache.hadive.qive.ql.io.HiveIgnoreKeyTextOutputFormat ". org.apache.hadoop.mapred.TextInputFormat
LOCATION "طريقة للتكوين بتنسيق hdfs"
TBLPROPERTIES ('auto.purge' = 'true'، 'transient_lastDlastDdlTime' = '1489060201')؛
أدخل في المخطط. GP_000001_CPA_CHARGE (`charge_id` ،
`svc_id` ،
`vnd_id` ،
`السعر` ،
"الكمية" ،
`القاعدة` ،
"ضريبة" ،
"الإجمالي" ،
`tax_rate` ،
"الضريبة" ،
"تهمة_نوع" ،
"الامتياز" ،
`charge_ref_id` ،
"عبيد" ،
`invoice_id` ،
"zero_state_id" ،
"user_id` ،
محاولة
`Quant_real` ،
`load_dttm` ،
`src_id` ،
package_id
`wf_run_id` ،
`MD5`)

حدد `charge_id` ،
`svc_id` ،
`vnd_id` ،
`السعر` ،
"الكمية" ،
`القاعدة` ،
"ضريبة" ،
"الإجمالي" ،
`tax_rate` ،
"الضريبة" ،
"تهمة_نوع" ،
"الامتياز" ،
`charge_ref_id` ،
"عبيد" ،
`invoice_id` ،
"zero_state_id" ،
"user_id` ،
محاولة
`Quant_real` ،
load_dttm ،
src_id
package_id ،
wf_run_id ،
MD5 من المخطط. Z_GP_000001_TABLE_20180807 ؛

كيفية الأتمتة


لحل المشكلة استخدمنا:

  • Jenkins - كأوركسترا وأداة لتنفيذ عملية CI.
  • Python - تقوم بتنفيذ الوظائف واختبارات الوحدة.
  • SQL - للوصول إلى قاعدة بيانات البيانات الوصفية.
  • Shell script - لنسخ القطع الأثرية بين الدلائل وإنشاء البرامج النصية في مشاريع multijob التي تعمل على خادم Jenkins.

بادئ ذي بدء ، نعمل في البداية مع عدد كبير من المصادر ، وبالتالي ، باستخدام برنامج نصي نصي كمعلمة بدء ، نقوم بتمرير معرفات المصادر إلى وظائف SQL للتعرف عليها. سيتم بعد ذلك تنفيذ وظائف SQL الناتجة تلقائيًا في قاعدة بيانات البيانات الوصفية. استنادًا إلى البيانات الوصفية المتاحة ، تشكل هذه الوظائف ملفًا يحتوي على قائمة بوظائف SQL الجديدة لكل من جداول المصدر حتى نتمكن من استدعاؤها لاحقًا في البرنامج القابل للتنفيذ. نتيجة تنفيذ الدوال التي تم إنشاؤها هي نقل قيم DDL أو JSON أو EDWEX إلى معلمة الإخراج.

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

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

وأين جنكينز هنا؟


تدخل Jenkins عندما يكون من الضروري إدارة كل هذه العمليات بشكل مرئي باستخدام واجهة.

تم اختيار هذه الأداة لأنها:

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

لحل المهام قمنا بإنشاء العديد من مشاريع multijob. تم استخدام هذا النوع من المشاريع ، لأنه يمكن أن يعمل بالتوازي مع وظائف أخرى في بداية واحدة. كل وظيفة مسؤولة عن تنفيذ مجموعة وظائفها. لذلك استبدلنا العملية المتسلسلة للحصول على القطع الأثرية بأخرى متوازية مستقلة. يبدأ كل شيء بشكل منفصل: تشكيل EDWEX ، JSON ، DDL ، تشكيل الهيكل في HIVE ، تركيب هياكل الجدول في قاعدة البيانات. نقوم بتحليل النتيجة في مراحل مختلفة من تكوين القطع الأثرية ونشرع في إطلاق الإجراءات اللاحقة إذا نجحت.

يتم تنفيذ جزء Jenkins دون الكثير من الحيل. يتم إرسال معلمات السلسلة أو التشغيل إلى الإدخال لبدء رمز الثعبان. تعتبر المعلمة String نافذة لإدخال قيمة من النوع str قبل البدء. يمكن نقل معلمة التشغيل على الطاير إلى وظيفة أخرى للتنفيذ ، وهذا يكفي فقط للإشارة من المشروع الذي من الضروري أخذ المتغير المستلم. أيضا ، يتم تمرير العقدة كمعلمة منفصلة للتنفيذ. هنا يتم تنفيذ التقسيم إلى أوقات التشغيل على DEV و TST و UAT و PRD. تم استخدام مهمة منفصلة لنقل ملفات EDWEX المستلمة إلى SVN برقم المراجعة حتى تتمكن من تتبع إصدارات الهياكل المتغيرة.

مثال واجهة في جنكينز:



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


هيكل آلية التجميع والتركيب

لتلخيص


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

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

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


All Articles