为第 2 代函数配置直接 VPC 出站流量

借助直接 VPC 出站流量,您可以将 Cloud Run functions 函数(第 2 代)的流量直接路由到 VPC 网络。

限制

准备工作

  • 启用 Cloud Functions API。
  • 安装 Google Cloud CLI,然后通过运行 gcloud init 对其进行初始化。
  • gcloud 组件更新到 545.0.0 版或更高版本:

    gcloud components update
    

  • 如果您的项目中还没有 VPC 网络,请创建一个 VPC 网络

  • 可选:如果您的函数需要使用 Google API 和服务的内部 IP 地址来访问它们,请在您用于直接 VPC 出站流量的子网上启用专用 Google 访问通道。

设置 IAM 权限

如需授权直连 VPC 出站流量,请让您的管理员向函数的服务账号授予 Cloud Run Invoker (roles/run.invoker) 角色。

使用以下方法之一确保 Cloud Run 可以访问 VPC 网络:

  • Cloud Run Service Agent 角色:默认情况下,Cloud Run 服务代理具有 Cloud Run Service Agent 角色 (roles/run.serviceAgent),该角色含有必要的权限。

  • 自定义权限:如需进行更精细的控制,请向 Cloud Run 服务代理授予对项目的以下额外权限:

    • compute.networks.get
    • compute.subnetworks.get
    • 项目或特定子网的 compute.subnetworks.use 权限
    • compute.addresses.get
    • compute.addresses.list
    • compute.addresses.create(仅对具有外部 IPv6 的双栈子网是必需的)
    • compute.addresses.delete(仅对具有外部 IPv6 的双栈子网是必需的)
    • compute.addresses.createInternal
    • compute.addresses.deleteInternal
    • compute.regionOperations.get
  • Compute Network User 角色:如果您不使用默认 Cloud Run Service Agent 角色或自定义权限,请授予 Cloud Run Service Agent 服务账号的 Compute Network User 角色 (roles/compute.networkUser)。具有外部 IPv6 的子网还需要 Compute Public IP Admin 角色 (roles/compute.publicIpAdmin)

    例如,如需授予 Compute Network User 角色,请运行以下命令:

    gcloud projects add-iam-policy-binding PROJECT_ID \
    --member "serviceAccount:service-PROJECT_NUMBER@serverless-robot-prod.iam.gserviceaccount.com" \
    --role "roles/compute.networkUser"

    替换以下内容:

    • PROJECT_ID:您的项目的 ID。
    • PROJECT_NUMBER:您在其中部署 Cloud Run 函数的项目编号。

配置直接 VPC 出站流量

为新的或现有的第 2 代函数配置直接 VPC 出站流量。

gcloud

  1. 如需在部署函数时配置直接 VPC 出站流量,请使用 gcloud beta functions deploy 命令并添加网络设置标志。

    gcloud beta functions deploy FUNCTION_NAME \
        --source . \
        --runtime RUNTIME \
        --trigger-http \
        --region REGION \
        --network=NETWORK \
        --subnet=SUBNET \
        --network-tags=NETWORK_TAG_NAMES \
        --direct-vpc-egress=EGRESS_SETTING
    

    替换以下内容:

    • FUNCTION_NAME:函数的名称。
    • RUNTIME:函数的运行时,例如 nodejs20
    • REGION:部署函数的区域。
    • 可选:将 NETWORK 替换为 VPC 网络的名称。指定 VPC 网络或子网,或同时指定两者。如果您仅指定网络,则子网会使用与网络相同的名称。
    • 可选:将 SUBNET 替换为子网的名称。指定 VPC 网络或子网,或同时指定两者。如果您仅指定网络,则子网会使用与网络相同的名称。您可以在同一子网上部署或执行多个函数。
    • 可选:将 NETWORK_TAG_NAMES 替换为要与函数关联的网络标记的名称。每个函数都可以有不同的网络标记,例如 network-tag-2
    • EGRESS_SETTING 替换为出站流量设置值
      • all:默认值。通过 VPC 网络发送所有出站流量。
      • private-ranges-only:仅发送通过 VPC 网络发送到内部地址的流量。
  2. 可选:如需从函数中移除所有直接 VPC 出站流量设置,请使用 --clear-network--clear-network-tags 标志重新部署函数。

Terraform

如需了解如何应用或移除 Terraform 配置,请参阅基本 Terraform 命令

如需配置直接 VPC 出站流量,请使用 google-beta Terraform 提供程序。

以 Cloud Run functions(第 2 代)直接 VPC 出站流量示例为起点,更新以下字段:

  • service_config.network:您的 VPC 网络的名称。
  • service_config.subnetwork:您的 VPC 子网的名称。
  • service_config.direct_vpc_egress:要将哪些流量发送到 VPC 网络。VPC_EGRESS_ALL_TRAFFIC 通过 VPC 网络发送所有出站流量。VPC_EGRESS_PRIVATE_RANGES_ONLY 仅将流量发送到 VPC 网络的专用 IP 地址范围。

示例:从函数调用内部服务

此示例展示了如何创建内部 Cloud Run 服务,然后通过使用直接 VPC 出站流量的 Cloud Run functions(第 2 代)函数调用该服务。

创建内部后端服务

  1. 为后端服务创建一个新目录,并切换到该目录:

    mkdir backend-service
    cd backend-service
    
  2. 创建一个包含以下内容的 package.json 文件:

    {
        "name": "backend-service",
        "version": "1.0.0",
        "description": "",
        "scripts": {
            "start": "node index.js"
        },
        "dependencies": {
            "express": "^4.18.1"
        }
    }
    
  3. 创建一个包含以下内容的 index.js 文件:

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
        res.send("hello world");
    });
    
    const port = parseInt(process.env.PORT) || 8080;
    app.listen(port, () => {
        console.log(`helloworld: listening on port ${port}`);
    });
    
  4. 将服务部署到 Cloud Run,并使用内部入站流量:

    gcloud run deploy backend \
        --source . \
        --no-allow-unauthenticated \
        --region=REGION \
        --ingress internal
    

    REGION 替换为您的区域,例如 us-west1

  5. 保存新服务的网址。您将在下一部分中用到该地址。

创建和部署函数

  1. 为函数创建新目录,并转到该目录:

    cd ..
    mkdir dvpc-function
    cd dvpc-function
    
  2. 创建一个包含以下内容的 package.json 文件:

    {
      "name": "sample-http",
      "version": "0.0.1",
      "dependencies": {
        "axios": "0.21.1",
        "@google-cloud/functions-framework": "^3.0.0"
      }
    }
    
  3. 创建一个包含以下内容的 index.js 文件。此代码向内部后端服务发出经过身份验证的请求。

    const axios = require('axios');
    const functions = require('@google-cloud/functions-framework');
    
    const callVPCService = async (req, res) => {
      const backendUrl = process.env.BACKEND_URL;
    
      if (!backendUrl) {
        console.error('BACKEND_URL environment variable not set.');
        res.status(500).send('BACKEND_URL not configured.');
        return;
      }
    
      try {
        const metadataServerURL = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
        const tokenUrl = metadataServerURL + backendUrl;
    
        const tokenResponse = await axios.get(tokenUrl, {
          headers: {
            'Metadata-Flavor': 'Google',
          },
        });
        const token = tokenResponse.data;
    
        const response = await axios.get(backendUrl, {
          headers: {
            Authorization: `bearer ${token}`,
          },
    });
    
    res.status(200).send(`Response from backend: ${response.data}`);
      } catch (error) {
        console.error(`Error calling backend service: ${error.message}`);
        res.status(500).send(`Error calling backend: ${error.message}`);
      }
    };
    
    functions.http('callVPCService', callVPCService);
    
  4. 部署配置了直接 VPC 出站流量的函数,以将所有流量路由到您的默认 VPC 网络:

    gcloud beta functions deploy my-2ndgen-function \
      --source . \
      --runtime nodejs20 \
      --trigger-http \
      --entry-point callVPCService \
      --network=default \
      --subnet=default \
      --direct-vpc-egress=all \
      --region=REGION \
      --allow-unauthenticated \
      --set-env-vars BACKEND_URL=BACKEND_URL
    

    替换以下内容:

    • REGION:您部署后端服务的区域。
    • BACKEND_URL:您创建的后端服务的网址。
  5. 函数部署后,访问其网址即可调用该函数。该函数会调用内部后端服务并返回其响应。

后续步骤