الوحدات: بنية تحتية قابلة لإعادة الاستخدام
الوحدات: بنية تحتية قابلة لإعادة الاستخدام
في Google وAmazon، لا يقوم أيُّ فريق بإنشاء S3 bucket أو VPC بكتابة كتل موارد خام من الصفر. بل يستهلكون وحدة داخلية — حزمة مُعتمَدة ومُختبَرة تُجسّد كل خط الأمان القياسي ومتطلبات الوسوم. الوحدات (Modules) هي الآلية التي ترقى بـ Terraform من سكريبتات شخصية إلى منصات بنية تحتية على مستوى المنظمة. حين تبدأ بالشعور بأن نسخ كتل الموارد بين المجلدات أمر خاطئ، تكون الوحدات هي الجواب.
ما هي الوحدة فعلياً؟
وحدة Terraform هي ببساطة مجلد يحتوي على ملفات .tf. كل مشروع Terraform هو بالفعل وحدة — المجلد الذي تُشغّل منه terraform init يُسمى الوحدة الجذر (root module). حين تستدعي مجلداً آخر (أو حزمة بعيدة) من جذرك، فذلك يُعدّ وحدة فرعية (child module). لا يوجد تركيب خاص للإعلان عن الوحدة؛ الحدود هي مسار نظام الملفات.
الملفات الثلاثة التي ستجدها تقريباً دائماً في وحدة منظَّمة جيداً هي:
main.tf— تعريفات المواردvariables.tf— إعلانات متغيرات المدخلاتoutputs.tf— القيم التي تُعرّضها الوحدة لمن يستدعيها
اختيارياً: versions.tf (قيود المزود المطلوبة) وREADME.md (توثيق للإنسان، إلزامي في Terraform Registry).
كتابة وحدة محلية
ابدأ بمثال بسيط لكنه واقعي: وحدة S3 bucket قابلة لإعادة الاستخدام تُطبّق التحكم في الإصدارات والتشفير من جانب الخادم وحجب الوصول العام — وهي الخط الأساسي الذي يجب أن يتمتع به كل bucket في الإنتاج.
تُغلّف الوحدة أربعة موارد خلف واجهة من متغيرَين. لا يحتاج المستهلك أن يعرف شيئاً عن SSE أو حجب الوصول العام — تلك القرارات مُشفَّرة مرة واحدة داخل الوحدة.
استهلاك وحدة باستخدام كتلة module
في الوحدة الجذر، قم بربط الوحدة الفرعية باستخدام كتلة module. وسيط source يخبر Terraform بمكان العثور على الوحدة. الوسيطات المتبقية تُحوَّل إلى إعلانات variable في الوحدة الفرعية.
بعد إضافة كتلة module جديدة، يجب عليك تشغيل terraform init قبل terraform plan. يقوم init بتنزيل أو نسخ مصادر الوحدات إلى ذاكرة التخزين المؤقت .terraform/modules/. إغفال هذه الخطوة هو أكثر أخطاء "module not found" شيوعاً.
.terraform/ إلى Git أبداً. يحتوي هذا المجلد على مزودين محمّلين ونسخ من مصادر الوحدات — قد يصل حجمه إلى مئات الميغابايتات وهو قابل للاسترداد الكامل بتشغيل terraform init. أضف .terraform/ و*.tfstate إلى .gitignore. ملف state الوحيد الذي قد يُوجد في Git هو *.tfstate مستخدم للتطوير المحلي في بيئة sandbox شخصية — حتى هذا غير مشجَّع.
مصادر الوحدات والإصدارات
يقبل وسيط source خمسة أنواع من المصادر، والاختيار له تداعيات تشغيلية حقيقية:
- مسار محلي (
./modules/vpc) — أسرع دورة تطوير، لا شبكة ولا إصدارات. استخدمه للوحدات التي تعيش في المستودع ذاته مع التكوين الجذر (أسلوب monorepo). التغييرات يتم التقاطها فوراً عندinitالتالي. - Terraform Registry (
hashicorp/consul/aws) — السجل العام علىregistry.terraform.io. يُصنَّف عبرversion. وحدات المجتمع مثلterraform-aws-modules/vpc/awsهي نقطة البداية القياسية في الصناعة. ثبّت دائماً إصداراً. - GitHub / GitLab (
git::https://github.com/org/repo.git//subdir?ref=v1.2.3) — مفيد للوحدات الخاصة قبل إعداد سجل خاص. ثبّت على وسم (tag) أو SHA لالتزام، لا على اسم فرع في الإنتاج. - سجل خاص (Terraform Cloud, Spacelift, Env0) — النمط المؤسسي. نفس تركيب
sourceكالسجل العام لكنه مُصادَق. يُتيح إصدارات دلالية وخطوط اختبار آلية وضوابط وصول على إصدارات الوحدات. - S3 / GCS bucket (
s3::https://s3.amazonaws.com/bucket/modules/vpc.zip) — آلية توزيع خاصة خفيفة دون الحاجة لسجل كامل.
= 5.8.0 يُثبّت بالضبط (هش — يحجب تصحيحات الأمان). ~> 5.8 (القيد التشاؤمي) يسمح بترقيات patch وminor ضمن نفس الإصدار الرئيسي، وهو الافتراضي الموصى به للوحدات من جهات خارجية. >= 5.0, < 6.0 نطاق صريح مفيد حين تعلم أن تغييراً جوهرياً قادماً في 6.0. شغّل terraform get -update لسحب أحدث إصدار مسموح به إلى ذاكرة التخزين المؤقت للوحدة.
تركيب الوحدات وتسلسل المخرجات
تصبح الوحدات قوية حين تتشابك مخرجاتها. النمط هو: وحدة توفر مورداً، ووحدة ثانية تستهلك مخرجها كمتغير مدخل، ويبني Terraform رسم التبعيات تلقائياً.
في كتلة الكود أعلاه، يرجع module.vpc.vpc_id وmodule.vpc.private_subnets إلى مخرجات يُصدرها وحدة VPC. يرى Terraform هذا المرجع العابر للوحدات ويضمن أن VPC مُجهَّز بالكامل قبل أي محاولة لإنشاء EKS cluster. لا تحتاج أبداً إلى إدارة depends_on لمراجع المخرجات العابرة للوحدات — الرسم البياني ضمني.
اختبار وإصدار وحداتك الخاصة
حين تُدير وحدات تستهلكها فرق أخرى، تحتاج إلى نظام إصدار. النمط القياسي يُشبه كيفية إصدار مكتبة برمجية:
- احتفظ بالوحدات في مستودع مخصص (أو مجلد فرعي محدد جيداً في monorepo). لا تخلط كود الوحدات مع التكوين على مستوى الجذر.
- ضع وسماً لكل إصدار بـوسم semver (
v1.0.0،v1.1.0). يُثبّت المستهلكون على وسم، لا على فرع. - اكتب اختبارات آلية مع Terratest (مبني على Go) أو أمر Terraform المدمج
terraform test(مبني على HCL، متاح منذ Terraform 1.6 / OpenTofu 1.7). تُجهّز الاختبارات بنية تحتية حقيقية في حساب AWS معزول، تتحقق من المخرجات، وتُدمّر كل شيء عند الانتهاء. - شغّل مجموعة الاختبارات في CI على كل pull request قبل الدمج والوسم.
هذا الخط — PR → اختبارات CI لبنية تحتية حقيقية → دمج → وسم → المستهلكون يُحدّثون تثبيت الإصدار — هو المعيار في Gruntwork وHashiCorp وفرق هندسة المنصات الكبيرة التي تنشر كتالوجات وحدات داخلية لآلاف المهندسين.