ติดตั้งใช้งานแอปพลิเคชัน JavaScript แบบ Full Stack ไปยัง Cloud Run ด้วย Cloud SQL สำหรับ PostgreSQL

1. ภาพรวม

Cloud Run เป็นแพลตฟอร์มที่มีการจัดการครบวงจรซึ่งช่วยให้คุณเรียกใช้โค้ดได้โดยตรงบนโครงสร้างพื้นฐานที่ปรับขนาดได้ของ Google Codelab นี้จะแสดงวิธีเชื่อมต่อแอปพลิเคชัน Next.js ใน Cloud Run กับฐานข้อมูล Cloud SQL สำหรับ PostgreSQL

ในแล็บนี้ คุณจะได้เรียนรู้วิธีทำสิ่งต่อไปนี้

  • สร้างอินสแตนซ์ Cloud SQL สำหรับ PostgreSQL
  • ติดตั้งใช้งานแอปพลิเคชันใน Cloud Run ที่เชื่อมต่อกับฐานข้อมูล Cloud SQL

2. ข้อกำหนดเบื้องต้น

  1. หากยังไม่มีบัญชี Google คุณต้องสร้างบัญชี Google
    • ใช้บัญชีส่วนตัวแทนบัญชีงานหรือบัญชีโรงเรียน บัญชีงานและบัญชีโรงเรียนอาจมีข้อจำกัดที่ทำให้คุณเปิดใช้ API ที่จำเป็นสำหรับแล็บนี้ไม่ได้

3. การตั้งค่าโปรเจ็กต์

  1. ลงชื่อเข้าใช้ Google Cloud Console
  2. เปิดใช้การเรียกเก็บเงินใน Cloud Console
    • การทำแล็บนี้ควรมีค่าใช้จ่ายน้อยกว่า $1 USD ในทรัพยากรระบบคลาวด์
    • คุณสามารถทำตามขั้นตอนที่ส่วนท้ายของแล็บนี้เพื่อลบทรัพยากรเพื่อหลีกเลี่ยงการเรียกเก็บเงินเพิ่มเติม
    • ผู้ใช้ใหม่มีสิทธิ์ใช้ช่วงทดลองใช้ฟรีมูลค่า$300 USD
  3. สร้างโปรเจ็กต์ใหม่หรือเลือกใช้โปรเจ็กต์ที่มีอยู่ซ้ำ

4. เปิดเครื่องมือแก้ไข Cloud Shell

  1. ไปที่ Cloud Shell Editor
  2. หากเทอร์มินัลไม่ปรากฏที่ด้านล่างของหน้าจอ ให้เปิดโดยทำดังนี้
    • คลิกเมนู 3 ขีด ไอคอนเมนู 3 ขีด
    • คลิก Terminal
    • คลิก Terminal ใหม่เปิดเทอร์มินัลใหม่ใน Cloud Shell Editor
  3. ในเทอร์มินัล ให้ตั้งค่าโปรเจ็กต์ด้วยคำสั่งนี้
    • รูปแบบ:
      gcloud config set project [PROJECT_ID]
      
    • ตัวอย่าง
      gcloud config set project lab-project-id-example
      
    • หากจำรหัสโปรเจ็กต์ไม่ได้ ให้ทำดังนี้
      • คุณแสดงรหัสโปรเจ็กต์ทั้งหมดได้โดยใช้คำสั่งต่อไปนี้
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      ตั้งค่ารหัสโปรเจ็กต์ในเทอร์มินัลของ Cloud Shell Editor
  4. หากได้รับแจ้งให้ให้สิทธิ์ ให้คลิกให้สิทธิ์เพื่อดำเนินการต่อ คลิกเพื่อให้สิทธิ์ Cloud Shell
  5. คุณควรเห็นข้อความต่อไปนี้
    Updated property [core/project].
    
    หากเห็น WARNING และระบบขอให้คุณ Do you want to continue (Y/N)? แสดงว่าคุณอาจป้อนรหัสโปรเจ็กต์ไม่ถูกต้อง กด N กด Enter แล้วลองเรียกใช้คำสั่ง gcloud config set project อีกครั้ง

5. เปิดใช้ API

เปิดใช้ API ในเทอร์มินัลโดยทำดังนี้

gcloud services enable \
  compute.googleapis.com \
  sqladmin.googleapis.com \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  networkconnectivity.googleapis.com \
  servicenetworking.googleapis.com \
  cloudaicompanion.googleapis.com

หากได้รับแจ้งให้ให้สิทธิ์ ให้คลิกให้สิทธิ์เพื่อดำเนินการต่อ คลิกเพื่อให้สิทธิ์ Cloud Shell

คำสั่งนี้อาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์ แต่ในที่สุดควรจะแสดงข้อความว่าดำเนินการสำเร็จคล้ายกับข้อความนี้

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. สร้างบัญชีบริการ

สร้างและกำหนดค่าบัญชีบริการ Google Cloud เพื่อให้ Cloud Run ใช้บัญชีดังกล่าวเพื่อให้มีสิทธิ์ที่ถูกต้องในการเชื่อมต่อกับ Cloud SQL

  1. เรียกใช้คำสั่ง gcloud iam service-accounts create ดังนี้เพื่อสร้างบัญชีบริการใหม่
    gcloud iam service-accounts create quickstart-service-account \
      --display-name="Quickstart Service Account"
    
  2. เรียกใช้คำสั่ง gcloud projects add-iam-policy-binding ดังนี้เพื่อเพิ่มบทบาทผู้เขียนบันทึกไปยังบัญชีบริการ Google Cloud ที่คุณเพิ่งสร้าง
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"
    

7. สร้างฐานข้อมูล Cloud SQL

  1. สร้างนโยบายการเชื่อมต่อบริการเพื่ออนุญาตการเชื่อมต่อเครือข่ายจาก Cloud Run ไปยัง Cloud SQL ด้วย Private Service Connect
    gcloud network-connectivity service-connection-policies create quickstart-policy \
        --network=default \
        --project=${GOOGLE_CLOUD_PROJECT} \
        --region=us-central1 \
        --service-class=google-cloud-sql \
        --subnets=https://www.googleapis.com/compute/v1/projects/${GOOGLE_CLOUD_PROJECT}/regions/us-central1/subnetworks/default
    
  2. สร้างรหัสผ่านที่ไม่ซ้ำกันสำหรับฐานข้อมูล
    export DB_PASSWORD=$(openssl rand -base64 20)
    
  3. เรียกใช้คำสั่ง gcloud sql instances create เพื่อสร้างอินสแตนซ์ Cloud SQL
    gcloud sql instances create quickstart-instance \
        --project=${GOOGLE_CLOUD_PROJECT} \
        --root-password=${DB_PASSWORD} \
        --database-version=POSTGRES_17 \
        --tier=db-perf-optimized-N-2 \
        --region=us-central1 \
        --ssl-mode=ENCRYPTED_ONLY \
        --no-assign-ip \
        --enable-private-service-connect \
        --psc-auto-connections=network=projects/${GOOGLE_CLOUD_PROJECT}/global/networks/default
    

คำสั่งนี้อาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์

  1. เรียกใช้คำสั่ง gcloud sql databases create เพื่อสร้างฐานข้อมูล Cloud SQL ภายใน quickstart-instance
    gcloud sql databases create quickstart_db \
      --instance=quickstart-instance
    

8. เตรียมแอปพลิเคชัน

เตรียมแอปพลิเคชัน Next.js ที่ตอบสนองต่อคำขอ HTTP

  1. หากต้องการสร้างโปรเจ็กต์ Next.js ใหม่ชื่อ task-app ให้ใช้คำสั่งต่อไปนี้
    npx --yes create-next-app@15 task-app \
      --ts \
      --eslint \
      --tailwind \
      --no-src-dir \
      --turbopack \
      --app \
      --no-import-alias
    
  2. เปลี่ยนไดเรกทอรีเป็น task-app
    cd task-app
    
  3. ติดตั้ง pg เพื่อโต้ตอบกับฐานข้อมูล PostgreSQL
    npm install pg
    
  4. ติดตั้ง @types/pg เป็นการอ้างอิงสำหรับนักพัฒนาซอฟต์แวร์เพื่อใช้แอปพลิเคชัน TypeScript Next.js
    npm install --save-dev @types/pg
    
  5. เปิดไฟล์ actions.ts ใน Cloud Shell Editor โดยทำดังนี้
    cloudshell edit app/actions.ts
    
    ตอนนี้คุณควรเห็นไฟล์ว่างที่ส่วนบนของหน้าจอ คุณแก้ไขไฟล์ actions.ts นี้ได้ที่นี่ แสดงว่าโค้ดจะอยู่ในส่วนบนของหน้าจอ
  6. คัดลอกโค้ดต่อไปนี้และวางลงในไฟล์ actions.ts ที่เปิดอยู่
    'use server'
    import pg from 'pg';
    
    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
    };
    
    const { Pool } = pg;
    
    const pool = new Pool({
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      ssl: {
        // @ts-expect-error require true is not recognized by @types/pg, but does exist on pg
        require: true,
        rejectUnauthorized: false, // required for self-signed certs
        // https://node-postgres.com/features/ssl#self-signed-cert
      }
    });
    
    const tableCreationIfDoesNotExist = async () => {
      await pool.query(`CREATE TABLE IF NOT EXISTS tasks (
        id SERIAL NOT NULL,
        created_at timestamp NOT NULL,
        status VARCHAR(255) NOT NULL default 'IN_PROGRESS',
        title VARCHAR(1024) NOT NULL,
        PRIMARY KEY (id)
      );`);
    }
    
    // CREATE
    export async function addNewTaskToDatabase(newTask: string) {
      await tableCreationIfDoesNotExist();
      await pool.query(`INSERT INTO tasks(created_at, status, title) VALUES(NOW(), 'IN_PROGRESS', $1)`, [newTask]);
      return;
    }
    
    // READ
    export async function getTasksFromDatabase() {
      await tableCreationIfDoesNotExist();
      const { rows } = await pool.query(`SELECT id, created_at, status, title FROM tasks ORDER BY created_at DESC LIMIT 100`);
      return rows;
    }
    
    // UPDATE
    export async function updateTaskInDatabase(task: Task) {
      await tableCreationIfDoesNotExist();
      await pool.query(
        `UPDATE tasks SET status = $1, title = $2 WHERE id = $3`,
        [task.status, task.title, task.id]
      );
      return;
    }
    
    // DELETE
    export async function deleteTaskFromDatabase(taskId: string) {
      await tableCreationIfDoesNotExist();
      await pool.query(`DELETE FROM tasks WHERE id = $1`, [taskId]);
      return;
    }
    
  7. เปิดไฟล์ page.tsx ใน Cloud Shell Editor โดยทำดังนี้
    cloudshell edit app/page.tsx
    
    ตอนนี้ไฟล์ที่มีอยู่ควรปรากฏที่ส่วนบนของหน้าจอ คุณแก้ไขไฟล์ page.tsx นี้ได้ที่นี่ แสดงว่าโค้ดจะอยู่ในส่วนบนของหน้าจอ
  8. ลบเนื้อหาที่มีอยู่ในไฟล์ page.tsx
  9. คัดลอกโค้ดต่อไปนี้และวางลงในไฟล์ page.tsx ที่เปิดอยู่
    'use client'
    import React, { useEffect, useState } from "react";
    import { addNewTaskToDatabase, getTasksFromDatabase, deleteTaskFromDatabase, updateTaskInDatabase } from "./actions";
    
    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
    };
    
    export default function Home() {
      const [newTaskTitle, setNewTaskTitle] = useState('');
      const [tasks, setTasks] = useState<Task[]>([]);
    
      async function getTasks() {
        const updatedListOfTasks = await getTasksFromDatabase();
        setTasks(updatedListOfTasks);
      }
    
      useEffect(() => {
        getTasks();
      }, []);
    
      async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        await addNewTaskToDatabase(newTaskTitle);
        await getTasks();
        setNewTaskTitle('');
      };
    
      async function updateTask(task: Task, newTaskValues: Partial<Task>) {
        await updateTaskInDatabase({ ...task, ...newTaskValues });
        await getTasks();
      }
    
      async function deleteTask(taskId: string) {
        await deleteTaskFromDatabase(taskId);
        await getTasks();
      }
    
      return (
        <main className="p-4">
          <h2 className="text-2xl font-bold mb-4">To Do List</h2>
          <div className="flex mb-4">
            <form onSubmit={handleSubmit} className="flex mb-8">
              <input
                type="text"
                placeholder="New Task Title"
                value={newTaskTitle}
                onChange={(e) => setNewTaskTitle(e.target.value)}
                className="flex-grow border border-gray-400 rounded px-3 py-2 mr-2 bg-inherit"
              />
              <button
                type="submit"
                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-nowrap"
              >
                Add New Task
              </button>
            </form>
          </div>
          <table className="w-full">
            <tbody>
              {tasks.map(function (task) {
                const isComplete = task.status === 'COMPLETE';
                return (
                  <tr key={task.id} className="border-b border-gray-200">
                    <td className="py-2 px-4">
                      <input
                        type="checkbox"
                        checked={isComplete}
                        onClick={() => updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })}
                        className="transition-transform duration-300 ease-in-out transform scale-100 checked:scale-125 checked:bg-green-500"
                      />
                    </td>
                    <td className="py-2 px-4">
                      <span
                        className={`transition-all duration-300 ease-in-out ${isComplete ? 'line-through text-gray-400 opacity-50' : 'opacity-100'}`}
                      >
                        {task.title}
                      </span>
                    </td>
                    <td className="py-2 px-4">
                      <button
                        onClick={() => deleteTask(task.id)}
                        className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded float-right"
                      >
                        Delete
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </main>
      );
    }
    

ตอนนี้แอปพลิเคชันพร้อมใช้งานแล้ว

9. ทำให้แอปพลิเคชันใช้งานได้ใน Cloud Run

  1. เรียกใช้คำสั่ง gcloud projects add-iam-policy-binding ดังนี้เพื่อเพิ่มบทบาทผู้ใช้เครือข่ายให้กับบัญชีบริการ Cloud Run สำหรับบริการ Cloud Run ที่คุณกำลังจะสร้าง
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
        --member "serviceAccount:service-$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} --format="value(projectNumber)")@serverless-robot-prod.iam.gserviceaccount.com" \
        --role "roles/compute.networkUser"
    
  1. เรียกใช้คำสั่ง gcloud projects add-iam-policy-binding ดังนี้เพื่อเพิ่มบทบาทผู้เขียน Artifact Registry ให้กับผู้ใช้ปัจจุบันสำหรับบริการ Cloud Run ที่คุณกำลังจะสร้าง
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
        --member=user:$(gcloud auth list --filter=status:ACTIVE --format="value(account)") \
        --role="roles/artifactregistry.writer"
    
  1. เรียกใช้คำสั่งด้านล่างเพื่อทำให้แอปพลิเคชันใช้งานได้ใน Cloud Run
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=. \
      --set-env-vars DB_NAME="quickstart_db" \
      --set-env-vars DB_USER="postgres" \
      --set-env-vars DB_PASSWORD=${DB_PASSWORD} \
      --set-env-vars DB_HOST="$(gcloud sql instances describe quickstart-instance --project=${GOOGLE_CLOUD_PROJECT} --format='value(settings.ipConfiguration.pscConfig.pscAutoConnections.ipAddress)')" \
      --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --network=default \
      --subnet=default \
      --allow-unauthenticated
    
  2. หากได้รับข้อความแจ้ง ให้กด Y และ Enter เพื่อยืนยันว่าคุณต้องการดำเนินการต่อ
    Do you want to continue (Y/n)? Y
    

หลังจากผ่านไป 2-3 นาที แอปพลิเคชันควรแสดง URL ให้คุณเข้าชม

ไปที่ URL เพื่อดูแอปพลิเคชันของคุณทำงาน ทุกครั้งที่คุณเข้าชม URL หรือรีเฟรชหน้าเว็บ คุณจะเห็นแอปงาน

10. เพิ่มฟีเจอร์ด้วย Gemini Code Assist

ตอนนี้คุณได้ติดตั้งใช้งานเว็บแอปที่มีฐานข้อมูลแล้ว ต่อไป เราจะเพิ่มฟีเจอร์ใหม่ลงในแอป Next.js โดยใช้ความสามารถของความช่วยเหลือจาก AI

  1. กลับไปที่ Cloud Shell Editor
  2. เปิด page.tsx อีกครั้ง
    cd ~/task-app
    cloudshell edit app/page.tsx
    
  3. ไปที่ Gemini Code Assist ใน Cloud Shell Editor โดยทำดังนี้
    • คลิกไอคอน Gemini ไอคอน Gemini Code Assist ในแถบเครื่องมือทางด้านซ้ายของหน้าจอ
    • หากได้รับข้อความแจ้ง ให้ลงชื่อเข้าใช้ด้วยข้อมูลเข้าสู่ระบบของบัญชี Google
    • หากได้รับแจ้งให้เลือกโปรเจ็กต์ ให้เลือกโปรเจ็กต์ที่คุณสร้างขึ้นสำหรับ Codelab นี้ เลือกโปรเจ็กต์สำหรับ Gemini Code Assist
  4. ป้อนพรอมต์ Add the ability to update the title of the task. The code in your output should be complete and working code. การตอบกลับควรมีข้อมูลคล้ายกับข้อมูลโค้ดต่อไปนี้เพื่อเพิ่มฟังก์ชัน handleEditStart และ handleEditCancel
    const [editingTaskId, setEditingTaskId] = useState('');
    const [editedTaskTitle, setEditedTaskTitle] = useState('');
    
    function handleEditStart(task: Task) {
      setEditingTaskId(task.id);
      setEditedTaskTitle(task.title);
    };
    
  5. แทนที่ page.tsx ด้วยเอาต์พุตของ Gemini Code Assist ตัวอย่างที่ใช้งานได้มีดังนี้
    'use client'
    import React, { useEffect, useState } from "react";
    import { addNewTaskToDatabase, getTasksFromDatabase, deleteTaskFromDatabase, updateTaskInDatabase } from "./actions";
    
    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
    };
    
    export default function Home() {
      const [newTaskTitle, setNewTaskTitle] = useState('');
      const [tasks, setTasks] = useState<Task[]>([]);
      const [editingTaskId, setEditingTaskId] = useState('');
      const [editedTaskTitle, setEditedTaskTitle] = useState('');
    
      async function getTasks() {
        const updatedListOfTasks = await getTasksFromDatabase();
        setTasks(updatedListOfTasks);
      }
    
      useEffect(() => {
        getTasks();
      }, []);
    
      async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        await addNewTaskToDatabase(newTaskTitle);
        await getTasks();
        setNewTaskTitle('');
      };
    
      async function updateTask(task: Task, newTaskValues: Partial<Task>) {
        await updateTaskInDatabase({ ...task, ...newTaskValues });
        await getTasks();
        setEditingTaskId('');
        setEditedTaskTitle('');
      }
    
      async function deleteTask(taskId: string) {
        await deleteTaskFromDatabase(taskId);
        await getTasks();
      }
    
      function handleEditStart(task: Task) {
        setEditingTaskId(task.id);
        setEditedTaskTitle(task.title);
      };
    
      return (
        <main className="p-4">
          <h2 className="text-2xl font-bold mb-4">To Do List</h2>
          <div className="flex mb-4">
            <form onSubmit={handleSubmit} className="flex mb-8">
              <input
                type="text"
                placeholder="New Task Title"
                value={newTaskTitle}
                onChange={(e) => setNewTaskTitle(e.target.value)}
                className="flex-grow border border-gray-400 rounded px-3 py-2 mr-2 bg-inherit"
              />
              <button
                type="submit"
                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-nowrap"
              >
                Add New Task
              </button>
            </form>
          </div>
          <table className="w-full">
            <tbody>
              {tasks.map(function (task) {
                const isComplete = task.status === 'COMPLETE';
                return (
                  <tr key={task.id} className="border-b border-gray-200">
                    <td className="py-2 px-4">
                      <input
                        type="checkbox"
                        checked={isComplete}
                        onClick={() => updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })}
                        className="transition-transform duration-300 ease-in-out transform scale-100 checked:scale-125 checked:bg-green-500"
                      />
                    </td>
                    <td className="py-2 px-4">
                      {editingTaskId === task.id ? (
                        <form
                          onSubmit={(e) => {
                            e.preventDefault();
                            updateTask(task, { title: editedTaskTitle });
                          }}
                          className="flex"
                        >
                          <input
                            type="text"
                            value={editedTaskTitle}
                            onChange={(e) => setEditedTaskTitle(e.target.value)}
                            onBlur={() => updateTask(task, { title: editedTaskTitle })} // Handle clicking outside input
                            className="flex-grow border border-gray-400 rounded px-3 py-1 mr-2 bg-inherit"
                          />
                        </form>
                      ) : (
                        <span
                          onClick={() => handleEditStart(task)}
                          className={`transition-all duration-300 ease-in-out cursor-pointer ${isComplete ? 'line-through text-gray-400 opacity-50' : 'opacity-100'}`}
                        >
                          {task.title}
                        </span>
                      )}
                    </td>
                    <td className="py-2 px-4">
                      <button
                        onClick={() => deleteTask(task.id)}
                        className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded float-right"
                      >
                        Delete
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </main>
      );
    }
    

11. ติดตั้งใช้งานแอปพลิเคชันไปยัง Cloud Run อีกครั้ง

  1. เรียกใช้คำสั่งด้านล่างเพื่อทำให้แอปพลิเคชันใช้งานได้ใน Cloud Run
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=. \
      --set-env-vars DB_NAME="quickstart_db" \
      --set-env-vars DB_USER="postgres" \
      --set-env-vars DB_PASSWORD=${DB_PASSWORD} \
      --set-env-vars DB_HOST="$(gcloud sql instances describe quickstart-instance --project=${GOOGLE_CLOUD_PROJECT} --format='value(settings.ipConfiguration.pscConfig.pscAutoConnections.ipAddress)')" \
      --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --network=default \
      --subnet=default \
      --allow-unauthenticated
    
  2. หากได้รับข้อความแจ้ง ให้กด Y และ Enter เพื่อยืนยันว่าคุณต้องการดำเนินการต่อ
    Do you want to continue (Y/n)? Y
    

12. ขอแสดงความยินดี

ในแล็บนี้ คุณได้เรียนรู้วิธีการต่อไปนี้

  • สร้างอินสแตนซ์ Cloud SQL สำหรับ PostgreSQL
  • ติดตั้งใช้งานแอปพลิเคชันใน Cloud Run ที่เชื่อมต่อกับฐานข้อมูล Cloud SQL

ล้างข้อมูล

Cloud SQL ไม่มีระดับฟรีและจะเรียกเก็บเงินจากคุณหากคุณใช้ต่อไป คุณสามารถลบโปรเจ็กต์ Cloud เพื่อหลีกเลี่ยงการเรียกเก็บเงินเพิ่มเติม

แม้ว่า Cloud Run จะไม่เรียกเก็บเงินเมื่อไม่ได้ใช้บริการ แต่คุณอาจยังคงถูกเรียกเก็บเงินสำหรับการจัดเก็บอิมเมจคอนเทนเนอร์ใน Artifact Registry การลบโปรเจ็กต์ Cloud จะหยุดการเรียกเก็บเงินสำหรับทรัพยากรทั้งหมดที่ใช้ภายในโปรเจ็กต์นั้น

หากต้องการ ให้ลบโปรเจ็กต์โดยทำดังนี้

gcloud projects delete $GOOGLE_CLOUD_PROJECT

นอกจากนี้ คุณอาจต้องการลบทรัพยากรที่ไม่จำเป็นออกจากดิสก์ Cloud Shell ด้วย ดังนี้

  1. ลบไดเรกทอรีโปรเจ็กต์ Codelab
    rm -rf ~/task-app
    
  2. คำเตือน! การดำเนินการถัดไปนี้จะยกเลิกไม่ได้ หากต้องการลบทุกอย่างใน Cloud Shell เพื่อเพิ่มพื้นที่ว่าง คุณสามารถลบไดเรกทอรีหน้าแรกทั้งหมดได้ โปรดระมัดระวังและตรวจสอบว่าได้บันทึกทุกอย่างที่คุณต้องการเก็บไว้ที่อื่นแล้ว
    sudo rm -rf $HOME
    

เรียนรู้อย่างต่อเนื่อง