1. مقدمة
نظرة عامة
في هذا الدرس التطبيقي، ستتم تهيئة Cloud Run على إنشاء إصدارات جديدة ونشرها تلقائيًا من تطبيقك عند إرسال تغييرات رمز المصدر إلى مستودع GitHub.
يحفظ هذا التطبيق التجريبي بيانات المستخدم في firestore، ولكن يتم حفظ جزء جزئي فقط من البيانات بشكل صحيح. وستقوم بتهيئة عمليات النشر المستمرة بحيث يصبح الإصلاح متاحًا في نسخة جديدة عند إرسال إصلاح خطأ إلى مستودع جيت هب.
المعلومات التي ستطّلع عليها
- كتابة تطبيق ويب Express باستخدام "محرِّر Cloud Shell"
- ربط حسابك على GitHub بخدمة Google Cloud لإجراء عمليات النشر المستمرة
- نشر تطبيقك تلقائيًا في "التشغيل في السحابة الإلكترونية"
- تعرَّف على طريقة استخدام HTMX وTailwindCSS.
2. الإعداد والمتطلبات
المتطلبات الأساسية
- لديك حساب على GitHub وأنت على دراية بإنشاء التعليمات البرمجية ودفعها إلى المستودعات.
- تسجيل الدخول إلى Cloud Console
- سبق لك نشر خدمة Cloud Run. على سبيل المثال، يمكنك متابعة نشر خدمة ويب من خلال التشغيل السريع لرمز المصدر للبدء.
تفعيل Cloud Shell
- من Cloud Console، انقر على تفعيل Cloud Shell .
إذا كانت هذه هي المرة الأولى التي تبدأ فيها Cloud Shell، ستظهر لك شاشة وسيطة تصف ماهيتها. إذا ظهرت لك شاشة وسيطة، انقر على متابعة.
من المفترَض أن تستغرق عملية إدارة الحسابات والاتصال بخدمة Cloud Shell بضع دقائق فقط.
يتم تحميل هذا الجهاز الافتراضي مع جميع أدوات التطوير اللازمة. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتم تشغيله في Google Cloud، ما يحسّن بشكل كبير من أداء الشبكة والمصادقة. يمكنك تنفيذ معظم عملك، إن لم يكن كلّه، في هذا الدرس التطبيقي حول الترميز باستخدام متصفّح.
بعد الربط بخدمة Cloud Shell، من المفترض أن تتأكّد من أنّه تمّت مصادقتك وأنّ المشروع مضبوط على رقم تعريف مشروعك.
- شغِّل الأمر التالي في 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`
- شغّل الأمر التالي في Cloud Shell للتأكد من معرفة الأمر gcloud بمشروعك:
gcloud config list project
مخرجات الأمر
[core] project = <PROJECT_ID>
إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:
gcloud config set project <PROJECT_ID>
مخرجات الأمر
Updated property [core/project].
3- تفعيل واجهات برمجة التطبيقات وضبط متغيرات البيئة
تفعيل واجهات برمجة التطبيقات
يتطلّب هذا الدرس التطبيقي حول الترميز استخدام واجهات برمجة التطبيقات التالية. يمكنك تمكين واجهات برمجة التطبيقات هذه عن طريق تشغيل الأمر التالي:
gcloud services enable run.googleapis.com \ cloudbuild.googleapis.com \ firestore.googleapis.com \ iamcredentials.googleapis.com
إعداد متغيرات البيئة
يمكنك ضبط متغيّرات البيئة التي سيتم استخدامها خلال هذا الدرس التطبيقي حول الترميز.
REGION=<YOUR-REGION> PROJECT_ID=<YOUR-PROJECT-ID> PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') SERVICE_ACCOUNT="firestore-accessor" SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
4. إنشاء حساب خدمة
ستستخدم Cloud Run حساب الخدمة هذا لطلب بيانات Vertex AI Gemini API. سيحصل حساب الخدمة هذا أيضًا على أذونات للقراءة والكتابة في Firestore وقراءة الأسرار من Secret Manager.
أولاً، أنشئ حساب الخدمة من خلال تنفيذ الأمر التالي:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Cloud Run access to Firestore"
والآن، امنح حساب الخدمة إذنًا بالقراءة والكتابة في Firestore.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/datastore.user
5- إنشاء مشروع على Firebase وإعداده
- في وحدة تحكُّم Firebase، انقر على إضافة مشروع.
- أدخِل <YOUR_PROJECT_ID>. لإضافة Firebase إلى أحد مشاريعك الحالية على Google Cloud
- راجِع بنود Firebase واقبلها إذا طُلب منك ذلك.
- انقر على متابعة.
- انقر على تأكيد الخطة لتأكيد خطة فوترة Firebase.
- يُعدّ تفعيل "إحصاءات Google" اختياريًا لهذا الدرس التطبيقي حول الترميز.
- انقر على إضافة Firebase.
- عند إنشاء المشروع، انقر على متابعة.
- من القائمة إنشاء، انقر على قاعدة بيانات Firestore.
- انقر على إنشاء قاعدة بيانات.
- اختر منطقتك من القائمة المنسدلة الموقع الجغرافي، ثم انقر على التالي.
- استخدِم الإعداد التلقائي البدء في وضع الإنتاج، ثم انقر على إنشاء.
6- كتابة بيانات الطلب
أولاً، أنشئ دليلاً لرمز المصدر والقرص المضغوط في هذا الدليل.
mkdir cloud-run-github-cd-demo && cd $_
بعد ذلك، أنشِئ ملف package.json
يتضمّن المحتوى التالي:
{ "name": "cloud-run-github-cd-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node app.js", "nodemon": "nodemon app.js", "tailwind-dev": "npx tailwindcss -i ./input.css -o ./public/output.css --watch", "tailwind": "npx tailwindcss -i ./input.css -o ./public/output.css", "dev": "npm run tailwind && npm run nodemon" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@google-cloud/firestore": "^7.3.1", "axios": "^1.6.7", "express": "^4.18.2", "htmx.org": "^1.9.10" }, "devDependencies": { "nodemon": "^3.1.0", "tailwindcss": "^3.4.1" } }
أولاً، أنشِئ ملف مصدر app.js
يتضمّن المحتوى أدناه. يحتوي هذا الملف على نقطة دخول الخدمة ويحتوي على المنطق الرئيسي للتطبيق.
const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); const path = require("path"); const { get } = require("axios"); const { Firestore } = require("@google-cloud/firestore"); const firestoreDb = new Firestore(); const fs = require("fs"); const util = require("util"); const { spinnerSvg } = require("./spinnerSvg.js"); const service = process.env.K_SERVICE; const revision = process.env.K_REVISION; app.use(express.static("public")); app.get("/edit", async (req, res) => { res.send(`<form hx-post="/update" hx-target="this" hx-swap="outerHTML"> <div> <p> <label>Name</label> <input class="border-2" type="text" name="name" value="Cloud"> </p><p> <label>Town</label> <input class="border-2" type="text" name="town" value="Nibelheim"> </p> </div> <div class="flex items-center mr-[10px] mt-[10px]"> <button class="btn bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]">Submit</button> <button class="btn bg-gray-200 text-gray-800 px-4 py-2 rounded-lg text-center text-sm font-medium mr-[10px]" hx-get="cancel">Cancel</button> ${spinnerSvg} </div> </form>`); }); app.post("/update", async function (req, res) { let name = req.body.name; let town = req.body.town; const doc = firestoreDb.doc(`demo/${name}`); //TODO: fix this bug await doc.set({ name: name /* town: town */ }); res.send(`<div hx-target="this" hx-swap="outerHTML" hx-indicator="spinner"> <p> <div><label>Name</label>: ${name}</div> </p><p> <div><label>Town</label>: ${town}</div> </p> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div>`); }); app.get("/cancel", (req, res) => { res.send(`<div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div>`); }); const port = parseInt(process.env.PORT) || 8080; app.listen(port, async () => { console.log(`booth demo: listening on port ${port}`); //serviceMetadata = helper(); }); app.get("/helper", async (req, res) => { let region = ""; let projectId = ""; let div = ""; try { // Fetch the token to make a GCF to GCF call const response1 = await get( "http://metadata.google.internal/computeMetadata/v1/project/project-id", { headers: { "Metadata-Flavor": "Google" } } ); // Fetch the token to make a GCF to GCF call const response2 = await get( "http://metadata.google.internal/computeMetadata/v1/instance/region", { headers: { "Metadata-Flavor": "Google" } } ); projectId = response1.data; let regionFull = response2.data; const index = regionFull.lastIndexOf("/"); region = regionFull.substring(index + 1); div = ` <div> This created the revision <code>${revision}</code> of the Cloud Run service <code>${service}</code> in <code>${region}</code> for project <code>${projectId}</code>. </div>`; } catch (ex) { // running locally div = `<div> This is running locally.</div>`; } res.send(div); });
إنشاء ملف باسم spinnerSvg.js
module.exports.spinnerSvg = `<svg id="spinner" alt="Loading..." class="htmx-indicator animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" > <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" ></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" ></path> </svg>`;
إنشاء ملف input.css
لـ tailwindCSS
@tailwind base; @tailwind components; @tailwind utilities;
وأنشئ ملف tailwind.config.js
لـ tailwindCSS
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./**/*.{html,js}"], theme: { extend: {} }, plugins: [] };
وأنشئ ملف .gitignore
.
node_modules/ npm-debug.log coverage/ package-lock.json .DS_Store
والآن، أنشِئ دليل public
جديدًا.
mkdir public cd public
وفي هذا الدليل العام، أنشئ ملف index.html
للواجهة الأمامية، التي ستستخدم htmx.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous" ></script> <link href="./output.css" rel="stylesheet" /> <title>Demo 1</title> </head> <body class="font-sans bg-body-image bg-cover bg-center leading-relaxed" > <div class="container max-w-[700px] mt-[50px] ml-auto mr-auto"> <div class="hero flex items-center"> <div class="message text-base text-center mb-[24px]"> <h1 class="text-2xl font-bold mb-[10px]"> It's running! </h1> <div class="congrats text-base font-normal"> Congratulations, you successfully deployed your service to Cloud Run. </div> </div> </div> <div class="details mb-[20px]"> <p> <div hx-trigger="load" hx-get="/helper" hx-swap="innerHTML" hx-target="this">Hello</div> </p> </div> <p class="callout text-sm text-blue-700 font-bold pt-4 pr-6 pb-4 pl-10 leading-tight" > You can deploy any container to Cloud Run that listens for HTTP requests on the port defined by the <code>PORT</code> environment variable. Cloud Run will scale automatically based on requests and you never have to worry about infrastructure. </p> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> Persistent Storage Example using Firestore </h1> <div hx-target="this" hx-swap="outerHTML"> <p> <div><label>Name</label>: Cloud</div> </p><p> <div><label>Town</label>: Nibelheim</div> </p> <div> <button hx-get="/edit" class="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium mt-[10px]" > Click to update </button> </div> </div> <h1 class="text-2xl font-bold mt-[40px] mb-[20px]"> What's next </h1> <p class="next text-base mt-4 mb-[20px]"> You can build this demo yourself! </p> <p class="cta"> <button class="bg-blue-500 text-white px-4 py-2 rounded-lg text-center text-sm font-medium" > VIEW CODELAB </button> </p> </div> </body> </html>
7. تشغيل التطبيق على الجهاز
في هذا القسم، ستُشغِّل التطبيق محليًا للتأكّد من وجود خطأ في التطبيق عندما يحاول المستخدم حفظ البيانات.
أولاً، يجب أن يكون لديك دور "مستخدم تخزين البيانات" للوصول إلى Firestore (في حال استخدام هويتك للمصادقة، على سبيل المثال، الجهاز قيد التشغيل في Cloud Shell) أو يمكنك انتحال هوية حساب المستخدم الذي تم إنشاؤه سابقًا.
استخدام ADC عند التشغيل محليًا
إذا كنت تستخدم Cloud Shell، يعني هذا أنّك تعمل على جهاز افتراضي في Google Compute Engine. سيتم تلقائيًا استخدام بيانات الاعتماد المرتبطة بهذا الجهاز الافتراضي (كما هو موضّح عند تشغيل gcloud auth list
) من خلال بيانات الاعتماد التلقائية للتطبيق (ADC)، لذا ليس من الضروري استخدام الأمر gcloud auth application-default login
. ومع ذلك، ستظلّ هويتك بحاجة إلى دور "مستخدم تخزين البيانات". يمكنك التخطّي إلى القسم تشغيل التطبيق محليًا.
ومع ذلك، إذا كنت تستخدم الوحدة الطرفية المحلية (أي ليس في Cloud Shell)، فستحتاج إلى استخدام "بيانات الاعتماد التلقائية للتطبيق" لمصادقة Google APIs. يمكنك 1) تسجيل الدخول باستخدام بيانات الاعتماد الخاصة بك (شرط أن يكون لديك دور "مستخدم تخزين البيانات") أو 2) تسجيل الدخول من خلال انتحال هوية حساب الخدمة المستخدَم في هذا الدرس التطبيقي حول الترميز.
الخيار 1) استخدام بيانات الاعتماد الخاصة بك لـ ADC
إذا أردت استخدام بيانات الاعتماد، يمكنك أولاً تشغيل gcloud auth list
للتحقّق من كيفية مصادقتك في gcloud. بعد ذلك، قد تحتاج إلى منح هويتك دور مستخدم Vertex AI. إذا كان لهوية هويتك دور "المالك"، سيكون لديك حاليًا دور مستخدم "مستخدم تخزين البيانات" هذا. وإذا لم يكن الأمر كذلك، يمكنك تشغيل هذا الأمر لمنح هويتك دور مستخدم Vertex AI ودور "مستخدم تخزين البيانات".
USER=<YOUR_PRINCIPAL_EMAIL> gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/datastore.user
ثم شغّل الأمر التالي
gcloud auth application-default login
الخيار 2) انتحال هوية حساب خدمة لـ ADC
إذا أردت استخدام حساب الخدمة الذي تم إنشاؤه في هذا الدرس التطبيقي حول الترميز، يجب أن يكون لحساب المستخدم دور "منشئ الرموز المميّزة لحساب الخدمة". يمكنك الحصول على هذا الدور من خلال تنفيذ الأمر التالي:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member user:$USER \ --role=roles/iam.serviceAccountTokenCreator
بعد ذلك، عليك تشغيل الأمر التالي لاستخدام ADC مع حساب الخدمة.
gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS
تشغيل التطبيق على الجهاز
بعد ذلك، تأكَّد من أنّك في الدليل الجذري cloud-run-github-cd-demo
الخاص بالدرس التطبيقي حول الترميز.
cd .. && pwd
الآن، ستقوم بتثبيت التبعيات.
npm install
وأخيرًا، يمكنك تشغيل التطبيق من خلال تشغيل النص البرمجي التالي. سينشئ هذا النص البرمجي أيضًا ملف exit.css من tailwindCSS.
npm run dev
والآن افتح متصفح الويب إلى http://localhost:8080. إذا كنت في Cloud Shell، يمكنك فتح الموقع الإلكتروني عن طريق فتح زر Web Preview (معاينة المنفذ) 8080.
أدخل نصًا لحقلي إدخال الاسم والبلدة واضغط على حفظ. وبعد ذلك، أعِد تحميل الصفحة. ستلاحظ أن حقل المدينة لم يستمر. ويمكنك إصلاح هذا الخطأ في القسم اللاحق.
إيقاف تشغيل التطبيق السريع محليًا (على سبيل المثال Ctrl^c في نظام التشغيل MacOS).
8. إنشاء مستودع GitHub
في الدليل المحلي، أنشِئ موقعًا إلكترونيًا جديدًا باسم "الفرع الرئيسي" كاسم الفرع التلقائي.
git init git branch -M main
إكمال قاعدة الرموز الحالية التي تحتوي على الخطأ يمكنك إصلاح الخطأ بعد ضبط عملية النشر المستمر.
git add . git commit -m "first commit for express application"
انتقِل إلى GitHub وأنشِئ مستودعًا فارغًا خاصًا بك أو متاحًا للجميع. يوصي هذا الدرس التطبيقي بتسمية مستودعك cloud-run-auto-deploy-codelab
لإنشاء مستودع فارغ، عليك عدم وضع علامة على كل الإعدادات التلقائية أو ضبطها على "بدون" بحيث لا يكون أي محتوى في المستودع تلقائيًا عند إنشائه، على سبيل المثال.
إذا أكملت هذه الخطوة بشكل صحيح، سترى التعليمات التالية على صفحة المستودع الفارغة:
عليك اتّباع تعليمات إرسال مستودع حالي من سطر الأوامر عن طريق تشغيل الأوامر التالية:
أولاً، أضف المستودع البعيد عن طريق تشغيل
git remote add origin <YOUR-REPO-URL-PER-GITHUB-INSTRUCTIONS>
ثم دفع الفرع الرئيسي إلى المستودع الرئيسي.
git push -u origin main
9. إعداد النشر المستمر
الآن بعد أن أصبح لديك رمز في GitHub، يمكنك إعداد نشر مستمر. انتقِل إلى Cloud Console للتشغيل في السحابة الإلكترونية.
- انقر على "إنشاء خدمة".
- انقر على النشر باستمرار من المستودع.
- انقر على إعداد إنشاء السحابة الإلكترونية.
- ضمن مستودع المصدر
- اختيار GitHub كموفِّر للمستودع
- انقر على إدارة المستودعات المرتبطة لضبط إذن الوصول إلى Cloud Build إلى المستودع.
- اختَر المستودع وانقر على التالي.
- ضمن تكوين الإصدار
- ترك الفرع كـ ^main$
- بالنسبة إلى نوع الإصدار، اختَر Go أو Node.js أو Python أو Java أو .NET Core أو Ruby أو PHP من خلال حِزم التطبيقات في Google Cloud
- ترك دليل سياق الإصدار باسم
/
- انقر على حفظ.
- ضمن المصادقة
- انقر على السماح بالاستدعاءات غير المُصدَّق عليها
- ضمن الحاويات(الحاويات)، المجلدات، الشبكات، الأمان
- ضمن علامة التبويب "الأمان"، اختَر حساب الخدمة الذي أنشأته في خطوة سابقة، على سبيل المثال:
Cloud Run access to Firestore
- ضمن علامة التبويب "الأمان"، اختَر حساب الخدمة الذي أنشأته في خطوة سابقة، على سبيل المثال:
- انقر على إنشاء.
سيؤدي هذا إلى نشر خدمة Cloud Run التي تحتوي على الخطأ الذي ستصلحه في القسم التالي.
10. إصلاح الخطأ
إصلاح الخطأ في الرمز البرمجي
في Cloud Shell Editor، يمكنك إضافة ملف app.js
والانتقال إلى التعليق المسمى //TODO: fix this bug
.
تغيير السطر التالي من
//TODO: fix this bug await doc.set({ name: name });
إلى
//fixed town bug await doc.set({ name: name, town: town });
التحقّق من الإصلاح من خلال التشغيل
npm run start
وافتح متصفح الويب. احفظ البيانات مجددًا للمدينة وأعِد تحميلها. عند إعادة التحميل، ستظهر بيانات البلدة التي تم إدخالها حديثًا وقد تم الاحتفاظ بها بشكل صحيح.
الآن بعد أن تحققت من الإصلاح، أصبحت جاهزًا لنشره. أولاً، اتّبع الحل.
git add . git commit -m "fixed town bug"
ثم إرسالها إلى المستودع الرئيسي على جيت هب.
git push origin main
ستنشر Cloud Build التغييرات تلقائيًا. يمكنك الانتقال إلى Cloud Console لخدمة Cloud Run لمراقبة تغييرات النشر.
التأكّد من حلّ المشكلة في قناة الإصدار العلني
عندما تُظهِر "وحدة التحكّم في Cloud Console" الخاصة بخدمة Cloud Run أنّ نسخة مراجعة ثانية تعمل الآن بنسبة 100%، على سبيل المثال https://console.cloud.google.com/run/detail/<YOUR_ معرفة>/<YOUR_SERVICE_NAME>/revisions، يمكنك فتح عنوان URL لخدمة "تشغيل السحابة الإلكترونية" في المتصفّح والتحقّق من استمرار بيانات البلدة التي تم إدخالها حديثًا بعد إعادة تحميل الصفحة.
11. تهانينا
تهانينا على إكمال الدرس التطبيقي حول الترميز.
ننصحك بمراجعة مستندات تشغيل السحابة الإلكترونية والنشر المستمر من git.
النقاط التي تناولناها
- كتابة تطبيق ويب Express باستخدام "محرِّر Cloud Shell"
- ربط حسابك على GitHub بخدمة Google Cloud لإجراء عمليات النشر المستمرة
- نشر تطبيقك تلقائيًا في "التشغيل في السحابة الإلكترونية"
- تعرَّف على طريقة استخدام HTMX وTailwindCSS.
12. تَنظيم
لتجنب تحصيل رسوم غير مقصودة، (على سبيل المثال، إذا تم استدعاء خدمات Cloud Run عن غير قصد أكثر من تخصيص استدعاء Cloud Run الشهري في الفئة المجانية)، يمكنك إما حذف Cloud Run أو حذف المشروع الذي أنشأته في الخطوة 2.
لحذف خدمة "تشغيل السحابة الإلكترونية"، انتقِل إلى "وحدة التحكم في تشغيل السحابة الإلكترونية في السحابة الإلكترونية" https://console.cloud.google.com/run واحذف خدمة "تشغيل السحابة الإلكترونية" التي أنشأتها في هذا الدرس التطبيقي حول الترميز، مثل حذف خدمة cloud-run-auto-deploy-codelab
.
إذا اخترت حذف المشروع بالكامل، يمكنك الانتقال إلى https://console.cloud.google.com/cloud-resource-manager، واختيار المشروع الذي أنشأته في الخطوة الثانية، ثم اختيار "حذف". إذا حذفت المشروع، ستحتاج إلى تغيير المشاريع في حزمة تطوير البرامج (SDK) للسحابة الإلكترونية. يمكنك عرض قائمة بجميع المشاريع المتاحة من خلال تشغيل gcloud projects list
.