SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-31

Ansible מול Terraform: מה ההבדל ומה לבחור?

Terraform מקים את התשתית ו-Ansible מגדיר את התוכנה בתוכה. הכירו את ההבדל המהותי בניהול ה-state, מתי להשתמש בכל כלי, ואיך לבצע את ה-handoff הנכון בין הכלים לניהול שרתים יעיל.

Ansible מול Terraform במשפט אחד

הבחירה בין Ansible לבין Terraform אינה בחירה בין שני כלים המבצעים את אותה עבודה. Terraform מצהיר על התשתית הקיימת: שרתים, כוננים, רשתות ורשומות DNS. Ansible מצהיר על המצב הרצוי בתוך מכונה קיימת: חבילות, משתמשים, קובצי תצורה ושירותים פעילים. Terraform מקים את ה-VPS, ו-Ansible הופך את ה-VPS הזה לשרת אינטרנט.

שניהם דקלרטיביים ושניהם מוגדרים כ-Infrastructure as Code (IaC). ההבדל האמיתי טמון במה שהם "זוכרים". Terraform כותב קובץ state הממפה כל משאב בקוד לאובייקט ממשי שנוצר דרך API, כך שהוא יודע שמחיקת חמש שורות משמעותה השמדת שרת. Ansible אינו זוכר דבר בין הרצות. הוא מתחבר באמצעות SSH, בוחן את המכונה ומשנה רק את מה שאינו תואם ל-playbook.

הבדל יחיד זה מסביר את המשך המדריך, כולל הסיבה לכך שערבוב שני התפקידים לכלי אחד מוביל לבעיות.

מה Terraform עושה בפועל

Terraform מתקשר עם API באמצעות תוסף ספק (provider plugin). דף הרישום של הספק שלכם מגדיר את סוגי המשאבים שניתן לכתוב, לכן שרת במארח אחד ושרת במארח אחר הם שמות משאבים שונים עם ארגומנטים שונים.

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 מורידה את הספק וכותבת קובץ נעילה (lock file). הפקודה terraform plan מציגה את ההבדלים בין הקוד שלכם לבין קובץ ה-state, ומסתיימת בשורה כמו Plan: 1 to add, 0 to change, 0 to destroy.. קראו שורה זו בכל פעם. ישנם ארגומנטים שלא ניתן לשנות במקומם, וה-plan מציין זאת בעזרת # forces replacement לצד המאפיין, ולאחריו 1 to add, 0 to change, 1 to destroy. החלת ה-plan הזה תמחק את השרת ותבנה שרת חדש וריק, וזו הדרך שבה אנשים מאבדים נתונים שחשבו שהם בטוחים.

שמירת ה-plan לקובץ והחלת הקובץ, במקום הרצה של terraform apply ישירות, מבטיחה שהדבר שבדקתם הוא הדבר שמתבצע. בין שתי הפקודות, מישהו אחר עלול היה לשנות את התשתית.

הקובץ terraform.tfstate הוא הזיכרון של המערכת. אם תאבדו אותו, Terraform לא ידע עוד שהשרתים האלו שייכים לכם, ולכן ה-apply הבא ינסה ליצור כפילויות. שמרו אותו ב-backend מרוחק ברגע שיותר מאדם אחד מריץ את הפקודות, כיוון ששני אנשים שמריצים apply בו-זמנית יגרמו לתוצאה הבאה:

Error: Error acquiring the state lock

OpenTofu הוא פיצול (fork) של Terraform עם אותן פקודות ואותו פורמט קבצים. נכון ליולי 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: היא מדווחת מה היה משתנה מבלי לבצע את השינוי בפועל, אם כי משימות התלויות במשימות קודמות עלולות לדווח דיווח שגוי במצב בדיקה, כיוון שהשינוי הקודם לא התרחש באמת.

כל הרצה מסתיימת בסיכום כגון ok=6 changed=2 unreachable=0 failed=0. הריצו את אותו ה-playbook פעמיים. ההרצה השנייה אמורה לדווח changed=0. משימה שמדווחת על שינוי (changed) בכל הרצה אינה אידמפוטנטית, ובדרך כלל מדובר במשימת command או shell שהייתה צריכה להיות ממומשת כמודול אמיתי. אם זהו תחום חדש עבורכם, התחילו עם יצירת ה-Ansible playbook הראשון בשרת 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 יש פיתוי הפוך. מודולי ענן יכולים ליצור שרתים, ועבור מספר קטן של מכונות זה עובד. מה שאתה מאבד הוא את גרף התלויות ואת קובץ ה-state. 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 ובנו את ה-inventory מתוצאה זו, כיוון ש--raw מטפל רק במחרוזת בודדת, מספר או ערך בוליאני.

כדאי לשמור על שלב ה-ping בין שני הכלים. הוא מפריד בין המצב שבו "Terraform סיפק לי כתובת שגויה" לבין המצב שבו "ב-playbook שלי יש באג"; שתי הבעיות הללו נראות זהות לחלוטין כאשר ה-playbook הוא הדבר הראשון שנוגע בשרת החדש.

קריאת מצב Terraform כ־Ansible inventory

אם אינכם מעוניינים לכתוב קובץ inventory כלל, האוסף cloud.terraform קורא את המצב (state) ישירות.

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, לכן הספרייה חייבת להיות מאותחלת מראש, אחרת התוסף ייכשל. כמו כן, הוא אינו ממציא מארחים (hosts) מתוך משאבי השרת שלכם: הוא קורא משאבי ansible_host ו-ansible_group, אותם עליכם להגדיר בקוד ה-Terraform שלכם באמצעות ה-provider של Ansible. דבר לא יופיע ב-ansible-inventory --graph עד שתוסיפו אותם.

קובץ inventory פשוט שנוצר ידנית קל יותר לניפוי שגיאות ועובד עם כל provider. התוסף הופך למשתלם ברגע שה-inventory גדל מעבר למספר קטן של מכונות ועריכה ידנית מתחילה לייצר שגיאות הקלדה; זהו בדיוק השלב שבו ניהול כמה שרתי Linux ממכונת שליטה אחת הופך לזרימת עבודה אמיתית ולא רק להרגל.

האם אתם באמת זקוקים ל-Terraform?

רוב האנשים שקוראים זאת אינם זקוקים לו, לפחות לא בשלב זה. Terraform מצדיק את עלותו כאשר יצירה והשמדה של תשתית הן משימות שחוזרות על עצמן. אם הזמנתם VPS אחד דרך לוח בקרה ואתם מתכוונים להחזיק בו במשך שנתיים, Terraform מתאר פעולה שמתרחשת פעם אחת, ומוסיף קובץ state שאסור לכם לאבד.

פנו ל-Terraform כאשר אתם בונים סביבות מחדש לעיתים קרובות, כאשר סביבת ה-staging חייבת להיות זהה לחלוטין לסביבת ה-production, כאשר כמה אנשים משנים תשתית ואתם רוצים תוכנית שניתן לבחון לפני שמוחקים משהו, או כאשר מה שאתם מנהלים חורג מעבר לשרתים וכולל רשומות DNS, מאזני עומסים וחוקי firewall שחיים ב-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 החל להאזין. המתינו לפורט במקום להוסיף פקודת sleep קבועה. ל-Ansible יש את ansible.builtin.wait_for_connection בדיוק למטרה זו; הריצו אותו כמשימה הראשונה ב-play. ברגע שאותו playbook מכוון לקבוצה ולא רק לשרת חדש אחד, החליטו מראש מה צריך לקרות כשמארח אחד נשאר בלתי נגיש, כיוון ש-Ansible מסיר את אותו מארח משאר הריצה, ושורת הסיכום היא המקום היחיד שבו הוא מדווח על כך.

מפתח המארח (host key) השתנה. השמדתם ויצרתם מחדש את השרת, והשרת החדש עונה באותה כתובת עם מפתח חדש.

Host key verification failed.

הסירו את הרשומה הישנה באמצעות ssh-keygen -R 203.0.113.10. זה קורה ללא הרף ברגע ש-Terraform מבצע בנייה מחדש, וזו סיבה טובה להימנע מבנייה מחדש של מכונות המכילות נתונים.

פקודת sudo נכשלת. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} משמעו ש-become: true דורש סיסמה באותו מארח. הגדירו sudo ללא סיסמה עבור משתמש ה-deploy, או העבירו את --ask-become-pass.

Terraform רוצה להשמיד משהו שלא נגעתם בו. ה-plan מציג שינויים שלא כתבתם, מה שאומר שהתשתית בפועל סטתה מהקוד, בדרך כלל כי מישהו שינה הגדרה בלוח הבקרה של ספק הענן. הריצו את terraform plan -refresh-only כדי לראות את ההבדל בעצמכם, ואז החליטו אם הקוד או המשאב החי שגויים. לעולם אל תבצעו apply לתוכנית הרסנית שאינכם יכולים להסביר שורה אחר שורה.

Ansible מדווח על שינוי בכל הרצה. משימת shell ללא הגנת creates או when רצה ללא תנאי. זו אינה בעיה קוסמטית בלבד, כיוון שמשמעות הדבר היא שלא ניתן להשתמש יותר ב-changed=0 כסימן לכך שהשרת נמצא במצב שביקשתם.

FAQ

האם Terraform יכול להחליף את Ansible?

לא עבור הגדרות בתוך שרת. Terraform יכול להריץ סקריפטים באמצעות ה-provisioner מסוג remote-exec, אך אלו רצים רק בעת יצירת המשאב, אינם מופיעים ב-terraform plan, וגורמים למשאב להיחשב כפגום (tainted) במקרה של כשל, מה שמוביל לתזמון מחיקה ובנייה מחדש בהרצה הבאה. ל-Terraform אין מקבילה למודול שבודק אם Nginx כבר מותקן ולא מבצע דבר אם הוא אכן מותקן. השתמשו ב-Terraform ליצירת המכונה, ולאחר מכן העבירו את הטיפול ל-Ansible.

האם Ansible יכול להחליף את Terraform?

עבור מספר קטן של שרתים בעלי אורך חיים ארוך, כן. ל-Ansible יש מודולי ענן שיוצרים שרתים, ואם אתם מזמינים שני מופעי VPS ומחזיקים אותם לאורך זמן, זה מספיק. מה שאתם מאבדים הוא קובץ ה-state וגרף התלויות: הסרת משימה מה-playbook לא תגרום להפסקת המשאב, והוא ימשיך לרוץ ולחייב אתכם בתשלום, כיוון ש-Ansible לא תיעד שהוא יצר אותו. Terraform היה מתכנן פעולת מחיקה (destroy).

מה כדאי ללמוד קודם?

Ansible, אם יש ברשותכם שרתים כיום. הוא מחזיר את ההשקעה כבר במכונה הראשונה, אינו דורש דבר מלבד SSH, והמיומנות רלוונטית גם לשרת שהזמנתם ידנית. Terraform מחזיר את ההשקעה בשלב מאוחר יותר, כאשר בונים סביבות מחדש באופן תדיר או מנהלים משאבי ספק מעבר לשרתים, כגון רשומות DNS וחוקי firewall.

איך מעבירים את ה-IP של השרת החדש מ-Terraform ל-Ansible?

הגדירו output בקוד ה-Terraform שלכם, וקראו אותו לאחר ה-apply. הפקודה terraform output -raw web_ip מדפיסה את הערך הגולמי עבור החלפת משתנה ב-shell, ו-terraform output -json מספקת את כל ה-outputs בבת אחת כאשר ישנם כמה מארחים. כתבו זאת לקובץ inventory, או התקינו את ה-collection מסוג cloud.terraform והפנו את ansible-inventory -i terraform.yml --graph לספריית הפרויקט.

מדוע ה-playbook שלי נכשל מיד לאחר ש-Terraform מסיים?

הספק מדווח על השרת כנוצר ברגע שה-API שלו מאשר זאת, בעוד מערכת ההפעלה עדיין עולה, ולכן חיבורי SSH נדחים בשניות הראשונות. השגיאה היא UNREACHABLE! עם Connection refused. הפכו את ansible.builtin.wait_for_connection למשימה הראשונה ב-play במקום לנחש זמן המתנה (sleep), כיוון שזמן העלייה משתנה בהתאם ל-image ולתוכנית.