SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-23

Ansible أم Terraform: أيهما تحتاج؟

ينشئ Terraform خادم VPS، بينما يهيّئه Ansible. تعرّف إلى الفرق الحقيقي، وأوامر التسليم بينهما، ولماذا لا تحتاج إلا إلى Ansible أحياناً.

Ansible مقابل Terraform في جملة واحدة

لا تتعلق المقارنة بين Ansible وTerraform بالاختيار بين أداتين تؤديان المهمة نفسها. يصرّح Terraform بالبنية التحتية الموجودة: الخوادم، والأقراص، والشبكات، وسجلات DNS. ويصرّح Ansible بالحالة المطلوبة داخل جهاز موجود مسبقاً: الحزم، والمستخدمون، وملفات الإعداد، والخدمات قيد التشغيل. ينشئ Terraform خادم VPS. ويحوّل Ansible خادم VPS هذا إلى خادم ويب.

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

يفسّر هذا الفرق الواحد بقية هذا الدليل، بما في ذلك سبب فشل دمج المهمتين في أداة واحدة.

ما الذي يفعله Terraform فعلياً

يتصل Terraform بواجهة API من خلال إضافة provider. تحدد صفحة سجل provider أنواع الموارد التي يمكنك كتابتها، لذلك يختلف اسم مورد خادم على مضيف عن اسم مورد خادم على مضيف آخر، وتختلف الوسائط أيضاً.

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

استبدل cloud_server بنوع المورد الذي يوثّقه provider لديك. تُعد كتلة output الجزء المهم في هذا الدليل، لأنها تحدد كيفية خروج العنوان من Terraform.

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

تنزّل terraform init provider وتنشئ ملف قفل. تطبع terraform plan الفرق بين الشيفرة التي كتبتها وملف الحالة، وتنتهي بسطر مثل Plan: 1 to add, 0 to change, 0 to destroy. اقرأ هذا السطر في كل مرة. لا يمكن تغيير بعض الوسائط في مكانها، وتوضح الخطة ذلك بوجود # forces replacement بجانب السمة، متبوعاً بـ1 to add, 0 to change, 1 to destroy. يؤدي تطبيق هذه الخطة إلى حذف الخادم وإنشاء خادم جديد فارغ، وهكذا يفقد المستخدمون بيانات ظنوا أنها آمنة.

يعني حفظ الخطة في ملف وتطبيق الملف، بدلاً من تشغيل terraform apply مجرداً، أن ما راجعته هو ما سيُنفَّذ. قد يغيّر شخص آخر البنية التحتية بين الأمرين.

terraform.tfstate هي الذاكرة. إذا فقدتها، فلن يعرف Terraform بعد ذلك أن تلك الخوادم مملوكة لك، ولذلك سيحاول التطبيق التالي إنشاء نسخ مكررة. احتفظ بها في backend بعيد فور تشغيل الأوامر من أكثر من شخص، لأن تطبيق شخصين للخطة في الوقت نفسه ينتج ما يلي:

Error: Error acquiring the state lock

OpenTofu هو fork من Terraform، ويستخدم الأوامر نفسها وتنسيق الملفات نفسه. اعتباراً من July 2026، يعمل كل ما يرد في هذا الدليل إذا كتبت tofu بدلاً من terraform.

ما الذي يفعله Ansible فعلياً

لا يحتاج Ansible إلى agent أو API. يفتح اتصال SSH، وينسخ وحدة Python صغيرة إلى الهدف، ويشغّلها، ثم يحذفها. كل ما يمكنك الوصول إليه باستخدام SSH وكلمة مرور sudo، يستطيع Ansible إعداده.

- name: Base web server
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Ensure nginx is running at boot
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

تتحقق وحدة ping من SSH وPython وsudo قبل أن تبدأ في تصحيح أخطاء playbook. النتيجة السليمة هي web1 | SUCCESS => {"ping": "pong"}. يُعد تشغيل --check --diff أقرب ما يقدمه Ansible إلى خطة: فهو يعرض التغييرات التي ستحدث من دون تطبيقها، لكن المهام التي تعتمد على مهام سابقة قد تعرض نتائج غير صحيحة في وضع check، لأن التغيير السابق لم يُطبَّق فعلياً.

ينتهي كل تشغيل بملخص مثل ok=6 changed=2 unreachable=0 failed=0. شغّل playbook نفسه مرتين. يجب أن يعرض التشغيل الثاني changed=0. المهمة التي تعرض الحالة changed في كل تشغيل ليست idempotent، وغالباً ما تكون مهمة command أو shell كان ينبغي أن تستخدم module حقيقية. إذا كان هذا المجال جديداً عليك، فابدأ بـأول playbook لـAnsible على VPS واحد ثم طوّره تدريجياً.

مواضع التداخل بين الأداتين ومواضع التعارض بينهما

يمكن لـTerraform تشغيل أوامر على خادم جديد باستخدام provisioner ‏remote-exec. وتصف وثائق HashiCorp الخاصة provisioners بأنها خيار أخير. وهناك أسباب وجيهة لذلك.

يعمل provisioner فقط عند إنشاء المورد. إذا عدّلت البرنامج النصي، فلن يحدث شيء على الخادم الموجود، لأن المورد، من وجهة نظر Terraform، يطابق الشيفرة بالفعل. ولا تظهر خطوات provisioner مطلقاً في terraform plan، لذلك لا تُظهر المراجعة أي أثر لها. إذا فشل البرنامج النصي، يضع Terraform المورد في حالة tainted، وعند التنفيذ التالي لـapply يدمّر خادماً كان على الأرجح سليماً ثم يعيد إنشاءه.

ويحدث الفشل أيضاً في توقيت غير مناسب. يبلّغ provider عن إنشاء الخادم فور إبلاغ API بذلك، بينما لا يزال نظام التشغيل قيد الإقلاع، ولا يزال sshd غير مستمع.

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

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

والقاعدة الناتجة هي: دع Terraform يدير الكائنات التي تنشئها API وتحذفها، ودع Ansible يدير كل ما هو داخل نظام تشغيل أقلع بالفعل.

التسليم، كما ينبغي

التسليم حدّ فاصل، وليس تكاملاً. ينهي Terraform عمله، وينشر عنواناً، ثم يتوقف. ويبدأ Ansible من ذلك العنوان.

terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml

يطبع terraform output -raw قيمة واحدة بلا علامات اقتباس وبلا غلاف JSON، وهذا هو المطلوب داخل استبدال shell. عند التعامل مع عدة خوادم، استخدم terraform output -json وأنشئ ملف الجرد منه، لأن -raw يتعامل فقط مع سلسلة أو رقم أو قيمة منطقية واحدة.

من المفيد الإبقاء على خطوة ping بين الأداتين. فهي تفصل بين حالة «زوّدني Terraform بالعنوان الخطأ» وحالة «يحتوي playbook على خطأ». ويبدو هذان الخطآن متطابقين عندما يكون playbook أول ما يتصل بالخادم الجديد.

قراءة حالة Terraform كمخزون Ansible

إذا كنت تفضّل عدم كتابة ملف مخزون على الإطلاق، فإن مجموعة cloud.terraform تقرأ الحالة مباشرة.

ansible-galaxy collection install cloud.terraform

اكتب terraform.yml بجوار ملف playbook:

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

هناك أمران يجب معرفتهما قبل الاعتماد عليه. يشغّل المكوّن الإضافي terraform show مقابل project_path، لذلك يجب أن يكون هذا الدليل مهيّأً مسبقاً، وإلا يفشل المكوّن الإضافي. كما أنه لا ينشئ مضيفين من موارد الخوادم لديك، بل يقرأ موارد ansible_host وansible_group التي تعرّفها في رمز Terraform باستخدام موفّر Ansible. لن يظهر شيء في ansible-inventory --graph حتى تضيف هذه الموارد.

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

هل تحتاج فعلاً إلى Terraform؟

معظم من يقرأ هذا لا يحتاج إليه، على الأقل ليس بعد. تصبح Terraform مفيدة عندما يكون إنشاء البنية التحتية وحذفها مهمة متكررة بحد ذاتها. إذا طلبت VPS واحداً عبر لوحة تحكم وتعتزم الاحتفاظ به لمدة عامين، فإن Terraform تصف مورداً يُنشأ مرة واحدة، وتضيف ملف حالة يجب ألا تفقده.

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

اكتفِ بـAnsible عندما تكون الخوادم طويلة العمر وقليلة العدد، وعندما يكون السؤال اليومي هو «هل أُعدّت هذه الخادم بشكل صحيح؟» وليس «هل توجد هذه الخادم؟». يغطي playbook واحد لتأمين خادم جديد النطاق نفسه الذي تغطيه الدقائق العشر الأولى على VPS جديد، مع ميزة أنه يعمل بالطريقة نفسها على الخادم التالي.

وينتج ترتيب التعلّم من ذلك. تعود فائدة Ansible منذ أول خادم تملكه. وتعود فائدة Terraform عند إعادة إنشاء البيئة الثالثة.

ما الذي يتعطل عند تسليم الخادم

الخادم غير جاهز. تنجح Terraform، لكن تفشل Ansible فوراً.

fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}

أعادت API عنواناً قبل أن يبدأ sshd بالاستماع. انتظر المنفذ بدلاً من إضافة مهلة ثابتة. تحتوي Ansible على ansible.builtin.wait_for_connection لهذا الغرض تحديداً، لذا شغّله بوصفه المهمة الأولى في play. بعد أن يستهدف playbook نفسه مجموعة بدلاً من خادم جديد واحد، حدّد مسبقاً ما الذي يجب أن يحدث عندما يبقى أحد المضيفين غير قابل للوصول، لأن Ansible تزيل ذلك المضيف من بقية التنفيذ، ويكون سطر الملخص هو الموضع الوحيد الذي تخبرك فيه بذلك.

تغيّر مفتاح المضيف. دمّرت الخادم وأنشأته من جديد، فأصبح الخادم الجديد يستجيب على العنوان نفسه باستخدام مفتاح جديد.

Host key verification failed.

أزل الإدخال القديم باستخدام ssh-keygen -R 203.0.113.10. يحدث ذلك باستمرار عندما تتولى Terraform إعادة الإنشاء، ولذلك من الأفضل تقليل عمليات إعادة الإنشاء على الأجهزة التي تحتوي على بيانات.

يفشل Sudo. يعني fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} أن become: true يحتاج إلى كلمة مرور على ذلك المضيف. إما أن تهيّئ sudo دون كلمة مرور لمستخدم النشر، أو تمرّر --ask-become-pass.

تريد Terraform تدمير مورد لم تلمسه. تعرض الخطة تغييرات لم تكتبها، ما يعني أن البنية التحتية الفعلية انحرفت عن الشيفرة، ويحدث ذلك عادةً لأن شخصاً غيّر إعداداً في لوحة الويب الخاصة بالمزوّد. شغّل terraform plan -refresh-only لعرض هذا الفرق وحده، ثم قرّر ما إذا كانت الشيفرة أو المورد الفعلي غير صحيح. لا تطبّق أبداً خطة تدميرية لا تستطيع تفسيرها سطراً بسطر.

تبلغ Ansible عن التغيير في كل تشغيل. تعمل مهمة shell التي لا تحتوي على حارس creates أو when دون شروط. هذه ليست مشكلة شكلية، لأنها تعني أنك لم تعد تستطيع استخدام changed=0 بوصفه إشارة إلى أن الخادم في الحالة التي طلبتها.

FAQ

هل يمكن لـTerraform أن يحل محل Ansible؟

ليس لإدارة الإعدادات داخل الخادم. يمكن لـTerraform تشغيل البرامج النصية باستخدام provisioner ‏remote-exec، لكن هذه البرامج تعمل عند إنشاء المورد فقط، ولا تظهر مطلقاً في terraform plan، وتضع المورد في حالة tainted عند فشلها، ما ي جدولة تدميره وإعادة إنشائه في عملية apply التالية. لا يوفّر Terraform ما يعادل module يتحقق مما إذا كان nginx مثبتاً مسبقاً ولا يفعل شيئاً إذا كان مثبتاً. استخدم Terraform لإنشاء الجهاز، ثم سلّم المهمة إلى Ansible.

هل يمكن لـAnsible أن يحل محل Terraform؟

بالنسبة إلى عدد صغير من الخوادم طويلة الأجل، نعم. يحتوي Ansible على cloud modules تنشئ الخوادم، وإذا طلبت مثيلَي VPS واحتفظت بهما، فهذا يكفي. لكنك ستفقد ملف الحالة ورسم التبعيات: إذا أزلت مهمة من playbook، فسيستمر المورد في العمل وستستمر تكلفته، لأن Ansible لم يسجل أنه أنشأه. أما Terraform فكان سيخطط لتدميره.

أيهما يجب أن أتعلم أولاً؟

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

كيف أنقل عنوان IP للخادم الجديد من Terraform إلى Ansible؟

عرّف output في كود Terraform، ثم اقرأه بعد apply. يطبع terraform output -raw web_ip القيمة المجردة لاستخدامها في استبدال shell، بينما يزوّدك terraform output -json بكل المخرجات دفعة واحدة عند وجود عدة مضيفين. اكتب ذلك في ملف inventory، أو ثبّت مجموعة cloud.terraform ووجّه ansible-inventory -i terraform.yml --graph إلى مجلد المشروع.

لماذا يفشل playbook لدي مباشرة بعد انتهاء Terraform؟

يُبلغ المزوّد عن إنشاء الخادم فور إبلاغ API بذلك، بينما يظل نظام التشغيل قيد الإقلاع، لذلك يرفض SSH الاتصال خلال الثواني الأولى. الخطأ هو UNREACHABLE! مع Connection refused. اجعل ansible.builtin.wait_for_connection المهمة الأولى في play بدلاً من تخمين مدة sleep، لأن وقت الإقلاع يختلف باختلاف image وplan.