ما الذي يُرسل فعليًا إلى بيئاتك (ولماذا يهم ذلك)

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

ثم يقول أحدهم: "دعني أعيد البناء للإنتاج، فقط لأكون آمنًا. سأضيف إصلاح التكوين الأخير."

البناء الإنتاجي يختلف عن بناء الاختبار. طابع زمني مختلف. ترجمة مختلفة. ربما تم سحب إصدار مكتبة مختلف قليلاً.

ينهار الإنتاج بعد ساعة. الآن لديك سؤال لا يمكنك الإجابة عليه: هل الخلل في الكود الذي كان مختلفًا، أم في تكوين البيئة؟ لقد فقدت القدرة على التأكد.

هذه هي المشكلة التي تحلها القطع الأثرية غير القابلة للتغيير. لكن قبل أن نصل إلى هناك، دعنا نتحدث عن ماهية القطعة الأثرية في الواقع.

الكود المصدري ليس ما يعمل على الخوادم

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

تسمى عملية التحويل هذه بالبناء. ومخرجات البناء تسمى قطعة أثرية.

يعتمد شكل القطعة الأثرية على ما تقوم ببنائه:

  • تطبيق Java ينتج ملف .jar أو .war.
  • تطبيق Python ينتج ملف wheel أو مجلدًا معبأً.
  • تطبيق Node.js ينتج مجلد dist بملفات مصغرة.
  • تطبيق Go ينتج ملفًا ثنائيًا قابلاً للتنفيذ.

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

خط أنابيب البناء ينتج القطعة الأثرية

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

إليك مقارنة بصرية بين النهج الصحيح والنمط المعاكس المحفوف بالمخاطر:

flowchart TD A["الكود المصدري"] --> B["البناء"] B --> C["القطعة الأثرية"] C --> D["سجل القطع الأثرية"] D --> E["الاختبار"] E --> F["الإنتاج"] B -.-> G["إعادة البناء للاختبار"] G -.-> H["القطعة الأثرية أ"] H -.-> I["الاختبار"] B -.-> J["إعادة البناء للإنتاج"] J -.-> K["القطعة الأثرية ب"] K -.-> L["الإنتاج"] style A fill:#e6f3ff,stroke:#333 style B fill:#e6f3ff,stroke:#333 style C fill:#d4edda,stroke:#333 style D fill:#d4edda,stroke:#333 style E fill:#d4edda,stroke:#333 style F fill:#d4edda,stroke:#333 style G fill:#f8d7da,stroke:#333 style H fill:#f8d7da,stroke:#333 style I fill:#f8d7da,stroke:#333 style J fill:#f8d7da,stroke:#333 style K fill:#f8d7da,stroke:#333 style L fill:#f8d7da,stroke:#333

هذا هو المكان الذي يكون فيه لمعظم الفرق خيار. ومعظم الفرق تتخذ الخيار الخاطئ دون أن تدرك ذلك.

مشكلة إعادة البناء لكل بيئة

إليك نمط شائع: بناء للاختبار، اختبره، ثم أعد البناء للإنتاج. المنطق يبدو معقولاً - "نريد التأكد من أن الإنتاج يحصل على أحدث بناء."

لكن هذا النمط يخلق خطرًا خفيًا. كل بناء يختلف قليلاً. قد ينتج المترجم مخرجات مختلفة. قد تحل التبعيات إلى إصدارات مختلفة قليلاً. يتغير الطابع الزمني للبناء. حتى ترتيب كتابة الملفات يمكن أن يختلف.

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

لقد فقدت اليقين. واليقين هو أثمن شيء يمكن أن تمتلكه أثناء حادث.

القطع الأثرية غير القابلة للتغيير تستعيد اليقين

القطعة الأثرية غير القابلة للتغيير هي تلك التي لا تتغير أبدًا بعد بنائها. بمجرد أن ينتجها البناء، يتم تجميد تلك القطعة الأثرية. لا تعديلات. لا إعادة بناء. لا تحرير يدوي.

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

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

إليك طريقة سريعة للتحقق من نشر نفس القطعة الأثرية في كل مكان:

# على جهاز البناء بعد اكتمال البناء
sha256sum myapp-v1.2.3.jar
# المخرجات: a1b2c3d4e5f6...  myapp-v1.2.3.jar

# على خادم الاختبار بعد النشر
sha256sum /opt/myapp/myapp-v1.2.3.jar
# المخرجات: a1b2c3d4e5f6...  /opt/myapp/myapp-v1.2.3.jar

# على خادم الإنتاج بعد النشر
sha256sum /opt/myapp/myapp-v1.2.3.jar
# المخرجات: a1b2c3d4e5f6...  /opt/myapp/myapp-v1.2.3.jar

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

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

القطع الأثرية غير القابلة للتغيير تجعل التراجع بسيطًا

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

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

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

أين تخزن القطع الأثرية؟

تحتاج القطع الأثرية إلى موقع تخزين مركزي وآمن وموثوق. يسمى هذا مستودع القطع الأثرية. تشمل الخيارات الشائعة:

  • Nexus أو Artifactory لتخزين القطع الأثرية للأغراض العامة
  • Docker Registry لصور الحاويات
  • دلاء S3 أو Azure Blob Storage لملفات القطع الأثرية الخام
  • سجلات الحزم مثل npm أو PyPI أو Maven Central

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

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

ما يُرسل إلى البيئات

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

قطعة أثرية واحدة لجميع البيئات. متسقة، قابلة للتتبع، وغير قابلة للتغيير.

ينطبق هذا المبدأ سواء كنت تنشر خدمة Java مصغرة، أو خط أنابيب بيانات Python، أو واجهة أمامية Node.js، أو أداة سطر أوامر Go. يتغير تنسيق التعبئة، لكن الفكرة تبقى كما هي: ابنِ مرة واحدة، وانشر في كل مكان.

قائمة مراجعة سريعة لفريقك

إذا كنت تقوم بإعداد أو مراجعة استراتيجية القطع الأثرية الخاصة بك، إليك بعض الأشياء للتحقق منها:

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

الخلاصة

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

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