כיצד לבנות מודול Terraform מותאם אישית

הסופר בחר בקרן החופשית והפתוחה לקבל תרומה כחלק מתוכנית כתיבה בעבור תרומות.

הקדמה

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

במדריך זה, תיצור מודול Terraform שיצבור מספר Droplets מאחורי Load Balancer לשם התאמת חוסניות. תשתמש גם בתכונות הלולאה for_each ו- count של שפת התצורה של Hashicorp (HCL) כדי לפרוס מספר מותאם אישית של מודולים בו זמנית.

דרישות קדם

הערה: עבור המדריך זה בודק במיוחד עם Terraform 1.1.3.

מבנה ויתרונות של מודולים

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

מודולים מותאמים אישית של Terraform נוצרים כדי לארוז רכיבים מקושרים שנעשה בהם שימוש תדיר והופקעות משותפות בפרויקטים גדולים יותר. הם עצמאיים, מארזים רק את המשאבים, המשתנים והספקים שצריכים להם.

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

שימושי ליצירת מודולים מסכמי משאבים שלכם כאשר אתם מוצאים את עצמכם מחזירים אותם עם התאמות רגילות. אריזת משאב יחיד כמודול יכולה להיות מיותרת ולהסיר לאט את הפשטות של הארכיטקטורה הכוללת.

עבור פרויקטים קטנים לפיתוח ובדיקה, לא נדרש לשלב מודולים מאחר שהם לא מביאים הרבה שיפור בממקרים אלה. עם יכולתם להתאמה אישית, המודולים הם הרכיב הבונה של פרויקטים מורכבים ביותר מבחינת היתרונות המשמעותיים במניעת כפילות קוד. המודולים מציעים גם את היתרון שהגדרות צריכות שינוי רק במקום אחד, והן יוחלפו אז בשאר התשתיות.

בשלב הבא, תגדיר, תשתמש ותותאם מודולים בפרויקטי Terraform.

יצירת מודול

בסעיף זה, תגדיר מספר דרופלטים ומאזין עומס כמשאבים של Terraform ותארז אותם למודול. תבצע גם את המודול המתקבל ניתן להתאמה באמצעות קלטי מודול.

תאחסן את המודול בתיקייה בשם droplet-lb, תחת תיקייה בשם modules. בהנחה שאתה נמצא בתיקיית terraform-modules שיצרת כחלק מהקדם דרושים, תיצור את שניהם יחד על ידי הרצת:

  1. mkdir -p modules/droplet-lb

הארגומנט -p מציין ל־mkdir ליצור את כל התיקיות בנתיב הנתון.

נווט אליה:

  1. cd modules/droplet-lb

כפי שצוין בסעיף הקודם, מודולים מכילים את המשאבים והמשתנים שהם משתמשים בהם. החל מגרסה 0.13 של Terraform, הם חייבים גם לכלול הגדרות של ספקי השירות שהם משתמשים בהם. מודולים אינם דורשים תצורה מיוחדת כדי להעיד על כך שהקוד מייצג מודול, מאחר ש־Terraform מחשיב כל תיקייה שמכילה קוד HCL כמודול, גם התיקייה העיקרית של הפרויקט.

משתנים שמוגדרים במודול נחשפים כקלטים שלו וניתן להשתמש בהם בהגדרות המשאבים כדי להתאים אותם. המודול שתיצור יכלול שני קלטים: מספר הדרופלטים ליצירה ושם הקבוצה שלהם. תיצור ותפתח לעריכה קובץ בשם variables.tf שם תאחסן את המשתנים:

  1. nano variables.tf

הוסף את השורות הבאות:

modules/droplet-lb/variables.tf
variable "droplet_count" {}
variable "group_name" {}

שמור וסגור את הקובץ.

תאגד את ההגדרה של ה-Droplet בקובץ בשם droplets.tf. צור ופתח אותו לעריכה:

  1. nano droplets.tf

הוסף את השורות הבאות:

modules/droplet-lb/droplets.tf
resource "digitalocean_droplet" "droplets" {
  count  = var.droplet_count
  image  = "ubuntu-20-04-x64"
  name   = "${var.group_name}-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

לפרמטר count, שמציין כמה מופעים של משאב ליצור, אתה מעביר את המשתנה droplet_count. הערך שלו יוגדר כאשר המודול נקרא מקוד הפרויקט הראשי. שמות ה-Droplets שנפרסמו יהיו שונים, ותקבל זאת על ידי הוספת אינדקס ה-Droplet הנוכחי לשם הקבוצה שסופקה. ההפצה של ה-Droplets תהיה באזור fra1 והן יפעלו ב-Ubuntu 20.04.

כאשר תסיים, שמור וסגור את הקובץ.

עם ה-Droplets הוגדרים כעת, תוכל להמשיך ליצירת ה-Load Balancer. תאחסן את ההגדרה של המשאב שלו בקובץ בשם lb.tf. צור ופתח אותו לעריכה על ידי הרצת:

  1. nano lb.tf

הוסף את ההגדרה של המשאב שלו:

modules/droplet-lb/lb.tf
resource "digitalocean_loadbalancer" "www-lb" {
  name   = "lb-${var.group_name}"
  region = "fra1"

  forwarding_rule {
    entry_port     = 80
    entry_protocol = "http"

    target_port     = 80
    target_protocol = "http"
  }

  healthcheck {
    port     = 22
    protocol = "tcp"
  }

  droplet_ids = [
    for droplet in digitalocean_droplet.droplets:
      droplet.id
  ]
}

הגדר את ה-Load Balancer עם שם הקבוצה בשמו כדי להבדיל אותו. אתה מפרסם אותו באזור fra1 יחד עם ה-Droplets. הסעיפים הבאים יציינו את היעד ואת פורטי הניטור והפרוטוטוקולים.

הבלוק droplet_ids שמודגש מקבל את המזהים של ה-Droplets, שצריך לנהל על ידי ה-Balancer עומד. מאחר וישנם מספר רב של Droplets, והספירה שלהם לא ידועה מראש, אתה משתמש בלולאת for כדי לעבור על אוסף ה-Droplets (digitalocean_droplet.droplets) ולקחת את המזהים שלהם. אתה מקיף את לולאת ה-for בסוגריים מרובעים ([]) כך שהאוסף התוצאה יהיה רשימה.

שמור וסגור את הקובץ.

הגדרת את ה-Droplet, Load Balancer ומשתנים למודול שלך. עליך להגדיר את דרישות ספק השירותים, ולציין אילו ספקים משתמש המודול, כולל גרסא והמיקום שלהם. מאז Terraform 0.13, על המודולים להגדיר באופן מפורש את המקורות של ספקי השירותים שאינם נתמכים על ידי Hashicorp; זה כי הם לא יורשים אותם מהפרויקט האב.

תאחסן את דרישות ספק השירותים בקובץ בשם provider.tf. צור אותו לעריכה על ידי הרצת:

  1. nano provider.tf

הוסף את השורות הבאות כדי לדרוש את ספק השירותים digitalocean:

modules/droplet-lb/provider.tf
terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

שמור וסגור את הקובץ כאשר תסיים. המודול droplet-lb כעת מחייב את ספק השירותים digitalocean.

המודולים תומכים גם בפלטים, שבאפשרותך להשתמש בהם כדי לחלץ מידע פנימי על מצב המשאבים שלהם. תגדיר פלט שמציג את כתובת ה-IP של ה-Balancer, ותאחסן אותו בקובץ בשם outputs.tf. צור אותו לעריכה:

  1. nano outputs.tf

הוסף את ההגדרה הבאה:

modules/droplet-lb/outputs.tf
output "lb_ip" {
  value = digitalocean_loadbalancer.www-lb.ip
}

פלט זה משיג את כתובת ה-IP של ה-Balancer. שמור וסגור את הקובץ.

המודול droplet-lb כעת הושלם פונקציונלית ומוכן להפצה. תקרא לו מהקוד הראשי, שתאחסן אותו בשורש הפרויקט. תנווט תחילה אליו על ידי העלאה בתיקיית הקבצים שלך שתי פעמים:

  1. cd ../..

לאחר מכן, צור ופתח לעריכה קובץ בשם main.tf, בו תשתמש במודול:

  1. nano main.tf

הוסף את השורות הבאות:

main.tf
module "groups" {
  source = "./modules/droplet-lb"

  droplet_count = 3
  group_name    = "group1"
}

output "loadbalancer-ip" {
  value = module.groups.lb_ip
}

בהגדרה זו, אתה קורא למודול droplet-lb הממוקם בתיקייה המצויה בנתיב המסומן כ source. אתה מגדיר את הקלט שהוא מספק, droplet_count ו group_name, שמוגדר כ group1 כך שתוכל מאוחר יותר להבחין בין המופעות.

מאחר שפלט ה- IP של הטעינה הוגדר במודול, לא יוצג באופן אוטומטי כאשר תפעיל את הפרויקט. הפתרון לכך הוא ליצור פלט נוסף המשיג את הערך שלו (loadbalancer_ip).

שמור וסגור את הקובץ כאשר תסיים.

אתחל את המודול על ידי הרצת הפקודה:

  1. terraform init

הפלט ייראה כך:

Output
Initializing modules... - groups in modules/droplet-lb Initializing the backend... Initializing provider plugins... - Finding digitalocean/digitalocean versions matching "~> 2.0"... - Installing digitalocean/digitalocean v2.19.0... - Installed digitalocean/digitalocean v2.19.0 (signed by a HashiCorp partner, key ID F82037E524B9C0E8) ... Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.

ניתן לנסות לתכנן את הפרויקט כדי לראות אילו פעולות תקרה Terraform על ידי הרצת:

  1. terraform plan -var "do_token=${DO_PAT}"

הפלט יהיה דומה לזה:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # ה- module.groups.digitalocean_droplet.droplets[0] ייוצר + resource "digitalocean_droplet" "droplets" { ... + name = "group1-0" ... } # ה- module.groups.digitalocean_droplet.droplets[1] ייוצר + resource "digitalocean_droplet" "droplets" { ... + name = "group1-1" ... } # ה- module.groups.digitalocean_droplet.droplets[2] ייוצר + resource "digitalocean_droplet" "droplets" { ... + name = "group1-2" ... } # ה- module.groups.digitalocean_loadbalancer.www-lb ייוצר + resource "digitalocean_loadbalancer" "www-lb" { ... + name = "lb-group1" ... } Plan: 4 to add, 0 to change, 0 to destroy. ...

פלט זה מפרט כי Terraform תיצור שלושה Droplets, בשמות group1-0, group1-1, ו- group1-2, וגם תיצור Load Balancer בשם group1-lb, אשר ינהל את התעבורה מהוצאה ואל דרופלטים.

ניתן לנסות ליישם את הפרויקט בענן על ידי הרצת:

  1. terraform apply -var "do_token=${DO_PAT}"

הזן yes כאשר תתבקש. הפלט יציג את כל הפעולות וגם את כתובת ה-IP של ה-Load Balancer תוצג:

Output
module.groups.digitalocean_droplet.droplets[1]: Creating... module.groups.digitalocean_droplet.droplets[0]: Creating... module.groups.digitalocean_droplet.droplets[2]: Creating... ... Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: loadbalancer-ip = ip_address

יצרת מודול המכיל מספר של Droplets שניתנים להתאמה אישית ו-Load Balancer שיתוכנן באופן אוטומטי לנהל את התעבורה הנכנסת והיוצאת שלהם.

שינוי שמות למשאבים שהוטבעו

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

לדוגמה, פתח את main.tf לעריכה על ידי הרצת:

  1. nano main.tf

שנה את שם המודול groups ל־groups_renamed, כפי שמודגש:

main.tf
module "groups_renamed" {
  source = "./modules/droplet-lb"

  droplet_count = 3
  group_name    = "group1"
}

output "loadbalancer-ip" {
  value = module.groups_renamed.lb_ip
}

שמור וסגור את הקובץ. לאחר מכן, אתחל מחדש את הפרויקט:

  1. terraform init

כעת תוכל לתכנן את הפרויקט:

  1. terraform plan -var "do_token=${DO_PAT}"

הפלט יהיה ארוך, אך יראה דומה לכך:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create - destroy Terraform will perform the following actions: # module.groups.digitalocean_droplet.droplets[0] יושמד ... # module.groups_renamed.digitalocean_droplet.droplets[0] יוצר ...

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

במקום זאת, באמצעות הבלוק moved, תוכל להורות לטרהפורם להעביר משאבים ישנים תחת השם החדש. פתח את main.tf לעריכה והוסף את השורות הבאות לסוף הקובץ:

moved {
  from = module.groups
  to   = module.groups_renamed
}

כשתסיים, שמור וסגור את הקובץ.

כעת תוכל לתכנן את הפרויקט:

  1. terraform plan -var "do_token=${DO_PAT}"

כאשר אתה מתכנן עם הבלוק moved שמופיע ב־main.tf, טרהפורם מעוניינת להעביר את המשאבים, במקום לבנות אותם מחדש:

Output
Terraform will perform the following actions: # module.groups.digitalocean_droplet.droplets[0] הועבר ל־module.groups_renamed.digitalocean_droplet.droplets[0] ... # module.groups.digitalocean_droplet.droplets[1] הועבר ל־module.groups_renamed.digitalocean_droplet.droplets[1] ...

העברת משאבים משנה את מיקומם במצב ה־Terraform, כלומר המשאבים האמיתיים בענן לא ישתנו, ייהרסו או יוצרו מחדש.

מכיוון שתשנה את התצורה באופן משמעותי בשלב הבא, השמד את המשאבים שפורסמו על־ידי הרצת:

  1. terraform destroy -var "do_token=${DO_PAT}"

הזן yes כאשר יתבקש. הפלט יסתיים ב:

Output
... Destroy complete! Resources: 4 destroyed.

בסעיף זה, שינית את השמות של המשאבים בפרויקט ה־Terraform שלך בלי להשמיד אותם בתהליך. עכשיו תפרסם מספר מופעים של מודול מאותו קוד באמצעות for_each ו־count.

פרסום מספר מודולים

בסעיף זה, תשתמש ב־count ו־for_each כדי לפרסם את המודול droplet-lb מספר פעמים עם התאמות אישיות.

שימוש ב־count

אחד הדרכים להפעיל מספר רב של המודולים השונים יחדיו היא להעביר את הכמות אל פרמטר ה- count, שזמין באופן אוטומטי לכל מודול. פתח את main.tf לעריכה:

  1. nano main.tf

שנה אותו כך שיראה כך, מסיר את ההגדרה הקיימת של פלט והבלוק moved:

main.tf
module "groups" {
  source = "./modules/droplet-lb"

  count  = 3

  droplet_count = 3
  group_name    = "group1-${count.index}"
}

על ידי הגדרת count ל- 3, אתה מורה ל- Terraform להפעיל את המודול שלוש פעמים, כל אחת עם שם קבוצה שונה. כאשר תסיים, שמור וסגור את הקובץ.

תכנן את ההפצה על ידי הרצת:

  1. terraform plan -var "do_token=${DO_PAT}"

הפלט יהיה ארוך, ויראה כך:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # המודול.groups[0].digitalocean_droplet.droplets[0] ייווצר ... # המודול.groups[0].digitalocean_droplet.droplets[1] ייווצר ... # המודול.groups[0].digitalocean_droplet.droplets[2] ייווצר ... # המודול.groups[0].digitalocean_loadbalancer.www-lb ייווצר ... # המודול.groups[1].digitalocean_droplet.droplets[0] ייווצר ... # המודול.groups[1].digitalocean_droplet.droplets[1] ייווצר ... # המודול.groups[1].digitalocean_droplet.droplets[2] ייווצר ... # המודול.groups[1].digitalocean_loadbalancer.www-lb ייווצר ... # המודול.groups[2].digitalocean_droplet.droplets[0] ייווצר ... # המודול.groups[2].digitalocean_droplet.droplets[1] ייווצר ... # המודול.groups[2].digitalocean_droplet.droplets[2] ייווצר ... # המודול.groups[2].digitalocean_loadbalancer.www-lb ייווצר ... Plan: 12 to add, 0 to change, 0 to destroy. ...

פרטי Terraform בפלט של כל אחת משלושת המודולים יכלול שלוש Droplets ו-Load Balancer המשויכים אליהם.

שימוש ב- for_each

ניתן להשתמש ב- for_each עבור מודולים כאשר נדרשת התאמה מורכבת יותר של המופע, או כאשר מספר המופעים תלוי בנתונים של צד שלישי (לעיתים תוצג כמפות) שאינם ידועים בעת כתיבת הקוד.

עכשיו עליך להגדיר מפה שמקשרת בין שמות קבוצות למספרי Droplet ולהפעיל מופעים של droplet-lb לפי זה. פתח main.tf לעריכה על ידי הרצת:

  1. nano main.tf

שנה את הקובץ כך שיראה כך:

main.tf
variable "group_counts" {
  type    = map
  default = {
    "group1" = 1
    "group2" = 3
  }
}

module "groups" {
  source   = "./modules/droplet-lb"
  for_each = var.group_counts

  droplet_count = each.value
  group_name    = each.key
}

למעשה, אתה מגדיר מפה ראשית בשם group_counts שמכילה כמה Droplets צריך להיות לקבוצה נתונה. לאחר מכן, אתה קורא למודול droplet-lb, אך מציין שללול for_each צריך לפעול על var.group_counts, המפה שהגדרת כמעט קודם. droplet_count מקבל את each.value, הערך של הזוג הנוכחי, שהוא מספר ה-Droplets לקבוצה הנוכחית. group_name מקבל את שם הקבוצה.

שמור וסגור את הקובץ כשתסיים.

נסה ליישם את התצורה על ידי הרצת:

  1. terraform plan -var "do_token=${DO_PAT}"

הפלט יפרט את הפעולות ש-Terraform יבצע כדי ליצור את שתי הקבוצות עם ה-Droplets שלהם וה-Load Balancers:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # יוצר את module.groups["group1"].digitalocean_droplet.droplets[0] ... # יוצר את module.groups["group1"].digitalocean_loadbalancer.www-lb ... # יוצר את module.groups["group2"].digitalocean_droplet.droplets[0] ... # יוצר את module.groups["group2"].digitalocean_droplet.droplets[1] ... # יוצר את module.groups["group2"].digitalocean_droplet.droplets[2] ... # יוצר את module.groups["group2"].digitalocean_loadbalancer.www-lb ...

בשלב זה, השתמשת ב-count ו-for_each כדי לפרוס מספר מותאמים אישית של אותו מודול מהקוד המרכזי.

מסקנה

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

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

Source:
https://www.digitalocean.com/community/tutorials/how-to-build-a-custom-terraform-module