הגדרת שירות Cloud Run לגשת לשירות Cloud Run פנימי וגם לאינטרנט ציבורי

1. מבוא

סקירה כללית

ארגונים רבים משתמשים ברשת של ענן וירטואלי פרטי (VPC) ב-Google Cloud עם אמצעי בקרה היקפיים כדי למנוע זליגת נתונים, וכך לאבטח את תנועה ברשת של השירותים והאפליקציות שלהם. רשת VPC היא גרסה וירטואלית של רשת פיזית שמוטמעת בתוך רשת הייצור של Google. רשת VPC מספקת קישוריות למכונות וירטואליות (VM) ב-Compute Engine, מציעה מאזני עומסים פנימיים של רשת להעברת סיגנל ללא שינוי ומערכות proxy למאזני עומסים פנימיים של אפליקציות, מתחברת לרשתות מקומיות באמצעות מנהרות Cloud VPN וחיבורי VLAN ל-Cloud Interconnect, ומפיצה תנועה ממאזני עומסים חיצוניים של Google Cloud למערכות בק-אנד.

בניגוד למכונות וירטואליות, שירותי Cloud Run לא משויכים לרשת VPC מסוימת כברירת מחדל. ב-Codelab הזה נדגים איך משנים את הגדרות תעבורת הנתונים הנכנסת (ingress) (חיבורים נכנסים) כך שרק תעבורה שמגיעה מ-VPC תוכל לגשת לשירות Cloud Run (למשל, שירות לקצה העורפי). בנוסף, ב-Codelab הזה נסביר איך לגרום לשירות שני (למשל, שירות frontend) לגשת לשירות backend של Cloud Run דרך VPC, וגם להמשיך לקבל גישה לאינטרנט הציבורי.

בדוגמה הזו, שירות הקצה העורפי של Cloud Run מחזיר את המחרוזת hello world. שירות ה-Cloud Run של הקצה הקדמי מספק שדה להזנת קלט בממשק המשתמש לאיסוף כתובת URL. לאחר מכן, שירות הקצה הקדמי מבצע בקשת GET לכתובת ה-URL הזו (למשל, שירות הקצה העורפי), ולכן זו בקשה משירות לשירות (במקום בקשה מדפדפן לשירות). כששירות הקצה הקדמי מצליח להגיע לקצה האחורי, ההודעה hello world מוצגת בדפדפן. לאחר מכן תראו איך אפשר להתקשר אל https://curlmyip.org כדי לאחזר את כתובת ה-IP של שירות הקצה הקדמי.

מה תלמדו

  • איך מאפשרים רק תעבורה מרשת VPC לשירות Cloud Run
  • איך מגדירים תעבורת נתונים יוצאת (egress) בשירות Cloud Run (למשל, קצה קדמי) כדי לתקשר עם שירות Cloud Run פנימי (למשל, בק-אנד) עם תעבורת נתונים נכנסת (ingress) פנימית בלבד, תוך שמירה על גישה לאינטרנט הציבורי לשירות הקצה הקדמי.

2. הגדרה ודרישות

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

הפעלת Cloud Shell

  1. ב-Cloud Console, לוחצים על Activate Cloud Shell d1264ca30785e435.png.

cb81e7c8e34bc8d.png

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

d95252b003979716.png

הקצאת המשאבים והחיבור ל-Cloud Shell נמשכים רק כמה רגעים.

7833d5e1c5d18f54.png

במכונה הווירטואלית הזו טעונים כל הכלים הדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את הביצועים והאימות של הרשת. אפשר לבצע את רוב העבודה ב-codelab הזה, אם לא את כולה, באמצעות דפדפן.

אחרי שמתחברים ל-Cloud Shell, אמור להופיע אימות ושהפרויקט מוגדר לפי מזהה הפרויקט.

  1. מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שעברתם אימות:
gcloud auth list

פלט הפקודה

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שפקודת gcloud מכירה את הפרויקט:
gcloud config list project

פלט הפקודה

[core]
project = <PROJECT_ID>

אם הוא לא מוגדר, אפשר להגדיר אותו באמצעות הפקודה הבאה:

gcloud config set project <PROJECT_ID>

פלט הפקודה

Updated property [core/project].

3. יצירת שירותי Cloud Run

הגדרה של משתני סביבה

אתם יכולים להגדיר משתני סביבה שישמשו אתכם לאורך כל ה-codelab הזה.

PROJECT_ID=<YOUR_PROJECT_ID>
REGION=<YOUR_REGION, e.g. us-central1>
FRONTEND=frontend-with-internet
BACKEND=backend
SUBNET_NAME=default

יצירת שירות הקצה העורפי ב-Cloud Run

קודם יוצרים ספרייה לקוד המקור ועוברים לספרייה הזו.

mkdir -p egress-private-codelab/frontend-w-internet egress-private-codelab/backend && cd egress-private-codelab/backend

לאחר מכן, יוצרים קובץ `package.json`` עם התוכן הבא:

{
    "name": "backend-service",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "node index.js"
    },
    "dependencies": {
        "express": "^4.18.1"
    }
}

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

const express = require('express');

const app = express();

app.use(express.urlencoded({ extended: true }));

app.get('/', function (req, res) {
    res.send("hello world");
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () => {
    console.log(`helloworld: listening on port ${port}`);
});

לבסוף, פורסים את שירות Cloud Run באמצעות הפקודה הבאה.

gcloud run deploy $BACKEND --source . --allow-unauthenticated --region $REGION

יצירת שירות הקצה הקדמי ב-Cloud Run

ניווט לספריית ה-frontend

cd ../frontend-w-internet

לאחר מכן, יוצרים קובץ package.json עם התוכן הבא:

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.6.6",
    "express": "^4.18.2",
    "htmx.org": "^1.9.10"
  }
}

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

const express = require("express");
const app = express();
const port = 8080;
const path = require('path');
const axios = require('axios');

// serve static content (index.html) using
// built-in middleware function in Express 
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));

// this endpoint receives a URL in the post body
// and then makes a get request to that URL
// results are sent back to the caller
app.post('/callService', async (req, res) => {

    const url = req.body.url;
    let message = "";

    try {
        console.log("url: ", url);
        const response = await axios.get(url);
        message = response.data;

    } catch (error) {
        message = error.message;
        console.error(error.message);
    }

    res.send(`
        ${message}
        <p>
        </p>
    `);
});

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
});

יצירת ספרייה ציבורית לקובץ index.html

mkdir public
touch public/index.html

מעדכנים את הפרמטר index.html כך שיכלול את הערכים הבאים:

<html>
  <script
    src="https://unpkg.com/htmx.org@1.9.10"
    integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
    crossorigin="anonymous"
  ></script>
  <body>
    <div style="margin-top: 100px; margin-left: 100px">
      <h1>I'm the Request Tester service on the Internet</h1>
      <form hx-trigger="submit" hx-post="/callService" hx-target="#zen">
        <label for="url"> URL:</label>
        <input
          style="width: 308px"
          type="text"
          id="url"
          name="url"
          placeholder="The backend service URL"
          required
        />
        <button hx-indicator="#loading" type="submit">Submit</button>
        <p></p>
        <span class="htmx-indicator" id="loading"> Loading... </span>
        <div id="zen" style="white-space: pre-wrap"></div>
        <p></p>
      </form>
    </div>
  </body>
</html>

לבסוף, פורסים את שירות Cloud Run באמצעות הפקודה הבאה.

gcloud run deploy $FRONTEND --source . --allow-unauthenticated --region $REGION

התקשרות לשירות לקצה העורפי

בקטע הזה תבדקו שפרסתם בהצלחה שני שירותים של Cloud Run.

פותחים את כתובת ה-URL של שירות ה-frontend בדפדפן האינטרנט, לדוגמה https://frontend-your-hash-uc.a.run.app/

בתיבת הטקסט, מזינים את כתובת ה-URL של שירות הבק-אנד. שימו לב שהבקשה הזו מנותבת ממופע הקצה הקדמי של Cloud Run לשירות הקצה העורפי של Cloud Run, ולא מהדפדפן שלכם.

יופיע הכיתוב hello world

4. הגדרת שירות לקצה העורפי רק לתעבורה פנימית

כדי לשלב שירות Cloud Run ברשת הפרטית, מריצים את פקודת gcloud הבאה.

gcloud run services update $BACKEND --ingress internal --region $REGION

אם תנסו להתקשר לשירות העורפי מהשירות החזיתי, תקבלו שגיאת 404. החיבור היוצא (או תעבורת נתונים יוצאת (egress)) של שירות Cloud Run בממשק הקצה הקדמי יוצא קודם לאינטרנט, ולכן Google Cloud לא יודעת מה המקור של הבקשה.

5. הגדרת שירות הקצה הקדמי לגישה ל-VPC

בקטע הזה תגדירו את שירות הקצה הקדמי של Cloud Run כדי שיוכל לתקשר עם שירות הקצה האחורי דרך VPC.

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

הגדרת תעבורת נתונים יוצאת (egress) ישירה מ-VPC

קודם מריצים את הפקודה הזו כדי להשתמש ביציאה ישירה של VPC בשירות הקצה הקדמי:

gcloud beta run services update $FRONTEND \
--network=$SUBNET_NAME \
--subnet=$SUBNET_NAME  \
--vpc-egress=private-ranges-only \
--region=$REGION

עכשיו אפשר לוודא שלשירות הקצה הקדמי יש גישה ל-VPC:

gcloud beta run services describe $FRONTEND \
--region=$REGION

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

VPC access:
    Network:        default
    Subnet:          default
    Egress:          private-ranges-only

הפעלת גישה פרטית ל-Google

בשלב הבא מפעילים גישה פרטית ל-Google ברשת המשנה על ידי הפעלת הפקודה הבאה:

gcloud compute networks subnets update $SUBNET_NAME \
--region=$REGION \
--enable-private-ip-google-access

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

gcloud compute networks subnets describe $SUBNET_NAME \
--region=$REGION \
--format="get(privateIpGoogleAccess)"

יצירת תחום DNS ב-Cloud DNS לכתובות URL של run.app

לבסוף, יוצרים תחום DNS של Cloud לכתובות URL של run.app כדי ש-Google Cloud יוכל להתייחס אליהן כאל כתובות IP פנימיות.

בשלב הקודם, כשמגדירים יציאה ישירה מ-VPC לטווחים פרטיים בלבד. המשמעות היא שחיבורים יוצאים משירות ה-frontend שלכם יועברו לרשת ה-VPC רק אם היעד הוא כתובת IP פנימית. עם זאת, שירות לקצה העורפי שלכם משתמש בכתובת URL מסוג run.app שמפנה לכתובת IP ציבורית.

בשלב הזה, יוצרים תחום DNS של Cloud DNS לכתובות ה-URL של run.app כדי לפתור את טווחי כתובות ה-IP של private.googleapis.com, שמזוהים ככתובות IP פנימיות. מעכשיו, כל הבקשות לטווחים האלה ינותבו דרך רשת ה-VPC.

אפשר לעשות את זה באמצעות: https://cloud.google.com/run/docs/securing/private-networking#from-other-services

# do not include the https:// in your DNS Name
# for example: backend-<hash>-uc.a.run.app
DNS_NAME=<your backend service URL without the https://>

gcloud dns --project=$PROJECT_ID managed-zones create codelab-backend-service \
 --description="" \
 --dns-name="a.run.app." \
 --visibility="private" \
 --networks=$SUBNET_NAME

gcloud dns --project=$PROJECT_ID record-sets create $DNS_NAME. \
--zone="codelab-backend-service" \
 --type="A" \
 --ttl="60" \
--rrdatas="199.36.153.8,199.36.153.9,199.36.153.10,199.36.153.11"

עכשיו, כשמנסים להגיע לשירות לקצה העורפי של האתר, מוצגת ההודעה hello world.

כשמנסים לגשת לאינטרנט באמצעות https://curlmyip.org/, כתובת ה-IP מוצגת.

6. פתרון בעיות

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

  • אם מופיעה השגיאה getaddrinfo ENOTFOUND backend-your-hash-uc.a.run.app, מוודאים שלא הוספתם את הקידומת https:// לרשומת ה-A של ה-DNS.
  • אם מופיעה שגיאת 404 כשמנסים לגשת לחלק האחורי של האתר אחרי הגדרת האזור, אפשר לחכות עד שתוקף המטמון ברשומה הגלובלית run.app יפוג (למשל, 6 שעות), או ליצור גרסה חדשה (ובכך לנקות את המטמון) על ידי הפעלת הפקודה הבאה: gcloud beta run services update $FRONTEND --network=$SUBNET_NAME --subnet=$SUBNET_NAME --vpc-egress=private-ranges-only --region=$REGION

7. מעולה!

כל הכבוד, סיימתם את ה-Codelab!

מומלץ לעיין במסמכי התיעוד בנושא רשת פרטית ב-Cloud Run.

מה נכלל

  • איך מאפשרים רק תעבורה מרשת VPC לשירות Cloud Run
  • איך מגדירים תעבורת נתונים יוצאת (egress) בשירות Cloud Run (למשל, קצה קדמי) כדי לתקשר עם שירות Cloud Run פנימי (למשל, בק-אנד) עם תעבורת נתונים נכנסת (ingress) פנימית בלבד, תוך שמירה על גישה לאינטרנט הציבורי לשירות הקצה הקדמי.

8. הסרת המשאבים

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

כדי למחוק את שירותי Cloud Run, נכנסים אל Cloud Run Cloud Console בכתובת https://console.cloud.google.com/functions/ ומוחקים את השירותים $FRONTEND ו-$BACKEND שיצרתם ב-codelab הזה.

אם אתם רוצים למחוק את הפרויקט כולו, אתם יכולים להיכנס לכתובת https://console.cloud.google.com/cloud-resource-manager, לבחור את הפרויקט שיצרתם בשלב 2 וללחוץ על 'מחיקה'. אם תמחקו את הפרויקט, תצטרכו לשנות את הפרויקטים ב-Cloud SDK. כדי לראות את רשימת כל הפרויקטים הזמינים, מריצים את הפקודה gcloud projects list.