从 Cloud Run 连接到 Private CloudSQL

1. 概览

在本实验中,您将创建 Cymbal Eats 菜单服务,公开用于添加、更新、删除和列出菜单项的 RESTful API。您将创建一个 Cloud SQL 数据库,作为菜单服务的后端数据库,该菜单服务将在 Cloud Run 中运行。由于 Cloud Run 与 Cloud SQL 数据库不位于同一 VPC,因此您需要配置无服务器 VPC 访问通道连接器,以允许 Cloud Run 通过专用 IP 地址与 Cloud SQL 通信。

19c7b05f35789fda

学习内容

在本实验中,您将学习如何完成以下操作:

  • 配置专用 VPC 网络
  • 创建专用 Postgres Cloud SQL 数据库
  • 将 CloudRun 连接到专用 VPC
  • 在 Cloud Run 上部署一项连接到 Cloud SQL 数据库的服务

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时对其进行更新。
  • 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常您不在乎这是什么在大多数 Codelab 中,您都需要引用项目 ID(它通常标识为 PROJECT_ID)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且该 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即某些 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。如需关停资源,以免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

环境设置

  1. 创建与项目和资源相关的环境变量
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export PROJECT_NAME=$(gcloud projects describe $PROJECT_ID --format='value(name)')
export REGION=us-east1
export MENU_SERVICE_NAME=menu-service

export SERVERLESS_VPC_CONNECTOR=cymbalconnector
export DB_INSTANCE_NAME=menu-catalog
export DB_INSTANCE_PASSWORD=password123
export DB_DATABASE=menu-db
export DB_USER=menu-user
export DB_PASSWORD=menupassword123
  1. 克隆代码库并导航到相应目录
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git && cd cymbal-eats/menu-service
  1. 启用服务
gcloud services enable \
    sqladmin.googleapis.com \
    run.googleapis.com \
    vpcaccess.googleapis.com \
    servicenetworking.googleapis.com

3. 配置专用访问通道

专用服务访问通道是作为您的 VPC 网络与 Cloud SQL 实例所在的底层 Google Cloud VPC 网络之间的 VPC 对等互连链路提供的。通过专用连接,您的 VPC 网络中的虚拟机实例和您使用的服务只能通过内部 IP 地址进行通信。如需访问通过专用服务访问通道提供的服务,虚拟机实例不需要互联网连接或外部 IP 地址。

  1. 分配 IP 地址范围
gcloud compute addresses create google-managed-services-default \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=20 \
    --network=projects/$PROJECT_ID/global/networks/default

输出示例

Created [https://www.googleapis.com/compute/v1/projects/cymbal-eats-2-348215/global/addresses/google-managed-services-default].
  1. 创建专用连接。
gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=google-managed-services-default \
    --network=default \
    --project=$PROJECT_ID

输出示例

Operation "operations/pssn.p24-528514492617-2f2b507f-e4e5-4d53-a4de-9ddaceb4e92f" finished successfully.

4. 设置 Cloud SQL

Cloud SQL 是一种全代管式数据库服务,让您可以轻松地在云端设置、维护、管理和控制 PostgreSQL 和 MySQL 关系型数据库。每个 Cloud SQL 实例都由一个在 Google Cloud 主机服务器上运行的虚拟机 (VM) 提供支持。高可用性选项还包含另一个可用区中的备用虚拟机,其设置与主虚拟机相同。该数据库保存在可扩缩且持久的网络存储设备(称为永久性磁盘)上,该设备已连接到虚拟机。系统会为每个虚拟机分配一个静态 IP 地址,以确保应用连接的 IP 地址在 Cloud SQL 实例的生命周期内保持不变。

219cb722c2dd1b82

您将创建一个具有专用 IP 地址的 Postgres Cloud SQL 数据库。

创建数据库和用户

  1. 创建 Postgres Cloud SQL 实例以使用专用 IP
gcloud sql instances create $DB_INSTANCE_NAME \
    --project=$PROJECT_ID \
    --network=projects/$PROJECT_ID/global/networks/default \
    --no-assign-ip \
    --database-version=POSTGRES_12 \
    --cpu=2 \
    --memory=4GB \
    --region=$REGION \
    --root-password=${DB_INSTANCE_PASSWORD}

输出示例

Created [https://sqladmin.googleapis.com/sql/v1beta4/projects/cymbal1/instances/menu-instance].
NAME: menu-instance
DATABASE_VERSION: POSTGRES_12
LOCATION: us-east1-a
TIER: db-custom-2-4096
PRIMARY_ADDRESS: -
PRIVATE_ADDRESS: 10.8.80.5
STATUS: RUNNABLE
  1. 向数据库实例添加数据库
gcloud sql databases create $DB_DATABASE --instance=$DB_INSTANCE_NAME

输出示例

Created database [menu-db].
instance: menu-catalog
name: menu-db
project: cymbal1
  1. 创建 SQL 用户
gcloud sql users create ${DB_USER} \
    --password=$DB_PASSWORD \
    --instance=$DB_INSTANCE_NAME

输出示例

Created user [menu-user].
  1. 存储数据库 IP 地址
export DB_INSTANCE_IP=$(gcloud sql instances describe $DB_INSTANCE_NAME \
    --format=json | jq \
    --raw-output ".ipAddresses[].ipAddress")
  1. 将 Cloud SQL Client 角色添加到 Compute Engine 服务账号
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/cloudsql.client"

输出示例

Updated IAM policy for project [cymbal1].
[...]

5. 无服务器 VPC

借助无服务器 VPC 访问通道,您可以从 Cloud Run、App Engine 或 Cloud Functions 等无服务器环境直接连接到 Virtual Private Cloud 网络。通过配置无服务器 VPC 访问通道,您的无服务器环境可以使用内部 DNS 和内部 IP 地址(如 RFC 1918 和 RFC 6598 所定义)向您的 VPC 网络发送请求。对这些请求的响应也会使用您的内部网络。

您将为 Cloud Run 服务创建一个无服务器 VPC 访问通道连接器,以连接到 Cloud SQL。

19c7b05f35789fda

  1. 在 Cloud SQL 实例所在的 VPC 网络中创建一个无服务器 VPC 访问通道连接器。
gcloud compute networks vpc-access connectors create ${SERVERLESS_VPC_CONNECTOR} \
    --region=${REGION} \
    --range=10.8.0.0/28

输出示例

Created connector [cymbalconnector].

6. 部署到 Cloud Run

您将构建 Docker 映像并将其部署到 Cloud Run,然后将 Cloud Run 连接到无服务器 VPC 连接器以访问 Cloud SQL 数据库。

  1. 使用 Maven 编译应用
./mvnw package -DskipTests

输出示例

[...]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  42.864 s
[INFO] Finished at: 2022-04-28T16:15:33Z
[INFO] ------------------------------------------------------------------------
  1. 构建 Docker 映像:
docker build -f src/main/docker/Dockerfile.jvm \
    --tag gcr.io/$PROJECT_NAME/menu-service .

输出示例

[...]
Successfully built 4ef5d7a3befc
Successfully tagged gcr.io/cymbal1/menu-service:latest
  1. 将 Docker 映像推送到 Container Registry:
docker push gcr.io/$PROJECT_NAME/menu-service

输出示例

Using default tag: latest
The push refers to repository [gcr.io/cymbalsql/menu-service]
17b374963800: Pushed
d9a51c06430d: Pushed
fff5d2a2cfc9: Pushed
f21fceb558c6: Pushed
5ffbbbf218dd: Pushed
60609ec85f86: Layer already exists
f2c4302f03b8: Layer already exists
latest: digest: sha256:f64cb7c288dbf4ad9b12bd210c23c5aec1048dee040450ff2d9dbdf96e83a426 size: 1789
  1. 部署菜单服务:
gcloud run deploy $MENU_SERVICE_NAME \
    --image=gcr.io/$PROJECT_NAME/menu-service:latest \
    --region $REGION \
    --allow-unauthenticated \
    --set-env-vars DB_USER=$DB_USER \
    --set-env-vars DB_PASS=$DB_PASSWORD \
    --set-env-vars DB_DATABASE=$DB_DATABASE \
    --set-env-vars DB_HOST=$DB_INSTANCE_IP \
    --vpc-connector $SERVERLESS_VPC_CONNECTOR \
    --project=$PROJECT_ID \
    --quiet

输出示例

[...]
Done.
Service [menu-service] revision [menu-service-00002-xox] has been deployed and is serving 100 percent of traffic.
Service URL: https://menu-service-g2mfphytdq-uk.a.run.app

Google 建议您使用 Secret Manager 来存储 SQL 凭据等敏感信息。您可以使用 Cloud Run 将 Secret 作为环境变量传递或作为卷装载

  1. 商店菜单服务网址:
MENU_SERVICE_URL=$(gcloud run services describe menu-service \
  --platform managed \
  --region $REGION \
  --format=json | jq \
  --raw-output ".status.url")
  1. 验证菜单服务网址
echo $MENU_SERVICE_URL

输出示例

https://menu-service-g2mfphytdq-uk.a.run.app

7. 测试服务

  1. 通过发送 POST 请求创建新菜单项:
curl -X POST "${MENU_SERVICE_URL}/menu" \
  -H 'Content-Type: application/json' \
  -d '{
       "itemImageURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
       "itemName": "Curry Plate",
       "itemPrice": 12.5,
       "itemThumbnailURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
       "spiceLevel": 3,
       "status": "Ready",
       "tagLine": "Spicy touch for your taste buds!!"
   }'

输出示例

{
    "id": 16,
    "createDateTime": "2022-04-28T18:14:04.17225",
    "itemImageURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
    "itemName": "Curry Plate",
    "itemPrice": 12.5,
    "itemThumbnailURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
    "spiceLevel": 3,
    "status": "Processing",
    "tagLine": "Spicy touch for your taste buds!!",
    "updateDateTime": "2022-04-28T18:14:04.172298"
}
  1. 通过发送 PUT 请求来更改菜单项的状态:
curl -X PUT "${MENU_SERVICE_URL}/menu/1" \
  -H 'Content-Type: application/json' \
  -d '{"status": "Ready"}'

输出示例

{
    "id": 1,
    "createDateTime": "2022-04-28T17:21:02.369093",
    "itemImageURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
    "itemName": "Curry Plate",
    "itemPrice": 12.50,
    "itemThumbnailURL": "https://images.unsplash.com/photo-1631452180519-c014fe946bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1587&q=80",
    "spiceLevel": 0,
    "status": "Ready",
    "tagLine": "Spicy touch for your taste buds!!",
    "updateDateTime": "2022-04-28T17:21:02.657636"
}

8. 恭喜!

恭喜,您已完成此 Codelab!

后续步骤:

探索其他 Cymbal Eats Codelab:

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

若要避免产生费用,最简单的方法是删除您为本教程创建的项目。