איך להפעיל אפליקציות איזון משקל ב-DigitalOcean עם CDK עבור Terraform ו-TypeScript

המחבר בחר ב קרן ויקימדיה לקבלת תרומה כחלק מתוכנית כתיבה עבור תרומות.

הקדמה

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

עם IaC, שינויים בתשתית שלך יכולים לעבור את אותו תהליך ביקורת קוד כמו קוד היישום שלך. ניתן לאחסן את הקוד בבקרת גרסאות (כמו Git) כדי לשמור היסטוריה של מצב התשתית שלך, וניתן לאוטומט את תהליך ההטמעה עוד יותר עם כלים ברמה גבוהה יותר כמו פלטפורמת מפתחים פנימית (IDP).

Terraform היא כלי IaC פופולרי האינו תלוי בפלטפורמה מסוימת עקב תמיכה רחבה שלו במגוון רחב של פלטפורמות, כולל GitHub, Cloudflare, ו־DigitalOcean. רוב תצורות Terraform נכתבות באמצעות שפת תיאור דקלרטיבית הנקראת HashiCorp Configuration Language (HCL).

כלי הפיתוח של הענן עבור Terraform (CDKTF) הוא כלי בנוי מעל Terraform שמאפשר לך להגדיר תשתיות באמצעות שפת תכנות מוכרת (כגון TypeScript, Python, או Go) במקום HCL. כלי זה יכול לספק מכף רגל נמוך יותר למפתחים שאינם מכירים את HCL, בעוד שהוא מאפשר למפתחים להשתמש בתכונות תכנות טבעיות כמו לולאות, משתנים ופונקציות.

במדריך זה, תתחיל בהתקנת כלי שורת הפקודה (CLI) cdktf. לאחר מכן, תיצור פרוייקט CDKTF ב־TypeScript ותגדיר את הפרוייקט עם שני שרתי NGINX המאוזנים בעזרת מאזן עומס. לבסוף, תשתמש ב־cdktf כדי להפעיל את התשתיות. בסיום המדריך, יהיה לך פרוייקט CDKTF ממנו תוכל לבנות כדי להרחיב את התשתיות שלך.

הערה: המדריך הזה נבדק עם CDKTF 0.11.2 ו-Terraform 1.2.2.

דרישות מוקדמות

כדי להשלים את המדריך הזה, תצטרך:

שלב 1 — התקנת פקודת השורת פקודה של cdktf

כדי להתחיל, תתקינו את כלי השורת פקודה של cdktf.

פקודת השורת פקודה של cdktf זמינה כחבילת NPM. אם תחפשו את cdktf ב־npmjs.com, תמצאו שתי חבילות בשמות דומים: cdktf ו־cdktf-cli.

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

  • ספרייה המכילה מערכת של בני דוברת טבעית (כגון פונקציות ומחלקות) להגדרת תשתיות. חלק זה מוכסה בתוך החבילה npm של cdktf. לדוגמה, ניתן לראות את השימוש במחלקות App ו־TerraformStack מהחבילה cdktf בפרויקט דוגמא הבא של CDKTF:

    import { App, TerraformStack } from "cdktf";
    class APIStack extends TerraformStack {}
    const app = new App();
    new APIStack(app, "feature-x");
    app.synth();
    
  • מתאים שמנתח את המבנים בתוך פרויקט CDKTF ומפחית אותם לסט של מסמכי JSON, שנספקים ל-Terraform באותו אופן ש-HCL נספק. ממשק זה מוכל בתוך כלי CLI הנקרא cdktf, המסופק על ידי החבילה cdktf-cli.

כדי להתקין את כלי ה-CLI cdktf, נדרשת החבילה cdktf-cli. ניתן להתקין חבילה זו באופן גלובאלי באמצעות npm, yarn, או מנהל חבילות לבחירתכם.

כדי להתקין את cdktf-cli עם npm, הפעילו את הפקודה הבאה:

  1. npm install --global [email protected]

הערה: ייתכן כי יהיה גרסה חדשה יותר של החבילה cdktf-cli לאחר פרסום המאמר זה. ניתן לנסות לעקוב אחר המדריך עם הגרסה העדכנית ביותר על ידי הרצת npm install --global cdktf-cli@latest, אך יש לקחת בחשבון כי ייתכנו שינויים קלים בתוצאות.

בנוסף, ניתן להשתמש ב-Homebrew ב-macOS או Linux כדי להתקין את כלי ה-CLI cdktf באמצעות הנוסחה cdktf:

  1. brew install cdktf

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

  1. cdktf

תראו פלט דומה לזה:

Output
Please pass a command to cdktf, here are all available ones: cdktf Commands: cdktf init Create a new cdktf project from a template. cdktf get Generate CDK Constructs for Terraform providers and modules. cdktf convert Converts a single file of HCL configuration to CDK for Terraform. cdktf deploy [stacks...] Deploy the given stacks cdktf destroy [stacks..] Destroy the given stacks cdktf diff [stack] Perform a diff (terraform plan) for the given stack cdktf list List stacks in app. cdktf login Retrieves an API token to connect to Terraform Cloud. cdktf synth Synthesizes Terraform code for the given app in a directory. cdktf watch [stacks..] [experimental] Watch for file changes and automatically trigger a deploy cdktf output [stacks..] Prints the output of stacks cdktf debug Get debug information about the current project and environment cdktf completion generate completion script Options: --version הצג מספר גרסה --disable-logging אל תכתוב קבצי יומן. נתמך באמצעות הסביבה CDKTF_DISABLE_LOGGING. --disable-plugin-cache-env אל תגדיר אוטומטית את TF_PLUGIN_CACHE_DIR. --log-level איזה רמת יומן יש לכתוב. -h, --help Show help Options can be specified via environment variables with the "CDKTF_" prefix (e.g. "CDKTF_OUTPUT")

הפלט מראה את הפקודות הזמינות. בשאר המדריך, תרכוש ניסיון בשימוש ב־cdktf init, cdktf get, cdktf deploy, ו־cdktf destroy.

כעת שהתקנת את CLI של cdktf, תוכל להגדיר תשתיות על ידי כתיבת קוד TypeScript כלשהו.

שלב 2 — יצירת פרויקט CDKTF חדש

בשלב זה, תשתמש ב־CLI של cdktf שהתקנת כדי ליצור פרויקט CDKTF תבניתי, עליו תבנה בשלבים הבאים.

צור ספרייה שתשמש בפרויקט CDKTF על ידי הרצת הפקודה הבאה:

  1. mkdir infra

לאחר מכן, נווט אל הספרייה שנוצרה חדשה:

  1. cd infra/

השתמש בפקודת cdktf init כדי ליצור תבנית לפרויקט CDKTF שתבנה עליו:

  1. cdktf init --template=typescript --project-name=base --project-description="Base architecture" --local

CDKTF מאפשר למפתחים להגדיר תשתיות באמצעות TypeScript, Python, Java, C# או Go. האפשרות --template=typescript אומרת ל־cdktf ליצור תבנית לפרוייקט CDKTF עם שימוש ב־TypeScript.

טרהפורם (ולכן CDKTF) משמרת את המשאבים שהיא מנהלת על ידי רישום ההגדרות והמצבים שלהם בקבצים הנקראים קבצי Terraform state. האפשרות --local אומרת ל־CDKTF לשמור את קבצי המצב הללו מקומית על המכונה שהופעל עליה cdktf (כל קובץ עוקב אחרי המבנה של terraform.<stack>.tfstate).

לאחר הרצת הפקודה, ייתכן שה-CLI תבקש את הרשאתך לשלוח דוחות קריסה לצוות CDKTF כדי לעזור להם לשפר את המוצר:

Output
? Do you want to send crash reports to the CDKTF team? See https://www.terraform.io/cdktf/create-and-deploy/configuration-file for more information (Y/n)

הקלד Y אם ברצונך להסכים או N אם אתה מסכים, ואז לחץ על ENTER.

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

Output
Your cdktf typescript project is ready! cat help Print this message Compile: npm run get Import/update Terraform providers and modules (you should check-in this directory) npm run compile Compile typescript code to javascript (or "npm run watch") npm run watch Watch for changes and compile typescript in the background npm run build Compile typescript Synthesize: cdktf synth [stack] Synthesize Terraform resources from stacks to cdktf.out/ (ready for 'terraform apply') Diff: cdktf diff [stack] Perform a diff (terraform plan) for the given stack Deploy: cdktf deploy [stack] Deploy the given stack Destroy: cdktf destroy [stack] Destroy the stack Test: npm run test Runs unit tests (edit __tests__/main-test.ts to add your own tests) npm run test:watch Watches the tests and reruns them on change Upgrades: npm run upgrade Upgrade cdktf modules to latest version npm run upgrade:next Upgrade cdktf modules to latest "@next" version (last commit)

תראה גם קבצים חדשים הנוספים לתיקיית infra. הקבצים החשובים ביותר הם cdktf.json ו־main.ts.

cdktf.json הוא קובץ הקונפיגורציה של הפרוייקט CDKTF. אם תפתח את הקובץ, תראה משהו דומה לזה:

cdktf.json
{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "28c87598-4343-47a9-bb5d-8fb0e031c41b",
  "terraformProviders": [],
  "terraformModules": [],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
    "allowSepCharsInLogicalIds": "true"
  }
}

המאפיין app מגדיר את הפקודה שתופעל כדי לסינתזז את קוד ה־TypeScript ל־JSON שניתן לעבוד עם טרהפורם. מאפיין זה מציין כי main.ts הוא נקודת הכניסה לפרוייקט CDKTF.

אם תפתח את הקובץ main.ts, תראה משהו דומה לזה:

main.ts
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    // הגדר משאבים כאן
  }
}

const app = new App();
new MyStack(app, "infra");
app.synth();

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

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

המבנה הקדם-מוגדר של פרויקט CDKTF שנוצר ב-main.ts מכיל מחלקת מחסנית יחידה בשם MyStack, המגדירה כרגע לא משאבים. נוצרת מופע של MyStack עם השם infra, המוכל בתוך יישום בשם app. בשלבים לאחר מכן, תגדיר משאבי תשתיות בתוך בנאי ה-MyStack.

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

שלב 3 – התקנת ספק ה-DigitalOcean

בשלב זה, תתקין את ספק DigitalOcean לתוך פרויקט CDKTF.

ספקים הם ספריות שמספקות הוראות ל-Terraform (שמשתמש ב־cdktf תחת המסכה) על איך ליצור, לעדכן ולמחוק משאבים בספקי ענן, ספקי SaaS ופלטפורמות אחרות המחשפות ממשקי תכנות לאפליקציות (APIs). ספקים מכניסים את הלוגיקה של קריאה ל-APIs הללו לפונקציות סטנדרטיות ש-Terraform יכול לקרוא.

לדוגמה, אם תרצה ליצור Droplet חדש ב-DigitalOcean בלי Terraform, תצטרך לשלוח בקשת POST לנקודת הקצה /v2/droplets של API של DigitalOcean. בעזרת Terraform, תתקין במקום זאת את ספק DigitalOcean ותגדיר משאב digitalocean_droplet, דומה לקטע הדוגמה הבא:

new Droplet(this, 'web', {
  image: 'ubuntu-20-04-x64',
  name,
  region: 'lon1',
  size: 's-1vcpu-1gb',
}

אז תוכל להשתמש בכלי ה-CLI של cdktf כדי לתרגם קוד TypeScript זה ל-JSON שתואם ל-Terraform ולהעביר אותו לספק, שיעשה את הקריאות ל-APIs המתאימות כדי ליצור את ה-Droplet במקומך.

עכשיו שהבנת מהו ספק, תוכל להתקין את ספק DigitalOcean לפרויקט CDKTF שלך.

פתח את קובץ ה־cdktf.json והוסף את המחרוזת digitalocean/digitalocean למערך של terraformProviders:

cdktf.json
{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "28c87598-4343-47a9-bb5d-8fb0e031c41b",
  "terraformProviders": ["digitalocean/digitalocean"],
  "terraformModules": [],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
    "allowSepCharsInLogicalIds": "true"
  }
}

digitalocean/digitalocean הוא המזהה עבור ספק השירות של DigitalOcean ב־רשות Terraform.

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

בשלב הבא, הרץ cdktf get כדי להוריד ולהתקין את ספק השירות.

  1. cdktf get

cdktf get יוריד את ספק השירות, יחלץ את הסכמה, ייצור את הקבצים המתאימים ב־TypeScript, ויביא אותו כמודול TypeScript תחת .gen/providers/. הייצור אוטומטי של קוד זה מאפשר לך להשתמש בכל ספקי שירות ומודולי HCL עם CDKTF, וזה האופן שבו CDKTF יכול לספק השלמות קוד בעורכים שתומכים בזה.

כאשר cdktf get מסיים לרוץ, תראה פלט דומה לזה:

Output
Generated typescript constructs in the output directory: .gen

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

בשלב זה, התקנת את ספק השירות digitalocean/digitalocean לפרוייקט. בשלב הבא, תגדיר את ספק השירות של DigitalOcean עם האישורים הדרושים כדי לאמת את ספק השירות עם ממשק ה־API של DigitalOcean.

שלב 4 — הגדרת ספק השירות של DigitalOcean

בשלב זה, תקבע את ספק DigitalOcean עם האסימון האישי שלך ב-DigitalOcean, שמאפשר לספק לקרוא ל-API של DigitalOcean בשמך.

ספקים שונים דורשים ותומכים באישורים שונים לגישה ל-API. לספק DigitalOcean, עליך לספק את האסימון האישי שלך ב-DigitalOcean. ניתן לציין את האסימון לספק על ידי הגדרת המשתנה הסביבתי כ-DIGITALOCEAN_TOKEN או DIGITALOCEAN_ACCESS_TOKEN.

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

  1. export DIGITALOCEAN_ACCESS_TOKEN="your_personal_access_token"

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

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

main.ts
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { DigitaloceanProvider } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    new DigitaloceanProvider(this, 'provider')
    
  }
}

const app = new App();
new MyStack(app, "infra");
app.synth();

המודול לספק נמצא ב-./.gen/providers/digitalocean, שנוצר אוטומטית כאשר הרצת cdktf get.

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

שלב 5 — הגדרת אפליקציות אינטרנט על דרופלטים

בשלב זה, תגדיר שני שרתי NGINX, כל אחד משרת קבצים שונים, המוצגים על שני Droplets זהים הפועלים על Ubuntu 20.04.

אתה מתחיל עם ההגדרה של שני ה-Droplets. שנה את main.ts עם השינויים המודגשים:

main.ts
...
import { DigitaloceanProvider, Droplet } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    ...
    const dropletNames = ['foo', 'bar']
    const droplets = dropletNames.map(name => new Droplet(this, name, {
        image: 'ubuntu-20-04-x64',
        name,
        region: 'lon1',
        size: 's-1vcpu-1gb',
      })
    )
  }
}

אתה משתמש בלולאה ניטיבית של JavaScript (Array.prototype.map()) כדי למנוע כפילות בקוד.

כך כמו שאתה יוצר את ה-Droplet דרך הקונסולה, ישנם מספר פרמטרים לציין:

  • image – ההפצה והגרסה של Linux שבה ירוץ ה-Droplet.
  • region – מרכז הנתונים בו ירוץ ה-Droplet.
  • size – כמות המשאבים של המעבד והזיכרון שיוקצו ל-Droplet.
  • name – שם ייחודי המשמש להפניה ל-Droplet.

הערכים עבור image, region ו־size חייבים להיות דברים שתמיכה בהם DigitalOcean. ניתן למצוא את הערכים התקפים (הנקראים slugs) עבור כל הדיסטריבוציות של Linux הנתמכות, גדלי Droplet, ואזורים על דף ה-DigitalOcean API Slugs. ניתן למצוא רשימה מלאה של מאפיינים נדרשים ואופציונליים בדף התיעוד של digitalocean_droplet.

הוספת מפתח SSH

כחלק מהדרישות המוקדמות, העלית מפתח ציבורי SSH ללא סיסמה לחשבון שלך ב-DigitalOcean ורשמת את שמו. כעת תשתמש בשם זה כדי לאחזר את זיהוי המפתח של SSH ולהעביר אותו להגדרת ה-Droplet שלך.

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

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

עדיין ב-main.ts ובתוך הבנאי של MyStack, הגדר מקור נתונים חדש בשם DataDigitaloceanSshKey, והעבר את השם שהקצית למפתח ה-SSH שלך (כאן, השם הוא do_cdktf):

main.ts
...
import { DataDigitaloceanSshKey, DigitaloceanProvider, Droplet } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    ...
    const dropletNames = ['foo', 'bar']
    const sshKey = new DataDigitaloceanSshKey(this, 'sshKey', {
      name: 'do_cdktf',
    })
    const droplets = dropletNames.map(name => new Droplet(this, name, {
    ...
  }
}
...

לאחר מכן, עדכן את ההגדרה של ה-Droplet כדי לכלול את מפתח ה-SSH:

main.ts
...
const droplets = dropletNames.map(name => new Droplet(this, name, {
  image: 'ubuntu-20-04-x64',
  name,
  region: 'lon1',
  size: 's-1vcpu-1gb',
  sshKeys: [sshKey.id.toString()]
}))
...

כאשר הוא מופעל, אפשר לגשת ל-Droplet באמצעות מפתח SSH פרטי במקום סיסמה.

ציון סקריפט של נתוני המשתמש כדי להתקין את NGINX

עכשיו הגדרת שני Droplets זהים הרצים Ubuntu, מוגדרים עם גישת SSH. המשימה הבאה היא להתקין את NGINX על כל Droplet.

כאשר יוצרים Droplet, כלי בשם CloudInit יאתחל את השרת. CloudInit יכול לקבל קובץ בשם user data, שיכול לשנות איך השרת מואתחל. נתוני המשתמש יכולים להיות כל קבצי cloud-config או סקריפטים שהשרת יכול לפרש, כמו סקריפטים של Bash.

בשארית השלב הזה, תיצרו סקריפט של Bash ותציין אותו כנתוני המשתמש של ה-Droplet. הסקריפט יתקין את NGINX כחלק מתהליך האתחול. בנוסף, הסקריפט יחליף גם את תוכן קובץ /var/www/html/index.html (הקובץ הברירת מחדל שמוצג על ידי NGINX) עם שם המארח וכתובת ה-IP של ה-Droplet, וזה יגרום לשני שרתי NGINX לשרת קבצים שונים. בשלב הבא, תציבו שני שרתי NGINX אלה מאחורי מאזן עומס; על ידי שירות קבצים שונים, יהיה ברור האם המאזן עומס מפיץ בקשות בצורה נכונה או לא

עדיין ב-main.ts, הוסיפו מאפיין חדש של userData לאובייקט התצורה של ה-Droplet:

main.ts
...
class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    ...
    const droplets = dropletNames.map(name => new Droplet(this, name, {
      image: 'ubuntu-20-04-x64',
      name,
      region: 'lon1',
      size: 's-1vcpu-1gb',
      sshKeys: [sshKey.id.toString()],
      userData: `#!/bin/bash

apt-get -y update
apt-get -y install nginx
export HOSTNAME=$(curl -s http://169.254.169.254/metadata/v1/hostname)
export PUBLIC_IPV4=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
echo Droplet: $HOSTNAME, IP Address: $PUBLIC_IPV4 > /var/www/html/index.html
`
    }))
  }
}

אזהרה: וודאו שאין שורות חדשות לפני ה-shebang (#!); אחרת, הסקריפט עשוי שלא להתבצע.

כאשר הדרופלט נקבע לראשונה, הסקריפט ירוץ כמשתמש root. הוא ישתמש במנהל החבילות של אובונטו, APT, כדי להתקין את החבילה של nginx. לאחר מכן, הוא ישתמש ב-שירות המטה-נתונים של דיגיטל אושן כדי לקבל מידע על עצמו ולכתוב את שם המארח וכתובת ה-IP לתוך index.html, שמוצג על ידי NGINX.

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

שלב 6 — הגדרת מאזן טעינה

בשלב זה, תגדיר מאזן טעינה של דיגיטל אושן על ידי הגדרת מופע של המשאב digitalocean_loadbalancer.

עדיין בתוך main.ts, הוסף את ההגדרה הבאה עבור מאזן טעינה בסוף בנאי ה-MyStack:

main.ts
...
import { App, Fn, TerraformStack } from "cdktf";
import { DataDigitaloceanSshKey, DigitaloceanProvider, Droplet, Loadbalancer } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    ...
    new Loadbalancer(this, 'lb', {
      name: 'default',
      region: 'lon1',
      algorithm: 'round_robin',
      forwardingRule: [{
        entryProtocol: 'http',
        entryPort: 80,
        targetProtocol: 'http',
        targetPort: 80,
      }],
      dropletIds: droplets.map((droplet) => Fn.tonumber(droplet.id))
    })
  }
}
...

הארגומנט forwardingRule אומר למאזין העומס להאזין לבקשות HTTP בפורט 80 ולהעביר אותן לכל אחת מה-Droplets על פורט 80.

ה-dropletIds מציינים את ה-Droplets אליהם המאזין לעומס יעביר את הבקשות. זה מקבל מספר, אך ערך של droplet.id הוא מחרוזת. לכן, השתמשת ב־Fn.tonumber פונקציית Terraform כדי להמיר את ערך מזהה ה-Droplet ממחרוזת למספר.

הערה: השתמשת בפונקציית Terraform Fn.tonumber כאן במקום ב־parseInt המובנית ב-JavaScript מאחר וערך של droplet.id אינו ידוע עד שה-Droplet מוצב. פונקציות Terraform מיועדות לפעול על ערכי הרץ שאינם ידועים לפני ש-Terraform מחיל הגדרה.

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

כעת הגדרת שני Droplets ומאזין עומס שנמצא מולם. הקובץ שלך main.ts צריך להיראות דומה לזה:

main.ts
import { Construct } from "constructs";
import { App, Fn, TerraformStack } from "cdktf";
import { DataDigitaloceanSshKey, DigitaloceanProvider, Droplet, Loadbalancer } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    new DigitaloceanProvider(this, 'provider')

    const dropletNames = ['foo', 'bar']
    const sshKey = new DataDigitaloceanSshKey(this, 'sshKey', {
      name: 'do_cdktf',
    })
    const droplets = dropletNames.map(name => new Droplet(this, name, {
        image: 'ubuntu-20-04-x64',
        name,
        region: 'lon1',
        size: 's-1vcpu-1gb',
        sshKeys: [sshKey.id.toString()],
        userData: `#!/bin/bash

apt-get -y update
apt-get -y install nginx
export HOSTNAME=$(curl -s http://169.254.169.254/metadata/v1/hostname)
export PUBLIC_IPV4=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
echo Droplet: $HOSTNAME, IP Address: $PUBLIC_IPV4 > /var/www/html/index.html
`
      })
    )

    new Loadbalancer(this, 'lb', {
      name: 'default',
      region: 'lon1',
      algorithm: 'round_robin',
      forwardingRule: [{
        entryProtocol: 'http',
        entryPort: 80,
        targetProtocol: 'http',
        targetPort: 80,
      }],
      dropletIds: droplets.map((droplet) => Fn.tonumber(droplet.id))
    })
  }
}

const app = new App();
new MyStack(app, "infra");
app.synth();

בשלב הבא, תשתמש בכלי ה-CLI של cdktf כדי להפעיל את פרוייקט ה-CDKTF שלך בפועל.

שלב 7 — הפעלת התשתיות שלך

בשלב זה, תשתמשו בכלי ה-CLI של cdktf כדי לפרוס את ה-Droplets וה-load balancers שהגדרתם בשלבים הקודמים.

ודאו שאתם נמצאים בתיקיית infra/ ושהגדרתם את משתנה הסביבה DIGITALOCEAN_ACCESS_TOKEN עבור ישוב הטרמינל שלכם, ואז הריצו את הפקודה cdktf deploy:

  1. cdktf deploy

תראו פלט דומה לזה:

Output
infra Initializing the backend... infra Initializing provider plugins... infra - Reusing previous version of digitalocean/digitalocean from the dependency lock file infra - Using previously-installed digitalocean/digitalocean v2.19.0 infra Terraform has been successfully initialized! infra 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: infra # digitalocean_droplet.bar (bar) will be created + resource "digitalocean_droplet" "bar" { + backups = false + created_at = (known after apply) + disk = (known after apply) + graceful_shutdown = false + id = (known after apply) + image = "ubuntu-20-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "bar" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "lon1" + resize_disk = true + size = "s-1vcpu-1gb" + ssh_keys = [ + "34377800", ] + status = (known after apply) + urn = (known after apply) + user_data = "f9b1d9796d069fe504ce0d89439b6b664b14b1a1" + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } # digitalocean_droplet.foo (foo) יוצר + resource "digitalocean_droplet" "foo" { + backups = false + created_at = (known after apply) + disk = (known after apply) + graceful_shutdown = false + id = (known after apply) + image = "ubuntu-20-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "foo" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "lon1" + resize_disk = true + size = "s-1vcpu-1gb" + ssh_keys = [ + "34377800", ] + status = (known after apply) + urn = (known after apply) + user_data = "f9b1d9796d069fe504ce0d89439b6b664b14b1a1" + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } # digitalocean_loadbalancer.lb (lb) יוצר + resource "digitalocean_loadbalancer" "lb" { + algorithm = "round_robin" + disable_lets_encrypt_dns_records = false + droplet_ids = (known after apply) + enable_backend_keepalive = false + enable_proxy_protocol = false + id = (known after apply) + ip = (known after apply) + name = "default" + redirect_http_to_https = false + region = "lon1" + size_unit = (known after apply) + status = (known after apply) + urn = (known after apply) + vpc_uuid = (known after apply) + forwarding_rule { + certificate_id = (known after apply) + certificate_name = (known after apply) + entry_port = 80 + entry_protocol = "http" + target_port = 80 + target_protocol = "http" + tls_passthrough = false } + healthcheck { + check_interval_seconds = (known after apply) + healthy_threshold = (known after apply) + path = (known after apply) + port = (known after apply) + protocol = (known after apply) + response_timeout_seconds = (known after apply) + unhealthy_threshold = (known after apply) } + sticky_sessions { + cookie_name = (known after apply) + cookie_ttl_seconds = (known after apply) + type = (known after apply) } } Plan: 3 to add, 0 to change, 0 to destroy. ───────────────────────────────────────────────────────────────────────────── Saved the plan to: plan To perform exactly these actions, run the following command to apply: terraform apply "plan" Please review the diff output above for infra ❯ Approve Applies the changes outlined in the plan. Dismiss Stop

הערה: CDKTF עדיין נמצא בפיתוח, והפלט עשוי להשתנות מאשר מה שמוצג למעלה.

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

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

תראו פלט דומה לזה:

Output
infra digitalocean_droplet.foo (foo): Creating... digitalocean_droplet.bar (bar): Creating... infra digitalocean_droplet.bar (bar): Still creating... [10s elapsed] infra digitalocean_droplet.foo (foo): Still creating... [10s elapsed] 1 Stack deploying 0 Stacks done 0 Stacks waiting

הפלט הזה מודיע לכם ש-cdktf מתקשר עם API של DigitalOcean כדי ליצור את ה-Droplet. cdktf יוצר את ה-Droplets תחילה מכיוון שה-load balancer תלוי בזיהוי ה-Droplet, שאינו ידוע עד שה-Droplets יופרסמו.

יצירת ה-Droplet נוהגת לקחת פחות מדקה. לאחר שה-Droplets יופרסמו, cdktf ממשיך ליצור את ה-load balancer.

Output
infra digitalocean_droplet.bar (bar): Creation complete after 54s [id=298041598] infra digitalocean_droplet.foo (foo): Creation complete after 55s [id=298041600] infra digitalocean_loadbalancer.lb (lb): Creating... infra digitalocean_loadbalancer.lb (lb): Still creating... [10s elapsed]

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

Output
infra digitalocean_loadbalancer.lb (lb): Still creating... [1m30s elapsed] infra digitalocean_loadbalancer.lb (lb): Creation complete after 1m32s [id=4f9ae2b7-b649-4fb4-beed-96b95bb72dd1] infra Apply complete! Resources: 3 added, 0 changed, 0 destroyed. No outputs found.

באפשרותך כעת לבקר ב- קונסולת DigitalOcean, כאשר תראה מאזין עומס בשם default ושני Droplets בריאים בשמות foo ו- bar, כאשר כל אחד מהם משמש כיעד עבור המאזין לעומס.

באפשרותך לבדוק אם NGINX רץ ומשרת תוכן כראוי על ידי ביקור בכתובת ה-IP של כל Droplet. אתה צריך לראות טקסט דומה לזה:

Droplet: bar, IP Address: droplet_ip

אם אינך רואה מחרוזת טקסט זו או שהשרת אינו מגיב, בדוק שהנתונים של המשתמש שציינת נכונים ושאין תווים (כולל שורות חדשות) שלפני השובץ (#!). בנוסף, באפשרותך להתחבר ב-SSH אל ה-Droplet באמצעות המפתח הפרטי שלך ולסקור את קבצי הלוגים שנוצרו על ידי CloudInit ב- /var/log/cloud-init-output.log:

  1. ssh -i path_to_ssh_private_key root@droplet_ip

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

הרץ את הפקודה הבאה מהטרמינל שלך כדי לשלוח עשר בקשות למאזין לעומס:

  1. for run in {1..10}; do curl http://load_balancer_ip/; done

תראה פלט דומה לזה, אף על פי שכתובות ה-IP המוצגות יהיו שונות:

Output
Droplet: foo, IP Address: droplet_foo_ip Droplet: bar, IP Address: droplet_bar_ip Droplet: foo, IP Address: droplet_foo_ip Droplet: bar, IP Address: droplet_bar_ip Droplet: bar, IP Address: droplet_bar_ip Droplet: foo, IP Address: droplet_foo_ip Droplet: bar, IP Address: droplet_bar_ip Droplet: foo, IP Address: droplet_foo_ip Droplet: bar, IP Address: droplet_bar_ip Droplet: foo, IP Address: droplet_foo_ip

הפלט מציין כי בקשות למאזין לעומס הועברו לכל Droplet חמישיות, מרמז על פעולתו של המאזין לעומס.

הערה: המאזין לעומס לא תמיד ישווה בין שני ה-Droplets בצורה מושלמת; יתכן שתמצא שארבע בקשות נשלחו לאחד ה-Droplets ושש לאחר. התנהגות זו היא תקינה.

בשלב זה, השתמשת ב־cdktf כדי להגדיר את המשאבים שלך, ואז השתמשת בממשק ניהול של DigitalOcean כדי לגלות את כתובות ה־IP של ה־Droplets וה־load balancer שלך. לאחר מכן, שלחת בקשות לכל Droplet ול־load balancer כדי לוודא שהם פועלים.

בשלב הבא, תקבל את כתובות ה־IP של ה־Droplets וה־load balancer מבלי להתחבר לממשק ניהול של DigitalOcean.

שלב 8 — פלט מידע

בשלב הקודם, היית צריך להתחבר לממשק ניהול של DigitalOcean כדי לקבל את כתובות ה־IP של ה־Droplet וה־load balancer שלך. בשלב זה, תשנה את הקוד שלך למעט כך שהמידע יודפס בפלט של פקודת cdktf deploy, וכך תחסוך לך טיול לממשק הניהול.

‏Terraform רשומה את התצורה והמצב של המשאבים שהוא ניהל בקבצי מצב. עבור הערך infra שלך, קובץ המצב נמצא ב־infra/terraform.infra.tfstate. תוכל למצוא את כתובות ה־IP של ה־Droplets וה־load balancer בתוך קובץ המצב הזה.

אך סידור של קובץ גדול יכול להיות לא נוח. CDKTF מספק את מבנה הנתונים TerraformOutput, שבאפשרותך להשתמש בו כדי לפלט משתנים ולהפוך אותם לזמינים מחוץ לערימה. כל הפלטים מודפסים ב־stdout לאחר הרצת cdktf deploy. ריצת cdktf output גם יכולה להדפיס פלטים בכל עת.

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

עדכן את קובץ main.ts כדי לכלול פלטים של כתובות ה-IP של מאזיני העומס וה-Droplets:

main.ts
import { Construct } from "constructs";
import { App, Fn, TerraformOutput, TerraformStack } from "cdktf";
import { DataDigitaloceanSshKey, DigitaloceanProvider, Droplet, Loadbalancer } from "./.gen/providers/digitalocean"

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    ...
    const lb = new Loadbalancer(this, 'lb', {
      ...
    })

    new TerraformOutput(this, "loadBalancerIP", {
      value: lb.ip,
    });

    droplets.forEach((droplet, index) => new TerraformOutput(this, `droplet${index}IP`, {
      value: droplet.ipv4Address
    }))
  }
}
...

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

הפעל את cdktf deploy כדי להפעיל את השינוי:

  1. cdktf deploy

בתוך הפלט, אתה צריך לראות משהו דומה למה שלמטה:

Output
───────────────────────────────────────────────────────────────────────────── Changes to Outputs: + droplet0IP = "droplet_foo_ip" + droplet1IP = "droplet_bar_ip" + loadBalancerIP = "load_balancer_ip" You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure. ─────────────────────────────────────────────────────────────────────────────

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

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

Output
infra droplet0IP = droplet_foo_ip droplet1IP = droplet_bar_ip loadBalancerIP = load_balancer_ip

כעת, בכל פעם שתריץ cdktf deploy או cdktf output, כתובת ה-IP של ה-Droplets והמאזינים לעומס מודפסת בפלט הטרמינל, וכך אין צורך לגשת אל המידע הזה מהקונסול של DigitalOcean.

עכשיו, אתה יכול להשתמש בשני Droplets ובמאזין העומס שיצרת ואישרת שהם פועלים. באפשרותך להשתמש בפרויקט CDKTF שפיתחת כבסיס להגדרת תשתיות מורכבות יותר (תוכל למצוא מימוש הפנייה בקישור הבא: do-community / digitalocean-cdktf-typescript).

שלב 9 — השמדת התשתיות שלך

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

עדיין בתיקיית infra/, הפעל cdktf destroy:

  1. cdktf destroy

תראה פלט דומה לזה:

Output
infra Initializing the backend... infra Initializing provider plugins... infra - Reusing previous version of digitalocean/digitalocean from the dependency lock file infra - Using previously-installed digitalocean/digitalocean v2.19.0 infra Terraform has been successfully initialized! infra digitalocean_droplet.bar (bar): Refreshing state... [id=298041598] digitalocean_droplet.foo (foo): Refreshing state... [id=298041600] infra digitalocean_loadbalancer.lb (lb): Refreshing state... [id=4f9ae2b7-b649-4fb4-beed-96b95bb72dd1] infra Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: infra # digitalocean_droplet.bar (bar) will be destroyed - resource "digitalocean_droplet" "bar" { - backups = false -> null - created_at = "2022-05-02T10:04:16Z" -> null - disk = 25 -> null - graceful_shutdown = false -> null - id = "298041598" -> null - image = "ubuntu-20-04-x64" -> null - ipv4_address = "droplet_bar_public_ip" -> null - ipv4_address_private = "droplet_bar_private_ip" -> null - ipv6 = false -> null - locked = false -> null - memory = 1024 -> null - monitoring = false -> null - name = "bar" -> null - price_hourly = 0.00744 -> null - price_monthly = 5 -> null - private_networking = true -> null - region = "lon1" -> null - resize_disk = true -> null - size = "s-1vcpu-1gb" -> null - ssh_keys = [ - "34377800", ] -> null - status = "active" -> null - tags = [] -> null - urn = "do:droplet:298041598" -> null - user_data = "f9b1d9796d069fe504ce0d89439b6b664b14b1a1" -> null - vcpus = 1 -> null - volume_ids = [] -> null - vpc_uuid = "bed80b32-dc82-11e8-83ec-3cfdfea9f3f0" -> null } # digitalocean_droplet.foo (foo) יושמד - resource "digitalocean_droplet" "foo" { - backups = false -> null - created_at = "2022-05-02T10:04:16Z" -> null - disk = 25 -> null - graceful_shutdown = false -> null - id = "298041600" -> null - image = "ubuntu-20-04-x64" -> null - ipv4_address = "droplet_foo_public_ip" -> null - ipv4_address_private = "droplet_foo_private_ip" -> null - ipv6 = false -> null - locked = false -> null - memory = 1024 -> null - monitoring = false -> null - name = "foo" -> null - price_hourly = 0.00744 -> null - price_monthly = 5 -> null - private_networking = true -> null - region = "lon1" -> null - resize_disk = true -> null - size = "s-1vcpu-1gb" -> null - ssh_keys = [ - "34377800", ] -> null - status = "active" -> null - tags = [] -> null - urn = "do:droplet:298041600" -> null - user_data = "f9b1d9796d069fe504ce0d89439b6b664b14b1a1" -> null - vcpus = 1 -> null - volume_ids = [] -> null - vpc_uuid = "bed80b32-dc82-11e8-83ec-3cfdfea9f3f0" -> null } # digitalocean_loadbalancer.lb (lb) יושמד - resource "digitalocean_loadbalancer" "lb" { - algorithm = "round_robin" -> null - disable_lets_encrypt_dns_records = false -> null - droplet_ids = [ - 298041598, - 298041600, ] -> null - enable_backend_keepalive = false -> null - enable_proxy_protocol = false -> null - id = "4f9ae2b7-b649-4fb4-beed-96b95bb72dd1" -> null - ip = "load_balancer_ip" -> null - name = "default" -> null - redirect_http_to_https = false -> null - region = "lon1" -> null - size_unit = 1 -> null - status = "active" -> null - urn = "do:loadbalancer:4f9ae2b7-b649-4fb4-beed-96b95bb72dd1" -> null - vpc_uuid = "bed80b32-dc82-11e8-83ec-3cfdfea9f3f0" -> null - forwarding_rule { - entry_port = 80 -> null - entry_protocol = "http" -> nul infra l - target_port = 80 -> null - target_protocol = "http" -> null - tls_passthrough = false -> null } - healthcheck { - check_interval_seconds = 10 -> null - healthy_threshold = 5 -> null - path = "/" -> null - port = 80 -> null - protocol = "http" -> null - response_timeout_seconds = 5 -> null - unhealthy_threshold = 3 -> null } - sticky_sessions { - cookie_ttl_seconds = 0 -> null - type = "none" -> null } } Plan: 0 to add, 0 to change, 3 to destroy. ───────────────────────────────────────────────────────────────────────────── Saved the plan to: plan To perform exactly these actions, run the following command to apply: terraform apply "plan" Please review the diff output above for infra ❯ Approve Applies the changes outlined in the plan. Dismiss Stop

הפעם, במקום להציג + ליד כל משאב, זה מציג -, מציין ש-CDKTF מתכוון להשמיד את המשאב. עיין בשינויים המוצעים, ואז השתמש במקשי החצים כדי לבחור לאשר וללחוץ על ENTER. ספק DigitalOcean יתקשר כעת עם API של DigitalOcean כדי להשמיד את המשאבים.

Output
infra digitalocean_loadbalancer.lb (lb): Destroying... [id=4f9ae2b7-b649-4fb4-beed-96b95bb72dd1] infra digitalocean_loadbalancer.lb (lb): Destruction complete after 1s infra digitalocean_droplet.bar (bar): Destroying... [id=298041598] digitalocean_droplet.foo (foo): Destroying... [id=298041600]

נמחק תחילה מאזן העומס כי אין לו תלותות (אין למשאבים אחרים התייחסות לאיזור העימות בקלטיהם). מכיוון שאיזור העימות מתייחס ל-Droplets, ניתן להשמיד אותם רק לאחר שמוחקים את איזור העימות.

לאחר השמדת המשאבים, תראה את השורה הבאה מודפסת בפלט:

Output
Destroy complete! Resources: 3 destroyed.

סיכום

במדריך זה, השתמשת ב-CDKTF כדי להגדיר ולהרוס עמוד אינטרנט עם טעינה מאוזנת, הכולל שני Droplets של DigitalOcean המפעילים שרתי NGINX, המופעלים מאחורי מאזן מעמד. גם פלטת מידע על המשאבים נדפסה בטרמינל.

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

תוכל גם לבדוק את התיעוד הרשמי של CDK for Terraform ואת המדריכים ללמידה נוספת על CDKTF.

Source:
https://www.digitalocean.com/community/tutorials/how-to-deploy-load-balanced-web-applications-on-digitalocean-with-cdk-for-terraform-and-typescript