البنية التحتية ككود مع OpenTofu (المتوافق مع Terraform) وAnsible
خروج

الدرس 1 من 10 · الأساسيات

البنية التحتية كشيفرة (Infrastructure as Code): لماذا، وبأي أدوات

30 د Debian 12

الهدف

فهم معنى « وصف البنية التحتية بالشيفرة »، وما يغيّره ذلك في العمل اليومي لفريق ما، وتحديد موقع الأدوات الرئيسية: OpenTofu وTerraform، وAnsible، وPulumi، وCloudFormation. ستكتب بعد ذلك أول إعداد OpenTofu لك.

أهم النقاط

  • تقوم البنية التحتية كشيفرة (IaC) على وصف الشبكات والخوادم والجدران النارية وقواعد البيانات وسجلات DNS في ملفات نصية مُدارة بالنسخ في Git، ثم ترك أداة تنشئها وتصونها.
  • تصف المقاربة التصريحية (declarative) الحالة المرغوبة (« ثلاثة خوادم ويب »)؛ وتحسب الأداة بنفسها الإجراءات. أما المقاربة الأمرية (imperative) فتصف الخطوات (« أنشئ خادمًا، ثم خادمًا ثانيًا… »).
  • نميّز بين التزويد (provisioning، أي إنشاء الموارد: OpenTofu وTerraform وCloudFormation وPulumi) وإدارة الإعدادات (configuration management، أي تثبيت البرمجيات وضبطها داخل الآلات: Ansible وPuppet وChef).
  • OpenTofu نسخة مشتقّة حرّة من Terraform، أُنشئت سنة 2023 بعد تغيير رخصة Terraform؛ وتحتفظ بلغته (HCL) وبجلّ أوامره تقريبًا.
  • الأداة التصريحية متساوية القوى (idempotent): إذا أُعيد تشغيلها عشر مرات، فلا تُجري تغييرات إلا إذا وُجد فرق بين الشيفرة والواقع.

المشكلة: البنية التحتية المصنوعة يدويًا

تخيّل وكالة ويب في الدار البيضاء تستضيف مواقع عشرين زبونًا. منذ خمس سنوات، أُنشئ كل خادم يدويًا في لوحة تحكم المزوّد: نقرة للآلة، وأخرى للجدار الناري، وقاعدة أُضيفت « مؤقتًا » في ليلة عطل.

النتيجة:

  • لا أحد يعرف بالضبط كيف أُعدّ خادم الزبون الرئيسي؛ فالمسؤول الذي ركّبه قد غادر؛
  • إنشاء بيئة اختبار مطابقة للإنتاج يستغرق يومين، ولا تكون أبدًا مطابقة حقًا؛
  • بعد حادث (حذف عرضي، اختراق)، تستغرق إعادة البناء أسبوعًا؛
  • التغييرات لا تُراجَع: قاعدة في الجدار الناري مفتوحة على الإنترنت كلها تمرّ دون أن ينتبه إليها أحد.

نتحدث هنا عن خوادم « ندف الثلج » (snowflake servers): كل واحد منها فريد ويستحيل استنساخه.

الحل: وصف كل شيء في ملفات

# reseau.tf — مقتطف مما ستكتبه في هذا التكوين
resource "aws_security_group" "web" {
  name = "web-client-atlas"

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

ما نربحه:

قبل (يدويًا) بعد (IaC)
التوثيق خاطئ أو غائب الشيفرة هي التوثيق
تغيير = نقرة غير مرئية تغيير = commit يراجعه زميل
بيئة الاختبار لا تشبه الإنتاج الشيفرة نفسها تنشئ الاختبار والإنتاج
إعادة البناء تستغرق أيامًا tofu apply يعيد إنشاء كل شيء في دقائق
« من فتح هذا المنفذ؟ » git log يجيب، مع التاريخ والسبب

تُطبّق IaC على البنية التحتية ممارسات تطوير البرمجيات: إدارة النسخ، ومراجعة الشيفرة، والاختبارات، والتكامل المستمر.

تصريحي أم أمري

لنقارن طريقتين لطلب ثلاثة خوادم ويب.

المقاربة الأمرية، سكربت:

# creer-serveurs.sh — كل تشغيل ينشئ ثلاثة خوادم إضافية
for i in 1 2 3; do
  fournisseur-cli server create --name "web-$i" --type petit --image debian-12
done

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

المقاربة التصريحية، OpenTofu:

resource "fournisseur_server" "web" {
  count = 3
  name  = "web-${count.index + 1}"
  type  = "petit"
  image = "debian-12"
}

أنت تصف النتيجة. في التشغيل الأول، ينشئ OpenTofu ثلاثة خوادم. وفي التشغيل الثاني، لا يفعل شيئًا: فالواقع يطابق الشيفرة مسبقًا. وإذا كتبت count = 5، فإنه ينشئ اثنين. وإذا كتبت count = 2، فإنه يحذف واحدًا. الأداة هي التي تحسب الفرق.

ولهذا، يحتفظ OpenTofu بـ حالة (state): قائمة ما أنشأه، مع المعرّفات الحقيقية لدى المزوّد. ستدرسها بالتفصيل في الدرس 2.1.

مشهد الأدوات

الأداة الفئة اللغة الأهداف الرخصة
OpenTofu تزويد تصريحي HCL كل مزوّد لديه API (AWS، Azure، GCP، OVHcloud، Scaleway، Proxmox، Kubernetes، DNS…) MPL 2.0 (حرّة)، مؤسسة Linux
Terraform تزويد تصريحي HCL الأهداف نفسها BUSL 1.1 منذ النسخة 1.6 (2023)
Ansible إدارة الإعدادات، التنسيق (orchestration) YAML (playbooks) الأنظمة (Linux، Windows، الشبكة)، عبر SSH، دون وكيل (agent) GPL v3
Pulumi تزويد تصريحي Python، TypeScript، Go، C#… كل مزوّد Apache 2.0 (المحرّك)
CloudFormation تزويد تصريحي YAML أو JSON AWS فقط خدمة AWS
Bicep / ARM تزويد تصريحي Bicep، JSON Azure فقط خدمة Microsoft
Puppet، Chef إدارة الإعدادات لغات خاصة (DSL) الأنظمة، مع وكيل متنوّعة
cloud-init الإعداد عند الإقلاع الأول YAML آلة افتراضية جديدة Apache 2.0 / GPL v3

بعض المعالم للاختيار:

  • عدة مزوّدين أو مزوّد محلي (OVHcloud، مزوّد استضافة مغربي، Proxmox): OpenTofu أو Terraform، بفضل آلاف الـ providers لديهما.
  • كل شيء لدى AWS مع فريق يريد البقاء في هذا النظام البيئي: CloudFormation ممكن، لكنك ستبقى حبيس AWS.
  • فريق من المطوّرين يفضّل لغة برمجة حقيقية (حلقات، اختبارات وحدوية): Pulumi.
  • إعداد ما بداخل الآلات (الحزم، الملفات، الخدمات): Ansible. وكثيرًا ما نجمعه مع OpenTofu (الدرس 3.1): ينشئ OpenTofu الآلات، ويُعدّها Ansible.

OpenTofu ليس أداة لإدارة الإعدادات: فهو ينشئ الموارد ويحذفها، لكنه لا يثبّت nginx داخل آلة (يمكنه فقط تمرير سكربت إقلاع، cloud-init مثلًا).

OpenTofu وTerraform: لمحة تاريخية

  • 2014: نشرت HashiCorp أداة Terraform تحت الرخصة الحرّة MPL 2.0. فأصبحت الأداة المرجعية لـ IaC متعدّد السحابات (multi-cloud).
  • غشت 2023: نقلت HashiCorp منتجاتها، ومنها Terraform ابتداءً من النسخة 1.6، إلى رخصة Business Source License (BUSL 1.1). تبقى الشيفرة مقروءة، لكن بعض الاستعمالات المنافسة لـ HashiCorp لم تعد مسموحًا بها. فأطلقت شركات ومساهمون حينها نسخة مشتقّة من شيفرة MPL، سُمّيت أولًا OpenTF.
  • شتنبر 2023: انضمّ المشروع إلى Linux Foundation باسم OpenTofu.
  • يناير 2024: صدور OpenTofu 1.6، أول نسخة مستقرة، متوافقة مع Terraform 1.5/1.6.
  • منذ ذلك الحين: يتطوّر المشروعان كلٌّ على حدة. يضيف OpenTofu وظائفه الخاصة (تشفير الحالة، enabled داخل lifecycle، ملفات .tofu…)، ويضيف Terraform وظائفه. ومن جهة أخرى، استحوذت IBM على HashiCorp (اكتملت العملية سنة 2025).

عمليًا، في هذا التكوين:

OpenTofu Terraform
الأمر tofu init، tofu plan، tofu apply terraform init، terraform plan، terraform apply
اللغة HCL، ملفات .tf (و.tofu) HCL، ملفات .tf
كتلة الإعدادات terraform { … } (الاسم نفسه، للتوافق) terraform { … }
السجلّ العام (registry) registry.opentofu.org registry.terraform.io
المزوّدون (providers) نفسهم (hashicorp/aws، hashicorp/local…) نفسهم

ما تتعلّمه هنا يفيد إذن أيضًا في مشروع Terraform قائم. والانتقال من مشروع Terraform حديث إلى OpenTofu يتطلّب عمومًا حفظ نسخة من الحالة، وتثبيت tofu، ثم تشغيل tofu init وtofu plan يجب ألا يُظهر أي تغيير.

سير عمل OpenTofu

   écrire (*.tf)  ──▶  tofu init  ──▶  tofu plan  ──▶  revue  ──▶  tofu apply
        ▲              télécharge      calcule les      (vous,      applique les
        │              les providers   différences      un collègue) changements
        └──────────────── git commit, push, merge request ◀───────────────┘
  1. الكتابة: تصف الموارد في ملفات .tf.
  2. التخطيط: يقارن tofu plan بين الشيفرة والحالة والواقع، ثم يعرض ما سيُنشأ (+)، أو يُعدَّل (~)، أو يُدمَّر (-).
  3. التطبيق: ينفّذ tofu apply الخطة، بعد تأكيدك.

يفصّل الدرس التالي هذه الأوامر.

ملفك الأول

نوع المورد terraform_data يوفّره OpenTofu نفسه، دون أي تنزيل: فهو لا ينشئ شيئًا لدى أي مزوّد، بل يحتفظ ببساطة بقيمة في الحالة. إنه مثالي للتعلّم.

# /root/iac/premier/main.tf
resource "terraform_data" "bonjour" {
  input = "Bonjour depuis Rabat"
}
# /root/iac/premier/outputs.tf
output "message" {
  value = terraform_data.bonjour.output
}
cd /root/iac/premier
tofu init          # لا يوجد أي مزوّد للتنزيل هنا
tofu apply         # أجب بـ « yes »
# Outputs:
# message = "Bonjour depuis Rabat"

وما لا يدخل إلى Git:

# .gitignore (في هذا الملف، يشغل التعليق السطر كله)
# المزوّدون المنزَّلون، يعيد tofu init إنشاءهم
.terraform/
# قد تحتوي الحالة على أسرار: مكانها backend بعيد
*.tfstate
# نسخها الاحتياطية (terraform.tfstate.backup)
*.tfstate.*
crash.log
# الخطط المحفوظة
*.tfplan

أما الملف .terraform.lock.hcl فـيجب تسجيله: فهو يثبّت النسخ الدقيقة للمزوّدين للفريق كله.

أخطاء متكررة

  • الخلط بين النقر والشيفرة: أي تعديل يُجرى يدويًا في لوحة التحكم سيُلغى (أو سيتسبّب في خطأ) عند tofu apply التالي. بمجرد اعتماد IaC، يمرّ كل شيء عبر الشيفرة.
  • إدارة الحالة بالنسخ (terraform.tfstate) في Git: قد تحتوي على كلمات مرور واضحة، وقد يمحو شخصان عمل بعضهما.
  • الاعتقاد بأن OpenTofu يُعدّ البرمجيات داخل الآلات: لذلك نستعمل Ansible أو cloud-init.
  • الانطلاق دون خطة: قد يدمّر apply دون قراءة الخطة قاعدة بيانات لأن وسيطًا ما تغيّر.

التطبيق العملي (Lab)

تحتوي حاوية المختبر على OpenTofu لكنها لا تملك اتصالًا بالشبكة: تكتب ملفات، ويعيد المصحّح قراءتها. المورد terraform_data لا يتطلّب أي تنزيل: يمكنك حتى تجربة tofu init ثم tofu apply -auto-approve داخل المجلد لرؤية المُخرَج.

  1. أول مورد terraform_data؛
  2. مُخرَج يعرض قيمته؛
  3. الكتلة terraform التي تشترط نسخة دنيا، مع ملفات منسّقة جيدًا (tofu fmt)؛
  4. ملف .gitignore لمشروع OpenTofu.

لكتابة ملف دون محرّر: cat > main.tf <<'EOF' … EOF.

اختبار قصير

  1. ما الفرق بين المقاربة التصريحية والمقاربة الأمرية؟
  2. تنتقل من count = 3 إلى count = 5 وتعيد تشغيل OpenTofu. ماذا يحدث؟
  3. OpenTofu أم Ansible: أيّهما ينشئ الآلة الافتراضية، وأيّهما يثبّت nginx داخلها؟
  4. لماذا يوجد OpenTofu، وما الذي يحتفظ به من Terraform؟
  5. هل يجب تسجيل terraform.tfstate في Git؟ وماذا عن .terraform.lock.hcl؟
الأجوبة
  1. التصريحية: نصف الحالة النهائية المرغوبة وتحسب الأداة الإجراءات. الأمرية: نكتب سلسلة الإجراءات المراد تنفيذها.
  2. يقارن OpenTofu الشيفرة بالحالة، فيرى أن هناك نسختين (instances) ناقصتين وينشئ اثنتين، دون المساس بالثلاث الموجودة.
  3. ينشئ OpenTofu الآلة (التزويد)؛ ويثبّت Ansible خادم nginx ويُعدّه داخلها (إدارة الإعدادات).
  4. أُنشئ سنة 2023، تحت مظلة Linux Foundation، بعد انتقال Terraform إلى رخصة BUSL؛ ويحتفظ بلغة HCL، وبالأوامر (مع tofu بدل terraform)، وبالمزوّدين.
  5. لا بالنسبة للحالة: قد تحتوي على أسرار ويجب مشاركتها عبر backend بعيد مع القفل (locking). ونعم بالنسبة لـ .terraform.lock.hcl، الذي يثبّت نسخ المزوّدين للفريق كله.

المراجع: توثيق OpenTofu (MPL 2.0). نص أصلي من MasterLinux.

تمارين تطبيقية

تُنجَز على Debian 12

الطرفية عبر الإنترنت قادمة قريبًا. في انتظار ذلك، أنجِز هذه المهام على جهازك بنظام لينكس أو على آلة افتراضية أو WSL.

  1. اكتب /root/iac/premier/main.tf: مورد (resource) من نوع terraform_data اسمه bonjour، قيمة وسيطه input هي "Bonjour depuis Rabat"
    عرض تلميحresource "<type>" "<nom>" { argument = valeur } — على ثلاثة أسطر، مع إزاحة الوسيط بمسافتين.
    عرض الحل
    printf '%s\n' 'resource "terraform_data" "bonjour" {' '  input = "Bonjour depuis Rabat"' '}' > /root/iac/premier/main.tf

    التحقق : f=/root/iac/premier/main.tf; grep -Eq '^resource +"terraform_data" +"bonjour" *[{] *$' $f && grep -Eq '^ +input *= *"Bonjour depuis Rabat" *$' $f && grep -Eq "^[}] *$" $f

  2. اكتب /root/iac/premier/outputs.tf: مُخرَج (output) اسمه message قيمته هي الخاصية output للمورد terraform_data.bonjour
    عرض تلميحoutput "message" { value = <type>.<nom>.<attribut> }
    عرض الحل
    printf '%s\n' 'output "message" {' '  value = terraform_data.bonjour.output' '}' > /root/iac/premier/outputs.tf

    التحقق : f=/root/iac/premier/outputs.tf; grep -Eq '^output +"message" *[{] *$' $f && grep -Eq '^ +value *= *terraform_data\.bonjour\.output *$' $f

  3. اكتب /root/iac/premier/versions.tf: كتلة terraform تشترط نسخة من OpenTofu أكبر من 1.6.0 أو مساوية لها (required_version). يجب أن تكون ملفاتك الثلاثة منسّقة جيدًا: يجب ألا يُبلغ tofu fmt -check /root/iac/premier عن أي شيء
    عرض تلميحاسم الكتلة هو فعلًا terraform، حتى مع OpenTofu. إزاحة بمسافتين، ومسافة واحدة على جانبي علامة المساواة.
    عرض الحل
    printf '%s\n' 'terraform {' '  required_version = ">= 1.6.0"' '}' > /root/iac/premier/versions.tf

    التحقق : f=/root/iac/premier/versions.tf; grep -Eq "^terraform *[{] *$" $f && grep -Eq '^ +required_version *= *">= *1\.6(\.0)?" *$' $f && { ! command -v tofu >/dev/null 2>&1 || tofu fmt -check /root/iac/premier >/dev/null 2>&1; }

  4. اكتب /root/iac/premier/.gitignore حتى لا يُدار بالنسخ أبدًا المجلد .terraform/ ولا ملفات الحالة (*.tfstate و*.tfstate.*)، لكن «دون» تجاهل ملف القفل .terraform.lock.hcl
    عرض تلميحسطر واحد لكل نمط. أما الملف .terraform.lock.hcl فيُسجَّل في Git.
    عرض الحل
    printf '%s\n' '.terraform/' '*.tfstate' '*.tfstate.*' 'crash.log' '*.tfplan' > /root/iac/premier/.gitignore

    التحقق : f=/root/iac/premier/.gitignore; grep -Eq "^(\*\*/)?\.terraform/?$" $f && grep -Eq "^\*\.tfstate$" $f && grep -Eq "^\*\.tfstate\.\*$" $f && ! grep -q "lock" $f

المراجع الرسمية المعتمدة: OpenTofu — Getting started — https://opentofu.org/docs/intro/ ; OpenTofu — OpenTofu vs. Chef, Puppet, etc. — https://opentofu.org/docs/intro/vs/chef-puppet/ ; OpenTofu — OpenTofu vs. CloudFormation, Heat, etc. — https://opentofu.org/docs/intro/vs/cloudformation/ ; OpenTofu — Migrating to OpenTofu — https://opentofu.org/docs/intro/migration/ ; OpenTofu — Use cases — https://opentofu.org/docs/intro/use-cases/. نص أصلي من MasterLinux.