פריסה ל-Compute Engine

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

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

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

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

  • מפעילים את ממשקי ה-API של Cloud Build,‏ Cloud Run,‏ Artifact Registry ומנהל המשאבים.

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

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

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

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

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

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

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

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

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

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

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

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

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

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

  • שתי קבוצות של מכונות וירטואליות ב-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. מפעילים את ממשקי ה-API של Cloud Build,‏ Cloud Run,‏ Artifact Registry ומנהל המשאבים:

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

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    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. מפעילים את ממשקי ה-API של Cloud Build,‏ Cloud Run,‏ Artifact Registry ומנהל המשאבים:

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

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    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 האחרון.

    מוצג הדף 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.

    בכרטיסייה History (היסטוריה) בחלק התחתון של הדף, יופיע קומיט אחד עם התיאור 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 שמתחיל את הפריסה.

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

    פתיחת Cloud Source Repositories

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

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

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

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

  13. פותחים את הדף פרטי הגרסה על ידי לחיצה על הגרסה הראשונה.

    יוצג צינור ההפעלה 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 שלב בנייה שקורא לפונקציה 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. קבצי ההגדרות של ה-build משתמשים בפקודה 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 מגדיר את האובייקטים שנוצרים עבור splitter 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 הכחול ול-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

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