Ansible أم Terraform: أيهما تحتاج؟
ينشئ Terraform خادم VPS، بينما يهيّئه Ansible. تعرّف على الفرق الحقيقي، ولماذا تتعثر أدوات provisioners، وأوامر التسليم، ومتى يكفي Ansible وحده.
Ansible مقابل Terraform في جملة واحدة
لا تتمثل المقارنة بين Ansible وTerraform في الاختيار بين أداتين تؤديان المهمة نفسها. يصرّح Terraform بالبنية التحتية الموجودة: الخوادم، والأقراص، والشبكات، وسجلات DNS. ويصرّح Ansible بالحالة المطلوبة داخل جهاز موجود بالفعل: الحزم، والمستخدمون، وملفات الإعداد، والخدمات قيد التشغيل. ينشئ Terraform خادم VPS. ويحوّل Ansible خادم VPS هذا إلى خادم ويب.
كلاهما تصريحي، ويُطلق على كليهما اسم البنية التحتية كتعليمة برمجية (IaC). يكمن الفرق الحقيقي فيما يتذكره كل منهما. يكتب Terraform ملف حالة يربط كل مورد في التعليمات البرمجية بكائن حقيقي أنشأه عبر واجهة API، ولذلك يمكنه معرفة أن حذف خمسة أسطر يعني تدمير خادم واحد. لا يتذكر Ansible أي شيء بين عمليات التشغيل. فهو يتصل عبر SSH، ويفحص الجهاز، ويغيّر فقط ما لا يطابق ملف التشغيل بالفعل.
يوضح هذا الفرق الواحد بقية هذا الدليل، بما في ذلك سبب فشل خلط المهمتين في أداة واحدة.
ما الذي يفعله Terraform فعليًا
يتصل Terraform بواجهة API من خلال مكوّن إضافي للمزوّد. تحدد صفحة سجل المزوّد أنواع الموارد التي يمكنك كتابتها. لذلك يختلف اسم المورد لخادم على مضيف عن اسم المورد لخادم على مضيف آخر، وتختلف الوسائط أيضًا.
resource "cloud_server" "web" {
name = "web1"
image = "ubuntu-24.04"
type = "small"
}
output "web_ip" {
value = cloud_server.web.ipv4_address
}استبدل cloud_server بنوع المورد الذي يوثقه المزوّد. تُعد كتلة output الجزء المهم في هذا الدليل، لأنها تحدد كيفية مغادرة العنوان لـ Terraform.
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanينزّل terraform init المزوّد ويكتب ملف قفل. يطبع 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 أن تلك الخوادم مملوكة لك، ولذلك سيحاول التطبيق التالي إنشاء نسخ مكررة. احتفظ به في خلفية بعيدة بمجرد أن يشغّل أكثر من شخص الأوامر، لأن تطبيق شخصين في الوقت نفسه ينتج ما يلي:
Error: Error acquiring the state lockOpenTofu هو تفرّع من Terraform، ويستخدم الأوامر نفسها وتنسيق الملفات نفسه. اعتبارًا من July 2026، يعمل كل ما في هذا الدليل إذا كتبت tofu بدلًا من terraform.
ما الذي يفعله Ansible فعليًا
لا يحتاج Ansible إلى وكيل أو واجهة API. يفتح اتصال SSH، وينسخ وحدة Python صغيرة إلى الهدف، ويشغّلها، ثم يحذفها. ويمكن لـ Ansible تهيئة أي نظام يمكنك الوصول إليه باستخدام SSH وكلمة مرور sudo.
- 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: trueansible -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 قبل أن تبدأ في تصحيح أخطاء ملف التشغيل. تكون النتيجة السليمة هي web1 | SUCCESS => {"ping": "pong"}. ويُعد التشغيل --check --diff أقرب ما يقدمه Ansible إلى خطة: فهو يعرض التغييرات التي ستحدث من دون تنفيذها، لكن المهام التي تعتمد على مهام سابقة قد تعرض نتائج غير صحيحة في وضع التحقق، لأن التغيير السابق لم يُنفّذ فعليًا.
ينتهي كل تشغيل بملخص مثل ok=6 changed=2 unreachable=0 failed=0. شغّل ملف التشغيل نفسه مرتين. يجب أن يعرض التشغيل الثاني changed=0. المهمة التي تعرض حالة التغيير في كل تشغيل ليست متسقة، وتكون عادةً مهمة command أو shell، وكان ينبغي أن تستخدم وحدة فعلية. إذا كان هذا المجال جديدًا عليك، فابدأ بـ أول ملف تشغيل Ansible على VPS واحد ثم طوّره تدريجيًا.
أين تتداخل الأداتان وأين تتعارضان
يمكن لـ Terraform تنفيذ أوامر على خادم جديد باستخدام provisioner remote-exec. وتصف وثائق HashiCorp الخاصة provisioners بأنها خيار أخير. وهناك أسباب وجيهة لذلك.
يعمل provisioner عند إنشاء المورد فقط. إذا عدّلت البرنامج النصي، فلن يحدث شيء على الخادم الحالي، لأن المورد، من وجهة نظر Terraform، يطابق الشيفرة بالفعل. لا تظهر خطوات provisioner مطلقًا في terraform plan، لذلك لا تكشف المراجعة عنها. إذا فشل البرنامج النصي، يضع Terraform المورد في حالة tainted، ثم يؤدي التطبيق التالي إلى تدمير خادم كان على الأرجح سليمًا وإعادة إنشائه.
يحدث الفشل أيضًا في توقيت سيئ. يبلّغ 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/infraansible-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.
تغيّر مفتاح المضيف. أزلت الخادم وأنشأته من جديد، وأصبح الخادم الجديد يستجيب على العنوان نفسه باستخدام مفتاح جديد.
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 تشغيل البرامج النصية باستخدام المُجهِّز remote-exec، لكنها تعمل فقط عند إنشاء المورد، ولا تظهر أبدًا في terraform plan، وتضع المورد في حالة غير موثوقة عند فشلها، ما يَجدول حذفه وإعادة إنشائه عند تنفيذ apply التالي. لا يملك Terraform ما يعادل وحدة تتحقق من تثبيت nginx مسبقًا ولا تفعل شيئًا إذا كان مثبتًا. استخدم Terraform لإنشاء الجهاز، ثم سلّم الإدارة إلى Ansible.
هل يمكن لـ Ansible أن يحل محل Terraform؟
نعم، إذا كان لديك عدد قليل من الخوادم طويلة التشغيل. يوفّر Ansible وحدات سحابية لإنشاء الخوادم، وإذا طلبت مثيلي VPS واحتفظت بهما، فهذا يكفي. لكنك تفقد ملف الحالة ورسم التبعيات: إذا حذفت مهمة من playbook، يظل المورد قيد التشغيل وتستمر فوترة تكلفته، لأن Ansible لا يسجل أنه أنشأه. أما Terraform فكان سيخطط لحذف المورد.
أيهما ينبغي أن أتعلم أولًا؟
تعلم Ansible إذا كنت تدير خوادم حاليًا. تسترد فائدته من الجهاز الأول، ولا يحتاج إلى شيء سوى SSH، وتنطبق مهارته على خادم طلبته يدويًا. تسترد فائدة Terraform لاحقًا، عندما تعيد إنشاء البيئات مرارًا أو تدير موارد لدى المزوّد تتجاوز الخوادم، مثل سجلات DNS وقواعد جدار الحماية.
كيف أنقل عنوان IP للخادم الجديد من Terraform إلى Ansible؟
عرّف output في كود Terraform، ثم اقرأه بعد تنفيذ apply. يطبع terraform output -raw web_ip القيمة المجردة لاستخدامها في استبدال الصدفة، بينما يمنحك terraform output -json كل المخرجات دفعة واحدة عند وجود عدة مضيفين. اكتبها في ملف جرد، أو ثبّت مجموعة cloud.terraform وأشر باستخدام ansible-inventory -i terraform.yml --graph إلى دليل المشروع.
لماذا يفشل playbook لدي مباشرة بعد انتهاء Terraform؟
يبلغ المزوّد عن إنشاء الخادم فور إبلاغ واجهة API الخاصة به بذلك، بينما يظل نظام التشغيل قيد الإقلاع، لذلك يرفض SSH الاتصالات خلال الثواني الأولى. الخطأ هو UNREACHABLE! مع Connection refused. اجعل ansible.builtin.wait_for_connection أول مهمة في play بدلًا من التخمين بمدة sleep، لأن وقت الإقلاع يختلف حسب الصورة والخطة.