פריסה ל-Compute Engine

במדריך הזה נסביר איך לבצע פריסות כחול/ירוק ללא השבתה בקבוצות של מכונות מנוהלות (MIG) ב-Compute Engine באמצעות Cloud Build ו-Terraform.

באמצעות Cloud Build אפשר להפוך לאוטומטיים מגוון תהליכים של מפתחים, כולל פיתוח ופריסה של אפליקציות בסביבות זמן ריצה שונות של Google Cloud , כמו Compute Engine,‏ Google Kubernetes Engine,‏ GKE Enterprise ופונקציות של Cloud Run.

קבוצות של מכונות מנוהלות (MIG) ב-Compute Engine מאפשרות להפעיל אפליקציות בכמה מכונות וירטואליות (VM) זהות. אתם יכולים להפוך את עומסי העבודה שלכם לניתנים להרחבה ולזמינים מאוד על ידי ניצול שירותים אוטומטיים של MIG, כולל: התאמה אוטומטית לעומס, תיקון תוכנה אוטומטי, פריסה אזורית (מספר אזורים) ועדכון אוטומטי. באמצעות מודל הפריסה הרציפה blue/green, תלמדו איך להעביר בהדרגה תנועת משתמשים מקבוצת מופעים מנוהלת אחת (blue) לקבוצת מופעים מנוהלת אחרת (green), כששתי הקבוצות פועלות בסביבת ייצור.

לפני שמתחילים

  • מפעילים את Cloud Build API,‏ Cloud Run API,‏ Artifact Registry API ו-Resource Manager API (מנהל המשאבים), אם אחד מהם עדיין לא מופעל.

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, נדרשת ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד 'בעלים' (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד 'אדמין בממשק Service Usage' (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    הפעלת ממשקי ה-API

  • חשוב להכין את קוד המקור של האפליקציה. קוד המקור צריך להיות מאוחסן במאגר כמו GitHub או Bitbucket.

  • כדי להריץ את הפקודות gcloud שבדף הזה, צריך להתקין את Google Cloud CLI.

הרשאות נדרשות לניהול זהויות והרשאות גישה (IAM)

  1. במסוף Google Cloud , נכנסים לדף Cloud Build Permissions:

    עוברים אל הרשאות

  2. בחשבון השירות שצוין ב-Cloud Build או בחשבון השירות שמוגדר כברירת מחדל ב-Cloud Build, מגדירים את הסטטוס של התפקידים הבאים למופעל:

    • ‫Compute Instance Admin v1 (roles/compute.instanceAdmin) | מאפשר ל-Cloud Build לפרוס מופעים חדשים ב-Compute Engine.
    • אדמין לניהול נפח האחסון (roles/storage.admin) | מאפשר קריאה וכתיבה מ-Cloud Storage.
    • Artifact Registry Writer (roles/artifactregistry.writer) | מאפשר שליפת תמונות מ-Artifact Registry וכתיבה ב-Artifact Registry.
    • כותב יומנים (roles/logging.logWriter) | מאפשר לכתוב רשומות ביומן ב-Cloud Logging.
    • עריכה ב-Cloud Build‏ (roles/cloudbuild.builds.editor) | מאפשר לחשבון השירות להריץ בנייה.

סקירה כללית של העיצוב

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

מודל כחול/ירוק

המודל הזה כולל את הרכיבים הבאים:

  • שתי קבוצות של מכונות וירטואליות ב-Compute Engine: כחולה וירוקה.
  • שלושה מאזני עומסים חיצוניים מסוג HTTP(S):
    • מאזן עומסים כחול-ירוק, שמנתב תעבורה ממשתמשי קצה למאגר הכחול או למאגר הירוק של מכונות וירטואליות.
    • מאזן עומסים כחול שמנתב תעבורה ממהנדסי QA ומפתחים למאגר המכונות הווירטואליות הכחול.
    • מאזן עומסים ירוק שמנתב תנועה ממהנדסי QA ומפתחים למאגר המופעים הירוק.
  • שתי קבוצות של משתמשים:
    • משתמשי קצה שיש להם גישה למאזן העומסים Blue-Green, שמפנה אותם למאגר המופעים Blue או למאגר המופעים Green.
    • מהנדסי QA ומפתחים שנדרשת להם גישה לשתי קבוצות המאגרים למטרות פיתוח ובדיקה. הם יכולים לגשת גם למאזן העומסים הכחול וגם למאזן העומסים הירוק, שמנתבים אותם למאגר המופעים הכחול ולמאגר המופעים הירוק בהתאמה.

מאגרי המכונות הווירטואליות הכחולות והירוקות מיושמים כקבוצות של מכונות מנוהלות (MIG) ב-Compute Engine, וכתובות IP חיצוניות מנותבות למכונות הווירטואליות בקבוצה באמצעות מאזני עומסים חיצוניים של HTTP(s). בדוגמת הקוד שמתוארת במסמך הזה נעשה שימוש ב-Terraform כדי להגדיר את התשתית הזו.

התרשים הבא ממחיש את פעולות הפיתוח שמתבצעות בפריסה:

רשימת השלבים למפתחים

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

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

סקריפט ההגדרה מפעיל צינור עיבוד נתונים של Cloud Build שמבצע את הפעולות הבאות:

הטריגר apply מצורף לקובץ Terraform בשם main.tfvars ב-Cloud Source Repositories. הקובץ הזה מכיל את משתני Terraform שמייצגים את מאזני העומסים הכחול והירוק.

כדי להגדיר את הפריסה, מעדכנים את המשתנים בקובץ main.tfvars. הטריגר apply מפעיל צינור Cloud Build שמבצע את tf_apply ואת הפעולות הבאות:

  • המערכת יוצרת שתי קבוצות של מכונות מנוהלות (MIG) ב-Compute Engine (אחת לירוק ואחת לכחול), ארבע מכונות וירטואליות ב-Compute Engine (שתיים לקבוצת ה-MIG הירוקה ושתיים לקבוצת ה-MIG הכחולה), שלושה מאזני עומסים (כחול, ירוק והמפצל) ושלוש כתובות IP ציבוריות.
  • הפקודה מדפיסה את כתובות ה-IP שבהן אפשר להשתמש כדי לראות את האפליקציות שפרסתם במופעים הכחולים והירוקים.

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

מטרות

  • שימוש ב-Cloud Build וב-Terraform כדי להגדיר מאזני עומסים חיצוניים מסוג HTTP(S) עם מערכות בק-אנד של קבוצות מופעי מכונה וירטואלית ב-Compute Engine.

  • מבצעים פריסות כחול-ירוק במכונות הווירטואליות.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

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

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

לפני שמתחילים

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. התקינו את ה-CLI של Google Cloud.

  3. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  4. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  5. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • Select a project: כדי לבחור פרויקט לא צריך תפקיד IAM ספציפי – אפשר לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד Project Creator (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים Google Cloud פרויקט:

      gcloud projects create PROJECT_ID

      מחליפים את PROJECT_ID בשם של פרויקט Google Cloud שיוצרים.

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

      מחליפים את PROJECT_ID בשם הפרויקט ב- Google Cloud .

  6. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  7. מפעילים את Cloud Build API,‏ Cloud Run API,‏ Artifact Registry API ו-Resource Manager API, אם אחד מהם עדיין לא מופעל:

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, נדרשת ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד 'בעלים' (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד 'אדמין בממשק Service Usage' (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    gcloud services enable cloudbuild.googleapis.com run.googleapis.com artifactregistry.googleapis.com cloudresourcemanager.googleapis.com
  8. התקינו את ה-CLI של Google Cloud.

  9. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  10. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  11. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • Select a project: כדי לבחור פרויקט לא צריך תפקיד IAM ספציפי – אפשר לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד Project Creator (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים Google Cloud פרויקט:

      gcloud projects create PROJECT_ID

      מחליפים את PROJECT_ID בשם של פרויקט Google Cloud שיוצרים.

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

      מחליפים את PROJECT_ID בשם הפרויקט ב- Google Cloud .

  12. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  13. מפעילים את Cloud Build API,‏ Cloud Run API,‏ Artifact Registry API ו-Resource Manager API, אם אחד מהם עדיין לא מופעל:

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, נדרשת ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד 'בעלים' (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד 'אדמין בממשק Service Usage' (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    gcloud services enable cloudbuild.googleapis.com run.googleapis.com artifactregistry.googleapis.com cloudresourcemanager.googleapis.com

אני רוצה לנסות

  1. מריצים את סקריפט ההגדרה ממאגר דוגמאות הקוד של Google:

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/setup.sh)
    
  2. כשסקריפט ההגדרה מבקש הסכמה מהמשתמש, מזינים yes.

    הסקריפט יסיים את הריצה תוך כמה שניות.

  3. במסוף Google Cloud , פותחים את הדף Build history ב-Cloud Build:

    פתיחת דף היסטוריית ה-Build

  4. לוחצים על הגרסה העדכנית ביותר.

    מוצג הדף Build details, שבו מוצג צינור של Cloud Build עם שלושה שלבי build: בשלב הראשון נוצר מאגר ב-Cloud Source Repositories, בשלב השני משובטים התכנים של מאגר הדוגמה ב-GitHub ל-Cloud Source Repositories, ובשלב השלישי מתווספים שני טריגרים של build.

  5. פותחים את Cloud Source Repositories:

    פתיחת Cloud Source Repositories

  6. ברשימת המאגרים, לוחצים על copy-of-gcp-mig-simple.

    בכרטיסייה היסטוריה בחלק התחתון של הדף, יופיע קומיט אחד עם התיאור A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git שנוצר על ידי Cloud Build כדי ליצור מאגר בשם copy-of-gcp-mig-simple.

  7. פותחים את הדף Triggers (טריגרים) של Cloud Build:

    פתיחת הדף Triggers

  8. יופיעו שני טריגרים לבנייה בשמות apply ו-destroy. הטריגר apply מצורף לקובץ infra/main.tfvars בענף main. הטריגר הזה מופעל בכל פעם שהקובץ מתעדכן. הטריגר destroy הוא טריגר ידני.

  9. כדי להתחיל בתהליך הפריסה, מעדכנים את הקובץ infra/main.tfvars:

    1. בחלון הטרמינל, יוצרים תיקייה בשם deploy-compute-engine ועוברים אליה:

      mkdir ~/deploy-compute-engine
      cd ~/deploy-compute-engine
      
    2. משכפלים את מאגר copy-of-gcp-mig-simple:

      gcloud source repos clone copy-of-mig-blue-green
      
    3. עוברים לספרייה המשוכפלת:

      cd ./copy-of-mig-blue-green
      
    4. מעדכנים את infra/main.tfvars כדי להחליף את הכחול בירוק:

      sed -i'' -e 's/blue/green/g' infra/main.tfvars
      
    5. מוסיפים את הקובץ המעודכן:

      git add .
      
    6. מבצעים Commit לקובץ:

      git commit -m "Promote green"
      
    7. דחיפת הקובץ:

      git push
      

      ביצוע שינויים ב-infra/main.tfvars מפעיל את הביצוע של apply trigger, שמתחיל את הפריסה.

  10. פותחים את Cloud Source Repositories:

    פתיחת Cloud Source Repositories

  11. ברשימת המאגרים, לוחצים על copy-of-gcp-mig-simple.

    ההתחייבות תופיע עם התיאור Promote green בכרטיסייה היסטוריה בתחתית הדף.

  12. כדי לראות את ההפעלה של הטריגר apply, פותחים את הדף Build history במסוף Google Cloud :

    פתיחת דף היסטוריית ה-Build

  13. לוחצים על ה-build הראשון כדי לפתוח את הדף Build details (פרטי ה-build).

    תראו את צינור ההפעלה apply עם שני שלבי בנייה. בשלב הראשון של הבנייה מופעלת הפקודה Terraform apply כדי ליצור את משאבי Compute Engine ואיזון העומסים לפריסה. בשלב השני של הבנייה, כתובת ה-IP שבה האפליקציה פועלת מודפסת.

  14. פותחים בדפדפן את כתובת ה-IP שמתאימה ל-MIG הירוק. יוצג צילום מסך דומה לזה שבהמשך, שבו מוצגת הפריסה:

    פריסה

  15. עוברים לדף Instance group של Compute Engine כדי לראות את קבוצות המופעים הכחולה והירוקה:

    פתיחת הדף Instance group

  16. פותחים את הדף VM instances כדי לראות את ארבע מכונות ה-VM:

    פתיחת הדף VM Instance

  17. פותחים את הדף External IP addresses כדי לראות את שלושת מאזני העומסים:

    פתיחת הדף כתובות IP חיצוניות

הסבר על הקוד

קוד המקור של דוגמת הקוד הזו כולל:

  • קוד המקור שקשור לסקריפט ההגדרה.
  • קוד מקור שקשור לצינורות Cloud Build.
  • קוד המקור שקשור לתבניות Terraform.

סקריפט הגדרות

‫setup.sh הוא סקריפט ההגדרה שמריץ את תהליך האתחול ויוצר את הרכיבים לפריסה כחול-ירוק. הסקריפט מבצע את הפעולות הבאות:

  • ההרשאה מאפשרת להשתמש בממשקי Cloud Build,‏ מנהל המשאבים,‏ Compute Engine ו-Cloud Source Repositories API.
  • מקצה את תפקיד ה-IAM‏ roles/editor לחשבון השירות של Cloud Build בפרויקט. התפקיד הזה נדרש כדי ש-Cloud Build יוכל ליצור ולהגדיר את רכיבי ה-GitOps הנדרשים לפריסה.
  • מקצה את תפקיד ה-IAM‏ roles/source.admin לחשבון השירות של Cloud Build בפרויקט. התפקיד הזה נדרש כדי שחשבון השירות של Cloud Build יוכל ליצור את Cloud Source Repositories בפרויקט שלכם ולשכפל את התוכן של מאגר הדוגמה ב-GitHub אל Cloud Source Repositories.
  • יוצר צינור Cloud Build בשם bootstrap.cloudbuild.yaml inline, שכולל את הפעולות הבאות:

    • יצירת מאגר חדש ב-Cloud Source Repositories.
    • הקוד מעתיק את קוד המקור ממאגר הנתונים לדוגמה של GitHub למאגר החדש ב-Cloud Source Repositories.
    • יוצר את טריגרים הבנייה apply ו-destroy.
set -e

BLUE='\033[1;34m'
RED='\033[1;31m'
GREEN='\033[1;32m'
NC='\033[0m'

echo -e "\n${GREEN}######################################################"
echo -e "#                                                    #"
echo -e "#  Zero-Downtime Blue/Green VM Deployments Using     #"
echo -e "#  Managed Instance Groups, Cloud Build & Terraform  #"
echo -e "#                                                    #"
echo -e "######################################################${NC}\n"

echo -e "\nSTARTED ${GREEN}setup.sh:${NC}"

echo -e "\nIt's ${RED}safe to re-run${NC} this script to ${RED}recreate${NC} all resources.\n"
echo "> Checking GCP CLI tool is installed"
gcloud --version > /dev/null 2>&1

readonly EXPLICIT_PROJECT_ID="$1"
readonly EXPLICIT_CONSENT="$2"

if [ -z "$EXPLICIT_PROJECT_ID" ]; then
    echo "> No explicit project id provided, trying to infer"
    PROJECT_ID="$(gcloud config get-value project)"
else
    PROJECT_ID="$EXPLICIT_PROJECT_ID"
fi

if [ -z "$PROJECT_ID" ]; then
    echo "ERROR: GCP project id was not provided as parameter and could not be inferred"
    exit 1
else
    readonly PROJECT_NUM="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
    if [ -z "$PROJECT_NUM" ]; then
        echo "ERROR: GCP project number could not be determined"
        exit 1
    fi
    echo -e "\nYou are about to:"
    echo -e "  * modify project ${RED}${PROJECT_ID}/${PROJECT_NUM}${NC}"
    echo -e "  * ${RED}enable${NC} various GCP APIs"
    echo -e "  * make Cloud Build ${RED}editor${NC} of your project"
    echo -e "  * ${RED}execute${NC} Cloud Builds and Terraform plans to create"
    echo -e "  * ${RED}4 VMs${NC}, ${RED}3 load balancers${NC}, ${RED}3 public IP addresses${NC}"
    echo -e "  * incur ${RED}charges${NC} in your billing account as a result\n"
fi

if [ "$EXPLICIT_CONSENT" == "yes" ]; then
  echo "Proceeding under explicit consent"
  readonly CONSENT="$EXPLICIT_CONSENT"
else
    echo -e "Enter ${BLUE}'yes'${NC} if you want to proceed:"
    read CONSENT
fi

if [ "$CONSENT" != "yes" ]; then
    echo -e "\nERROR: Aborted by user"
    exit 1
else
    echo -e "\n......................................................"
    echo -e "\n> Received user consent"
fi

#
# Executes action with one randomly delayed retry.
#
function do_with_retry {
    COMMAND="$@"
    echo "Trying $COMMAND"
    (eval $COMMAND && echo "Success on first try") || ( \
        echo "Waiting few seconds to retry" &&
        sleep 10 && \
        echo "Retrying $COMMAND" && \
        eval $COMMAND \
    )
}

echo "> Enabling required APIs"
# Some of these can be enabled later with Terraform, but I personally
# prefer to do all API enablement in one place with gcloud.
gcloud services enable \
    --project=$PROJECT_ID \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    compute.googleapis.com \
    sourcerepo.googleapis.com \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/editor"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --role='roles/editor' \
    --condition=None \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/source.admin"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --condition=None \
    --role='roles/source.admin' \
    --no-user-output-enabled \
    --quiet

echo "> Configuring bootstrap job"
rm -rf "./bootstrap.cloudbuild.yaml"
cat <<'EOT_BOOT' > "./bootstrap.cloudbuild.yaml"
tags:
- "mig-blue-green-bootstrapping"
steps:
- id: create_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating source code repository"

    gcloud source repos delete \
        "copy-of-mig-blue-green" \
        --quiet || true

    gcloud source repos create \
        "copy-of-mig-blue-green" \
        --quiet

- id: copy_demo_source_into_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
    - "PROJECT_NUMBER=$PROJECT_NUMBER"
  script: |
    #!/bin/bash
    set -e

    readonly GIT_REPO="https://github.com/GoogleCloudPlatform/cloud-build-samples.git"

    echo "Cloning demo source repo"
    mkdir /workspace/from/
    cd /workspace/from/
    git clone $GIT_REPO ./original
    cd ./original

    echo "Cloning new empty repo"
    mkdir /workspace/to/
    cd /workspace/to/
    gcloud source repos clone \
        "copy-of-mig-blue-green"
    cd ./copy-of-mig-blue-green

    echo "Making a copy"
    cp -r /workspace/from/original/mig-blue-green/* ./

    echo "Setting git identity"
    git config user.email \
        "$PROJECT_NUMBER@cloudbuild.gserviceaccount.com"
    git config user.name \
        "Cloud Build"

    echo "Commit & push"
    git add .
    git commit \
        -m "A copy of $GIT_REPO"
    git push

- id: add_pipeline_triggers
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating destroy trigger"
    gcloud builds triggers delete "destroy" --quiet || true
    gcloud builds triggers create manual \
        --name="destroy" \
        --repo="https://source.developers.google.com/p/$PROJECT_ID/r/copy-of-mig-blue-green" \
        --branch="master" \
        --build-config="pipelines/destroy.cloudbuild.yaml" \
        --repo-type=CLOUD_SOURCE_REPOSITORIES \
        --quiet

    echo "(Re)Creating apply trigger"
    gcloud builds triggers delete "apply" --quiet || true
    gcloud builds triggers create cloud-source-repositories \
        --name="apply" \
        --repo="copy-of-mig-blue-green" \
        --branch-pattern="master" \
        --build-config="pipelines/apply.cloudbuild.yaml" \
        --included-files="infra/main.tfvars" \
        --quiet

EOT_BOOT

echo "> Waiting API enablement propagation"
do_with_retry "(gcloud builds list --project "$PROJECT_ID" --quiet && gcloud compute instances list --project "$PROJECT_ID" --quiet && gcloud source repos list --project "$PROJECT_ID" --quiet) > /dev/null 2>&1" > /dev/null 2>&1

echo "> Executing bootstrap job"
gcloud beta builds submit \
    --project "$PROJECT_ID" \
    --config ./bootstrap.cloudbuild.yaml \
    --no-source \
    --no-user-output-enabled \
    --quiet
rm ./bootstrap.cloudbuild.yaml

echo -e "\n${GREEN}All done. Now you can:${NC}"
echo -e "  * manually run 'apply' and 'destroy' triggers to manage deployment lifecycle"
echo -e "  * commit change to 'infra/main.tfvars' and see 'apply' pipeline trigger automatically"

echo -e "\n${GREEN}Few key links:${NC}"
echo -e "  * Dashboard: https://console.cloud.google.com/home/dashboard?project=$PROJECT_ID"
echo -e "  * Repo: https://source.cloud.google.com/$PROJECT_ID/copy-of-mig-blue-green"
echo -e "  * Cloud Build Triggers: https://console.cloud.google.com/cloud-build/triggers;region=global?project=$PROJECT_ID"
echo -e "  * Cloud Build History: https://console.cloud.google.com/cloud-build/builds?project=$PROJECT_ID"

echo -e "\n............................."

echo -e "\n${GREEN}COMPLETED!${NC}"

צינורות עיבוד נתונים של Cloud Build

‫apply.cloudbuild.yaml ו-destroy.cloudbuild.yaml הם קובצי ההגדרות של Cloud Build שבהם סקריפט ההגדרה משתמש כדי להגדיר את המשאבים לתהליך GitOps. ‫apply.cloudbuild.yaml מכיל שני שלבי בנייה:

  • tf_apply build שלב build שקורא לפונקציה tf_install_in_cloud_build_step, שמתקינה את Terraform. tf_apply שיוצר את המשאבים שמשמשים בתהליך העבודה של GitOps. הפונקציות tf_install_in_cloud_build_step ו-tf_apply מוגדרות ב-bash_utils.sh, ובשלב הבנייה נעשה שימוש בפקודה source כדי להפעיל אותן.
  • describe_deployment שלב build שקורא לפונקציה describe_deployment שמדפיסה את כתובות ה-IP של מאזני העומסים.

‫destroy.cloudbuild.yaml calls tf_destroy שמוחקת את כל המשאבים שנוצרו על ידי tf_apply.

הפונקציות tf_install_in_cloud_build_step,‏ tf_apply,‏ describe_deployment ו-tf_destroy מוגדרות בקובץ bash_utils.sh. קבצי הגדרות הבנייה משתמשים בפקודה source כדי לקרוא לפונקציות.

steps:
  - id: run-terraform-apply
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_apply

  - id: describe-deployment
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      describe_deployment

tags:
  - "mig-blue-green-apply"
steps:
  - id: run-terraform-destroy
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_destroy

tags:
  - "mig-blue-green-destroy"

הקוד הבא מציג את הפונקציה tf_install_in_cloud_build_step שמוגדרת ב-bash_utils.sh. קובצי התצורה של ה-build קוראים לפונקציה הזו כדי להתקין את Terraform תוך כדי תנועה. הוא יוצר קטגוריה של Cloud Storage כדי לתעד את הסטטוס של Terraform.

function tf_install_in_cloud_build_step {
    echo "Installing deps"
    apt update
    apt install \
        unzip \
        wget \
        -y

    echo "Manually installing Terraform"
    wget https://releases.hashicorp.com/terraform/1.3.4/terraform_1.3.4_linux_386.zip
    unzip -q terraform_1.3.4_linux_386.zip
    mv ./terraform /usr/bin/
    rm -rf terraform_1.3.4_linux_386.zip

    echo "Verifying installation"
    terraform -v

    echo "Creating Terraform state storage bucket $BUCKET_NAME"
    gcloud storage buckets create \
        "gs://$BUCKET_NAME" || echo "Already exists..."

    echo "Configure Terraform provider and state bucket"
cat <<EOT_PROVIDER_TF > "/workspace/infra/provider.tf"
terraform {
  required_version = ">= 0.13"
  backend "gcs" {
    bucket = "$BUCKET_NAME"
  }
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 3.77, < 5.0"
    }
  }
}
EOT_PROVIDER_TF

    echo "$(cat /workspace/infra/provider.tf)"
}

בקטע הקוד הבא מוצגת הפונקציה tf_apply שמוגדרת ב-bash_utils.sh. קודם כל מופעלת הפונקציה terraform init שמעמיסה את כל המודולים ואת הספריות המותאמות אישית, ואז מופעלת הפונקציה terraform apply כדי להעמיס את המשתנים מהקובץ main.tfvars.

function tf_apply {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform apply"
    terraform \
        -chdir="$TF_CHDIR" \
        apply \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

בקטע הקוד הבא מוצגת הפונקציה describe_deployment שמוגדרת ב-bash_utils.sh. הוא משתמש ב-gcloud compute addresses describe כדי לאחזר את כתובות ה-IP של מאזני העומסים באמצעות השם שלהם, ומדפיס אותן.

function describe_deployment {
    NS="ns1-"
    echo -e "Deployment configuration:\n$(cat infra/main.tfvars)"
    echo -e \
      "Here is how to connect to:" \
      "\n\t* active color MIG: http://$(gcloud compute addresses describe ${NS}splitter-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* blue color MIG: http://$(gcloud compute addresses describe ${NS}blue-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* green color MIG: http://$(gcloud compute addresses describe ${NS}green-address-name --region=us-west1 --format='value(address)')/"
    echo "Good luck!"
}

בקטע הקוד הבא מוצגת הפונקציה tf_destroy שמוגדרת ב-bash_utils.sh. היא מפעילה את terraform init שמעמיס את כל המודולים והספריות המותאמות אישית, ואז מפעילה את terraform destroy שמבטל את הטעינה של משתני Terraform.

function tf_destroy {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform destroy"
    terraform \
        -chdir="$TF_CHDIR" \
        destroy \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

תבניות Terraform

כל קובצי התצורה והמשתנים של Terraform נמצאים בתיקייה copy-of-gcp-mig-simple/infra/.

  • ‫main.tf: קובץ ההגדרות של Terraform
  • ‫main.tfvars: בקובץ הזה מוגדרים משתני Terraform.
  • ‫mig/ ו-splitter/: התיקיות האלה מכילות את המודולים שמגדירים את מאזני העומסים. התיקייה mig/ מכילה את קובץ התצורה של Terraform שמגדיר את ה-MIG למאזני העומסים Blue ו-Green. ה-MIG הכחול וה-MIG הירוק זהים, ולכן הם מוגדרים פעם אחת ומופעלים עבור האובייקטים הכחולים והירוקים. קובץ התצורה של Terraform למאזן העומסים של המפצל נמצא בתיקייה splitter/ .

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

MIG_VER_BLUE     = "v1"
MIG_VER_GREEN    = "v1"
MIG_ACTIVE_COLOR = "blue"

קטע הקוד הבא הוא מתוך infra/main.tf. בדוגמה הזו:

  • משתנה מוגדר עבור הפרויקט Google Cloud .
  • ‫Google מוגדרת כספק Terraform.
  • משתנה מוגדר למרחב שמות. כל האובייקטים שנוצרו על ידי Terraform מקבלים את הקידומת של המשתנה הזה, כדי שאפשר יהיה לפרוס כמה גרסאות של האפליקציה באותו פרויקט וששמות האובייקטים לא יתנגשו זה עם זה.
  • המשתנים MIG_VER_BLUE, MIG_VER_BLUE ו-MIG_ACTIVE_COLOR הם הקישורים למשתנים בקובץ infra/main.tfvars.
variable "project" {
  type        = string
  description = "GCP project we are working in."
}

provider "google" {
  project = var.project
  region  = "us-west1"
  zone    = "us-west1-a"
}

variable "ns" {
  type        = string
  default     = "ns1-"
  description = "The namespace used for all resources in this plan."
}

variable "MIG_VER_BLUE" {
  type        = string
  description = "Version tag for 'blue' deployment."
}

variable "MIG_VER_GREEN" {
  type        = string
  description = "Version tag for 'green' deployment."
}

variable "MIG_ACTIVE_COLOR" {
  type        = string
  description = "Active color (blue | green)."
}

בקטע הקוד הבא מתוך infra/main.tf מוצגת יצירת מופע של מודול הפיצול. המודול הזה מקבל את הצבע הפעיל כדי שמאזן העומסים של המפצל יידע באיזה MIG לפרוס את האפליקציה.

module "splitter-lb" {
  source               = "./splitter"
  project              = var.project
  ns                   = "${var.ns}splitter-"
  active_color         = var.MIG_ACTIVE_COLOR
  instance_group_blue  = module.blue.google_compute_instance_group_manager_default.instance_group
  instance_group_green = module.green.google_compute_instance_group_manager_default.instance_group
}

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

module "blue" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_BLUE
  ns                                   = var.ns
  color                                = "blue"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

module "green" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_GREEN
  ns                                   = var.ns
  color                                = "green"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

הקובץ splitter/main.tf מגדיר את האובייקטים שנוצרים עבור קבוצת ה-MIG של המפצל. קטע הקוד הבא מ-splitter/main.tf מכיל את הלוגיקה למעבר בין קבוצת ה-MIG הירוקה לקבוצת ה-MIG הכחולה. הוא נתמך על ידי השירות google_compute_region_backend_service, שיכול לנתב תעבורה לשני אזורי קצה עורפיים: var.instance_group_blue או var.instance_group_green. הערך capacity_scaler מגדיר כמה מהתנועה תנותב.

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

resource "google_compute_region_backend_service" "default" {
  name                  = local.l7-xlb-backend-service
  region                = "us-west1"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  health_checks         = [google_compute_region_health_check.default.id]
  protocol              = "HTTP"
  session_affinity      = "NONE"
  timeout_sec           = 30
  backend {
    group           = var.instance_group_blue
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "blue" ? 1 : 0
  }
  backend {
    group           = var.instance_group_green
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "green" ? 1 : 0
  }
}

הקובץ mig/main.tf מגדיר את האובייקטים שקשורים לקבוצות ה-MIG הכחולה והירוקה. בקטע הקוד הבא מהקובץ הזה מוגדרת תבנית של הגדרות מכונה של Compute Engine שמשמשת ליצירת מאגרי מכונות וירטואליות. שימו לב שבתבנית המופע הזו, מאפיין מחזור החיים של Terraform מוגדר ל-create_before_destroy. הסיבה לכך היא שכאשר מעדכנים את הגרסה של המאגר, אי אפשר להשתמש בתבנית כדי ליצור את הגרסה החדשה של המאגר אם עדיין נעשה בה שימוש בגרסה הקודמת של המאגר. אבל אם הגרסה הישנה יותר של המאגר תושמד לפני יצירת התבנית החדשה, יהיה פרק זמן שבו המאגרים לא יפעלו. כדי להימנע מהתרחיש הזה, הגדרנו את מחזור החיים של Terraform ל-create_before_destroy כדי שהגרסה החדשה יותר של מאגר המכונות הווירטואליות תיצור קודם לפני שהגרסה הישנה יותר תושמד.

resource "google_compute_instance_template" "default" {
  name = local.l7-xlb-backend-template
  disk {
    auto_delete  = true
    boot         = true
    device_name  = "persistent-disk-0"
    mode         = "READ_WRITE"
    source_image = "projects/debian-cloud/global/images/family/debian-10"
    type         = "PERSISTENT"
  }
  labels = {
    managed-by-cnrm = "true"
  }
  machine_type = "n1-standard-1"
  metadata = {
    startup-script = <<EOF
    #! /bin/bash
    sudo apt-get update
    sudo apt-get install apache2 -y
    sudo a2ensite default-ssl
    sudo a2enmod ssl
    vm_hostname="$(curl -H "Metadata-Flavor:Google" \
    http://169.254.169.254/computeMetadata/v1/instance/name)"
    sudo echo "<html><body style='font-family: Arial; margin: 64px; background-color: light${var.color};'><h3>Hello, World!<br><br>version: ${var.app_version}<br>ns: ${var.ns}<br>hostname: $vm_hostname</h3></body></html>" | \
    tee /var/www/html/index.html
    sudo systemctl restart apache2
    EOF
  }
  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }
    network    = var.google_compute_network.id
    subnetwork = var.google_compute_subnetwork.id
  }
  region = "us-west1"
  scheduling {
    automatic_restart   = true
    on_host_maintenance = "MIGRATE"
    provisioning_model  = "STANDARD"
  }
  tags = ["load-balanced-backend"]

  # NOTE: the name of this resource must be unique for every update;
  #       this is wy we have a app_version in the name; this way
  #       new resource has a different name vs old one and both can
  #       exists at the same time
  lifecycle {
    create_before_destroy = true
  }
}

הסרת המשאבים

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

מחיקת משאבים בודדים

  1. מוחקים את המשאבים של Compute Engine שנוצרו על ידי טריגר ההפעלה:

    1. פותחים את הדף Triggers (טריגרים) של Cloud Build:

      פתיחת הדף Triggers

    2. בטבלה Triggers (טריגרים), מאתרים את השורה שמתאימה לטריגר destroy (השמדה) ולוחצים על Run (הפעלה). כשההפעלה של הטריגר מסתיימת, המשאבים שנוצרו על ידי הטריגר apply נמחקים.

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

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/teardown.sh)
    

מחיקת הפרויקט

    כדי למחוק Google Cloud פרויקט:

    gcloud projects delete PROJECT_ID

המאמרים הבאים