أساسيات Terraform

البنية التحتية ككود وTerraform

18 دقيقة الدرس 1 من 30

البنية التحتية ككود وTerraform

قبل Terraform، كان توفير بيئة سحابية يعني النقر عبر واجهة الويب، أو تشغيل أوامر AWS CLI بشكل عشوائي، أو كتابة سكريبتات هشة تعمل مرة واحدة ثم تصبح معرفة قبلية لا يجرؤ أحد على تعديلها. النتيجة كانت بنية تحتية كندفة الثلج (Snowflake) — كل بيئة مختلفة قليلاً عن الأخرى، يستحيل إعادة إنتاجها بشكل موثوق، ومخيفة عند محاولة تغييرها.

البنية التحتية ككود (IaC) هي ممارسة وصف بنيتك التحتية في ملفات قابلة للقراءة البشرية ومحفوظة في نظام تحكم بالإصدارات، والسماح لأداة بتوفيرها وإدارتها من تلك الوصف. في Google وMeta وكل بيئة سحابية جادة، لا يُنشأ أي مورد إنتاجي بالنقر. كل شيء — الـ VPCs وصلاحيات IAM وكلاسترات EKS وقواعد بيانات RDS وسجلات DNS — مُعلَن ككود، مراجع عبر Pull Request، ومُطبَّق من خلال pipeline آلي. سجل التدقيق موجود في Git؛ نطاق تأثير أي تغيير مرئي قبل تنفيذه.

IaC التصريحي مقابل الأمري

هناك مقاربتان لـ IaC: أمرية (Imperative) وتصريحية (Declarative). كتب تشغيل Ansible والسكريبتات الشل أمرية — تُحدد الخطوات للوصول إلى الحالة المطلوبة. Terraform تصريحي — تُحدد كيف يجب أن تبدو الحالة النهائية وتترك لـ Terraform تحديد الخطوات.

للنموذج التصريحي ميزة حاسمة على نطاق واسع: الاتساق (Idempotency). تشغيل terraform apply عشر مرات على نفس الإعداد ينتج نفس البنية التحتية في كل مرة. لا آثار جانبية، لا انجراف تراكمي. مع السكريبت الأمري، تشغيله مرتين قد يُنشئ موارد مكررة، أو يفشل على كائنات موجودة بالفعل، أو يترك البيئة منصفة جزئياً.

لماذا يسيطر Terraform على IaC في 2025: Terraform (وفرعه مفتوح المصدر OpenTofu) مستقل عن المزود — نفس سير العمل يدير AWS وGCP وAzure وCloudflare وGitHub وDatadog وKubernetes وأكثر من 3,000 مزود آخر عبر واجهة متسقة. على عكس AWS CloudFormation (AWS فقط) أو قوالب Azure ARM (Azure فقط)، يمكن لقاعدة كود Terraform واحدة تنسيق كامل بصمتك متعددة السحاب وSaaS. لهذا هو الأداة الافتراضية لـ IaC في المنظمات الهندسية الكبيرة التي تعمل على أكثر من سحابة واحدة.

حلقة Plan / Apply

سير عمل Terraform الجوهري هو حلقة ثلاثية الأطوار: كتابة → خطة → تطبيق. هذا هو الانضباط الذي يفصل IaC الاحترافي عن أسلوب "اكتب سكريبت وادعُ ألا يفشل".

الكتابة: تؤلف ملفات .tf تصف الموارد التي تريدها. يستخدم Terraform لغة HCL (HashiCorp Configuration Language)، وهي لغة إعداد مصممة لتكون مقروءة من البشر والآلات.

الخطة: يحسب terraform plan الفرق بين إعدادك المُعلَن وحالة العالم الحقيقية (المُتتبَّعة في ملف الحالة). يطبع فرقاً دقيقاً — أي موارد ستُنشأ أو تُغيَّر أو تُحذف — دون لمس أي شيء. هذا هو فحصك قبل الإقلاع. في pipelines CI الإنتاجية، يُنشر ناتج الخطة كتعليق PR حتى يتمكن المراجعون من الموافقة على التغييرات الدقيقة قبل تنفيذها.

التطبيق: ينفّذ terraform apply الخطة. يستدعي Terraform APIs المزودين بترتيب التبعية، يُنشئ الموارد بالتوازي حيث أمكن، ويُحدّث ملف الحالة لتسجيل ما أصبح موجوداً.

# 1. تهيئة مجلد العمل (تنزيل المزودين، إعداد الـ backend) terraform init # 2. رؤية ما سيتغير بالضبط — لا آثار جانبية terraform plan # 3. تطبيق التغييرات (يطلب التأكيد؛ استخدم -auto-approve في CI) terraform apply # 4. حذف جميع الموارد المُدارة (استخدم بحذر شديد في الإنتاج) terraform destroy
شغّل دائماً terraform plan قبل apply، حتى محلياً. في الإنتاج، اجعل أثر الخطة إلزامياً: أنشئه بـ terraform plan -out=tfplan، خزّنه كـ artifact في CI، ثم طبّق تلك الخطة بالضبط بـ terraform apply tfplan. هذا يضمن أن ما تمت مراجعته هو ما يُطبَّق فعلاً — لا شروط سباق حيث تتغير البيئة بين الخطة والتطبيق.

المزودون: نموذج امتداد Terraform

لا معرفة لـ Terraform نفسه بـ AWS أو Kubernetes أو Cloudflare. جميع أنواع الموارد تعيش في المزودين (Providers) — إضافات تترجم تعريفات الموارد التصريحية لـ Terraform إلى استدعاءات API حقيقية. مزود AWS يستدعي AWS APIs؛ مزود Kubernetes يتحدث إلى خادم API للكلاستر؛ مزود GitHub يدير المستودعات وعضويات الفرق.

كتلة المزود في إعدادك تُعلن أي مزود تستخدم وبأي إصدار. يُنزّل Terraform المزودين من Terraform Registry أثناء terraform init ويخزّنهم محلياً في .terraform/.

# main.tf — تعريف المزودين المطلوبين وإعداد AWS terraform { required_version = ">= 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" # السماح بـ 5.x، منع 6.x (تغييرات كاسرة) } } } provider "aws" { region = "us-east-1" # بيانات الاعتماد تأتي من متغيرات البيئة أو ~/.aws/credentials # لا تضع الأسرار مباشرة هنا أبداً } # مورد S3 Bucket بسيط resource "aws_s3_bucket" "app_assets" { bucket = "myorg-app-assets-prod" tags = { Environment = "production" ManagedBy = "terraform" Team = "platform" } }

قيد الإصدار ~> 5.0 هو نمط حرج للإنتاج. يُثبّت الإصدار الرئيسي، ويسمح بتحديثات الترقيع والإصدار الثانوي (5.1، 5.2...) تلقائياً بينما يمنع 6.0 الذي قد يحتوي تغييرات كاسرة. حذف قيود الإصدار يجعل terraform init يُنزّل أحدث إصدار — طريقة صامتة لاستيراد تغييرات كاسرة في قاعدة كود البنية التحتية.

Terraform plan/apply workflow with provider plugin model Write .tf config files Plan terraform plan Apply terraform apply State File terraform.tfstate state informs diff Provider Plugin Model Terraform Core plan + state engine AWS Provider hashicorp/aws K8s Provider hashicorp/kubernetes GitHub Provider integrations/github Datadog Provider datadog/datadog → AWS APIs → K8s API server → GitHub API → Datadog API 3,000+ provider على Terraform Registry — سير عمل واحد يدير كامل بصمتك التحتية
حلقة plan/apply في Terraform (أعلى) ونموذج إضافة المزودين (أسفل) — يترجم المزودون الإعداد التصريحي إلى استدعاءات API حقيقية.

كيف يعرف Terraform ما يوجد: ملف الحالة

يتتبع Terraform كل مورد يديره في ملف الحالة (terraform.tfstate). ملف الحالة هو الجسر بين إعداد HCL الخاص بك والبنية التحتية الحقيقية. عند تشغيل terraform plan، يقرأ Terraform كلاً من الإعداد والحالة، يستدعي المزود لتحديث سمات الموارد الحية، ويحسب الفرق.

لهذا عاقبة مهمة: الموارد التي أُنشئت خارج Terraform (بالنقر في وحدة التحكم، أو بسكريبت فريق آخر) غير مرئية لـ Terraform ما لم تستوردها صراحةً. هذا ميزة (وضوح الملكية) وفخ شائع في آن واحد (المهندسون الذين يتجاوزون Terraform يُنشئون موارد غير متتبعة تُسبب لاحقاً فروقات خطة محيّرة أو تكاليف مهجورة).

لا تلتزم أبداً بـ terraform.tfstate في Git، ولا تخزّنه محلياً للبنية التحتية المشتركة. يحتوي ملف الحالة على بيانات حساسة — معرفات الموارد وعناوين IP وأحياناً أسراراً مُخرجة من الموارد. والأهم، إذا طبّق مهندسان على نفس ملف الحالة المحلي في آن واحد، يصبح الحالة تالفة وبنيتك التحتية في وضع مجهول. الحل هو backend بعيد (S3 + DynamoDB للقفل، أو Terraform Cloud) — مُغطّى في الدرس السادس. في كل فريق حقيقي، اليوم الأول من إعداد Terraform هو تكوين الـ backend البعيد. الحالة المحلية مقبولة فقط للتجارب الشخصية.

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

هذا مثال بسيط لكنه كامل يُنشئ AWS S3 Bucket ويُخرج ARN الخاص به. شغّله على حساب AWS شخصي للحصول على الخبرة العضلية لسير العمل:

# هيكل الملفات # . # ├── main.tf # └── outputs.tf # main.tf terraform { required_version = ">= 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = "us-east-1" } resource "aws_s3_bucket" "example" { bucket = "myorg-terraform-intro-demo" tags = { ManagedBy = "terraform" } } # outputs.tf output "bucket_arn" { description = "ARN of the demo S3 bucket" value = aws_s3_bucket.example.arn } # --- الأوامر --- # export AWS_ACCESS_KEY_ID=... # export AWS_SECRET_ACCESS_KEY=... # تنزيل إضافة مزود AWS terraform init # معاينة: تظهر "+ aws_s3_bucket.example will be created" terraform plan # إنشاء المورد؛ اكتب "yes" عند الطلب terraform apply # التحقق من الناتج terraform output bucket_arn # التنظيف (يحذف الـ bucket) terraform destroy

لاحظ أن aws_s3_bucket.example.arn يُشير إلى سمة ARN للـ bucket قبل وجود المورد. يحل Terraform هذه المراجع في وقت التطبيق، بناءً على رسم بياني للتبعية لتحديد ترتيب التنفيذ. صيغة المرجع هذه — resource_type.name.attribute — أساسية لكل شيء في Terraform وتُغطى بعمق في الدرس السابع.

فعّل خادم لغة Terraform في محررك فوراً. ثبّت إضافة HashiCorp Terraform الرسمية لـ VS Code (أو ما يعادلها لمحررك). توفر الإكمال التلقائي لكل سمة مورد، والتوثيق المضمّن، والتحقق من الصياغة. كتابة HCL بدونها كتابة كود بدون فاحص أنواع — ستكتشف الأخطاء في وقت terraform plan بدلاً من المحرر، وهو حلقة تغذية راجعة أبطأ.

الدرس التالي يتعمق في صياغة HCL — الكتل، والوسائط، والتعبيرات، ونظام الأنواع، وكيفية كتابة أول إعداد متعدد الموارد. بحلول الدرس العاشر ستكون قد وفّرت مكدساً ويب كاملاً — موازن تحميل، وحدات EC2، وRDS، وRoute 53 — بالكامل من كود Terraform.