Cách tự động triển khai các thay đổi từ GitHub sang Cloud Run bằng Cloud Build

1. Giới thiệu

Tổng quan

Trong lớp học lập trình này, bạn sẽ định cấu hình Cloud Run để tự động tạo và triển khai các phiên bản mới của ứng dụng mỗi khi bạn đẩy các thay đổi về mã nguồn vào một kho lưu trữ GitHub.

Ứng dụng minh hoạ này lưu dữ liệu người dùng vào Firestore, tuy nhiên, chỉ một phần dữ liệu được lưu đúng cách. Bạn sẽ định cấu hình quá trình triển khai liên tục để khi bạn đẩy bản sửa lỗi vào kho lưu trữ GitHub, bạn sẽ tự động thấy bản sửa lỗi đó có trong một bản sửa đổi mới.

Kiến thức bạn sẽ học được

  • Viết ứng dụng web Express bằng Trình chỉnh sửa Cloud Shell
  • Kết nối tài khoản GitHub với Google Cloud để triển khai liên tục
  • Tự động triển khai ứng dụng lên Cloud Run
  • Tìm hiểu cách sử dụng HTMX và TailwindCSS

2. Thiết lập và yêu cầu

Điều kiện tiên quyết

Kích hoạt Cloud Shell

  1. Trong Cloud Console, hãy nhấp vào Kích hoạt Cloud Shell d1264ca30785e435.png.

cb81e7c8e34bc8d.png

Nếu đây là lần đầu tiên bạn khởi động Cloud Shell, bạn sẽ thấy một màn hình trung gian mô tả về Cloud Shell. Nếu bạn thấy màn hình trung gian, hãy nhấp vào Tiếp tục.

d95252b003979716.png

Quá trình cung cấp và kết nối với Cloud Shell chỉ mất vài phút.

7833d5e1c5d18f54.png

Máy ảo này được tải tất cả các công cụ phát triển cần thiết. Máy ảo này cung cấp một thư mục chính 5 GB liên tục và chạy trong Google Cloud, giúp tăng cường đáng kể hiệu suất mạng và xác thực. Bạn có thể thực hiện hầu hết, nếu không phải là tất cả, công việc trong lớp học lập trình này bằng trình duyệt.

Sau khi kết nối với Cloud Shell, bạn sẽ thấy rằng mình đã được xác thực và dự án được đặt thành mã dự án của bạn.

  1. Chạy lệnh sau trong Cloud Shell để xác nhận rằng bạn đã được xác thực:
gcloud auth list

Đầu ra của lệnh

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Chạy lệnh sau trong Cloud Shell để xác nhận rằng lệnh gcloud biết về dự án của bạn:
gcloud config list project

Đầu ra của lệnh

[core]
project = <PROJECT_ID>

Nếu không, bạn có thể đặt bằng lệnh này:

gcloud config set project <PROJECT_ID>

Đầu ra của lệnh

Updated property [core/project].

3. Bật API và đặt các biến môi trường

Bật API

Lớp học lập trình này yêu cầu sử dụng các API sau. Bạn có thể bật các API đó bằng cách chạy lệnh sau:

gcloud services enable run.googleapis.com \
    cloudbuild.googleapis.com \
    firestore.googleapis.com \
    iamcredentials.googleapis.com

Thiết lập các biến môi trường

Bạn có thể đặt các biến môi trường sẽ được sử dụng trong suốt lớp học lập trình này.

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. Tạo một tài khoản dịch vụ

Tài khoản dịch vụ này sẽ được Cloud Run sử dụng để gọi Vertex AI Gemini API. Tài khoản dịch vụ này cũng có quyền đọc và ghi vào Firestore, cũng như đọc các bí mật từ Secret Manager.

Trước tiên, hãy tạo tài khoản dịch vụ bằng cách chạy lệnh sau:

gcloud iam service-accounts create $SERVICE_ACCOUNT \
  --display-name="Cloud Run access to Firestore"

Bây giờ, hãy cấp cho tài khoản dịch vụ quyền đọc và ghi vào Firestore.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role=roles/datastore.user

5. Tạo và định cấu hình dự án Firebase

  1. Trong bảng điều khiển của Firebase, hãy nhấp vào Thêm dự án.
  2. Nhập <YOUR_PROJECT_ID> để thêm Firebase vào một trong các dự án hiện có trên Google Cloud
  3. Nếu được nhắc, hãy xem xét và chấp nhận các điều khoản của Firebase.
  4. Nhấp vào Tiếp tục.
  5. Nhấp vào Xác nhận gói để xác nhận gói thanh toán Firebase.
  6. Bạn có thể chọn Bật Google Analytics cho lớp học lập trình này.
  7. Nhấp vào Thêm Firebase.
  8. Khi dự án đã được tạo, hãy nhấp vào Tiếp tục.
  9. Từ trình đơn Tạo, hãy nhấp vào Cơ sở dữ liệu Firestore.
  10. Nhấp vào Tạo cơ sở dữ liệu.
  11. Chọn khu vực của bạn trong trình đơn thả xuống Vị trí, sau đó nhấp vào Tiếp theo.
  12. Sử dụng Bắt đầu ở chế độ phát hành công khai mặc định, sau đó nhấp vào Tạo.

6. Viết ứng dụng

Trước tiên, hãy tạo một thư mục cho mã nguồn và cd vào thư mục đó.

mkdir cloud-run-github-cd-demo && cd $_

Sau đó, hãy tạo tệp package.json có nội dung sau:

{
  "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"
  }
}

Trước tiên, hãy tạo tệp nguồn app.js có nội dung bên dưới. Tệp này chứa điểm truy cập cho dịch vụ và chứa logic chính cho ứng dụng.

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);
});

Tạo tệp có tên là 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>`;

Tạo tệp input.css cho tailwindCSS

@tailwind base;
@tailwind components;
@tailwind utilities;

Và tạo tệp tailwind.config.js cho tailwindCSS

/** @type {import('tailwindcss').Config} */
module.exports = {
    content: ["./**/*.{html,js}"],
    theme: {
        extend: {}
    },
    plugins: []
};

Và tạo tệp .gitignore.

node_modules/

npm-debug.log
coverage/

package-lock.json

.DS_Store

Bây giờ, hãy tạo một thư mục public mới.

mkdir public
cd public

Và trong thư mục công khai đó, hãy tạo tệp index.html cho giao diện người dùng, tệp này sẽ sử dụng 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. Chạy ứng dụng cục bộ

Trong phần này, bạn sẽ chạy ứng dụng cục bộ để xác nhận rằng có một lỗi trong ứng dụng khi người dùng cố gắng lưu dữ liệu.

Trước tiên, bạn cần có vai trò Người dùng Datastore để truy cập vào Firestore (nếu sử dụng danh tính của bạn để xác thực, chẳng hạn như bạn đang chạy trong Cloud Shell) hoặc bạn có thể mạo danh tài khoản người dùng đã tạo trước đó.

Sử dụng ADC khi chạy cục bộ

Nếu đang chạy trong Cloud Shell, thì bạn đã chạy trên một máy ảo Google Compute Engine. Thông tin xác thực của bạn được liên kết với máy ảo này (như được hiển thị bằng cách chạy gcloud auth list) sẽ tự động được Thông tin xác thực mặc định của ứng dụng (ADC) sử dụng, vì vậy, bạn không cần sử dụng lệnh gcloud auth application-default login. Tuy nhiên, danh tính của bạn vẫn cần có vai trò Người dùng Datastore. Bạn có thể bỏ qua phần Chạy ứng dụng cục bộ.

Tuy nhiên, nếu đang chạy trên thiết bị đầu cuối cục bộ (tức là không phải trong Cloud Shell), bạn cần sử dụng Thông tin xác thực mặc định của ứng dụng để xác thực với các API của Google. Bạn có thể 1) đăng nhập bằng thông tin xác thực của mình (miễn là bạn có vai trò Người dùng Datastore) hoặc 2) bạn có thể đăng nhập bằng cách mạo danh tài khoản dịch vụ được sử dụng trong lớp học lập trình này.

Lựa chọn 1) Sử dụng thông tin xác thực của bạn cho ADC

Nếu muốn sử dụng thông tin xác thực của mình, trước tiên, bạn có thể chạy gcloud auth list để xác minh cách bạn được xác thực trong gcloud. Tiếp theo, bạn có thể cần cấp cho danh tính của mình vai trò Người dùng Vertex AI. Nếu danh tính của bạn có vai trò Chủ sở hữu, thì bạn đã có vai trò người dùng Người dùng Datastore này. Nếu không, bạn có thể chạy lệnh này để cấp cho danh tính của mình vai trò người dùng Vertex AI và vai trò Người dùng Datastore.

USER=<YOUR_PRINCIPAL_EMAIL>

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member user:$USER \
  --role=roles/datastore.user

Sau đó, chạy lệnh sau

gcloud auth application-default login

Lựa chọn 2) Mạo danh Tài khoản dịch vụ cho ADC

Nếu muốn sử dụng tài khoản dịch vụ được tạo trong lớp học lập trình này, thì tài khoản người dùng của bạn cần có vai trò Trình tạo mã thông báo tài khoản dịch vụ. Bạn có thể lấy vai trò này bằng cách chạy lệnh sau:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member user:$USER \
  --role=roles/iam.serviceAccountTokenCreator

Tiếp theo, bạn sẽ chạy lệnh sau để sử dụng ADC với tài khoản dịch vụ

gcloud auth application-default login --impersonate-service-account=$SERVICE_ACCOUNT_ADDRESS

Chạy ứng dụng cục bộ

Tiếp theo, hãy đảm bảo bạn đang ở thư mục gốc cloud-run-github-cd-demo cho lớp học lập trình.

cd .. && pwd

Bây giờ, bạn sẽ cài đặt các phần phụ thuộc.

npm install

Cuối cùng, bạn có thể khởi động ứng dụng bằng cách chạy tập lệnh sau. Tập lệnh này cũng sẽ tạo tệp output.css từ tailwindCSS.

npm run dev

Bây giờ, hãy mở trình duyệt web của bạn đến http://localhost:8080. Nếu đang ở trong Cloud Shell, bạn có thể mở trang web bằng cách mở nút Xem trước trên web và chọn Xem trước cổng 8080.

xem trước trên web – nút xem trước trên cổng 8080

Nhập văn bản cho các trường nhập tên và thị trấn rồi nhấn vào lưu. Sau đó, hãy làm mới trang. Bạn sẽ nhận thấy rằng trường thị trấn không được duy trì. Bạn sẽ khắc phục lỗi này trong phần tiếp theo.

Dừng ứng dụng Express chạy cục bộ (ví dụ: Ctrl^c trên MacOS).

8. Tạo kho lưu trữ GitHub

Trong thư mục cục bộ, hãy tạo một kho lưu trữ mới có main làm tên nhánh mặc định.

git init
git branch -M main

Cam kết cơ sở mã hiện tại có chứa lỗi. Bạn sẽ khắc phục lỗi sau khi định cấu hình quá trình triển khai liên tục.

git add .
git commit -m "first commit for express application"

Chuyển đến GitHub và tạo một kho lưu trữ trống ở chế độ riêng tư hoặc công khai. Lớp học lập trình này đề xuất đặt tên cho kho lưu trữ là cloud-run-auto-deploy-codelab. Để tạo một kho lưu trữ trống, bạn sẽ bỏ chọn tất cả các chế độ cài đặt mặc định hoặc đặt thành không có để không có nội dung nào trong kho lưu trữ theo mặc định khi được tạo, ví dụ:

Chế độ cài đặt mặc định của GitHub

Nếu bạn hoàn tất bước này đúng cách, bạn sẽ thấy các hướng dẫn sau trên trang kho lưu trữ trống:

Hướng dẫn về kho lưu trữ GitHub trống

Bạn sẽ làm theo hướng dẫn đẩy một kho lưu trữ hiện có từ dòng lệnh bằng cách chạy các lệnh sau:

Trước tiên, hãy thêm kho lưu trữ từ xa bằng cách chạy

git remote add origin <YOUR-REPO-URL-PER-GITHUB-INSTRUCTIONS>

sau đó đẩy nhánh chính vào kho lưu trữ ngược dòng.

git push -u origin main

9. Thiết lập quá trình triển khai liên tục

Bây giờ bạn đã có mã trong GitHub, bạn có thể thiết lập quá trình triển khai liên tục. Chuyển đến Cloud Console cho Cloud Run.

  • Nhấp vào Tạo dịch vụ
  • Nhấp vào Triển khai liên tục từ kho lưu trữ
  • Nhấp vào THIẾT LẬP CLOUD BUILD.
  • Trong phần Kho lưu trữ nguồn
    • Chọn GitHub làm Nhà cung cấp kho lưu trữ
    • Nhấp vào Quản lý kho lưu trữ được kết nối để định cấu hình quyền truy cập Cloud Build vào kho lưu trữ
    • Chọn kho lưu trữ của bạn rồi nhấp vào Tiếp theo
  • Trong phần Cấu hình bản dựng
    • Để nguyên Branch là ^main$
    • Đối với Loại bản dựng, hãy chọn Go, Node.js, Python, Java, .NET Core, Ruby hoặc PHP thông qua buildpack của Google Cloud
  • Để nguyên Thư mục ngữ cảnh bản dựng là /
  • Nhấp vào Lưu
  • Trong phần Xác thực
    • Nhấp vào Cho phép lệnh gọi không được xác thực
  • Trong phần Vùng chứa, Ổ đĩa, Mạng, Bảo mật
    • Trong thẻ Bảo mật, hãy chọn tài khoản dịch vụ mà bạn đã tạo ở bước trước đó, ví dụ: Cloud Run access to Firestore
  • Nhấp vào TẠO

Thao tác này sẽ triển khai dịch vụ Cloud Run có chứa lỗi mà bạn sẽ khắc phục trong phần tiếp theo.

10. Khắc phục lỗi

Khắc phục lỗi trong mã

Trong Trình chỉnh sửa Cloud Shell, hãy mở tệp app.js và chuyển đến nhận xét có nội dung //TODO: fix this bug

thay đổi dòng sau từ

 //TODO: fix this bug
    await doc.set({
        name: name
    });

tới

//fixed town bug
    await doc.set({
        name: name,
        town: town
    });

Xác minh bản sửa lỗi bằng cách chạy

npm run start

và mở trình duyệt web. Lưu lại dữ liệu cho thị trấn và làm mới. Bạn sẽ thấy dữ liệu thị trấn mới nhập đã được duy trì chính xác khi làm mới.

Bây giờ bạn đã xác minh bản sửa lỗi, bạn đã sẵn sàng triển khai bản sửa lỗi đó. Trước tiên, hãy cam kết bản sửa lỗi.

git add .
git commit -m "fixed town bug"

rồi đẩy bản sửa lỗi đó vào kho lưu trữ ngược dòng trên GitHub.

git push origin main

Cloud Build sẽ tự động triển khai các thay đổi của bạn. Bạn có thể chuyển đến Cloud Console cho dịch vụ Cloud Run để theo dõi các thay đổi về quá trình triển khai.

Xác minh bản sửa lỗi trong môi trường phát hành công khai

Sau khi bảng điều khiển Cloud cho dịch vụ Cloud Run hiển thị bản sửa đổi thứ 2 hiện đang phân phát 100% lưu lượng truy cập, ví dụ: https://console.cloud.google.com/run/detail/<YOUR_REGION>/<YOUR_SERVICE_NAME>/revisions, bạn có thể mở URL dịch vụ Cloud Run trong trình duyệt và xác minh dữ liệu thị trấn mới nhập được duy trì sau khi làm mới trang.

11. Xin chúc mừng!

Chúc mừng bạn đã hoàn thành lớp học lập trình này!

Bạn nên xem lại tài liệu về Cloud Runquá trình triển khai liên tục từ git.

Nội dung chúng ta đã đề cập

  • Viết ứng dụng web Express bằng Trình chỉnh sửa Cloud Shell
  • Kết nối tài khoản GitHub với Google Cloud để triển khai liên tục
  • Tự động triển khai ứng dụng lên Cloud Run
  • Tìm hiểu cách sử dụng HTMX và TailwindCSS

12. Dọn dẹp

Để tránh bị tính phí ngoài ý muốn (ví dụ: nếu các dịch vụ Cloud Run vô tình được gọi nhiều lần hơn hạn mức gọi Cloud Run hằng tháng trong cấp miễn phí), bạn có thể xoá Cloud Run hoặc xoá dự án mà bạn đã tạo ở Bước 2.

Để xoá dịch vụ Cloud Run, hãy chuyển đến Cloud Console của Cloud Run tại https://console.cloud.google.com/run rồi xoá dịch vụ Cloud Run mà bạn đã tạo trong lớp học lập trình này, ví dụ: xoá dịch vụ cloud-run-auto-deploy-codelab.

Nếu chọn xoá toàn bộ dự án, bạn có thể chuyển đến https://console.cloud.google.com/cloud-resource-manager, chọn dự án mà bạn đã tạo ở Bước 2 rồi chọn Xoá. Nếu xoá dự án, bạn cần thay đổi dự án trong Cloud SDK. Bạn có thể xem danh sách tất cả các dự án hiện có bằng cách chạy gcloud projects list.