Google Cloud Run:高效部署与管理容器化应用

1. GitLab 管道测试与部署

在仓库中创建了名为 .gitlab-ci.yml 的文件,其中的 Stages 部分定义了 GitLab 管道的阶段。这里有两个阶段:测试(test)和生产部署(deploy_production)。

测试阶段在运行时会执行以下操作,且必须全部成功才能进入下一阶段,否则管道失败,代码不会部署:
1. apt update
2. apt install -y nodejs npm
3. npm install
4. npm run test

npm run test 会让 GitLab 在运行时执行 package.json 中定义的测试脚本。示例代码中,测试脚本执行 index.test.js

"test": "mocha test/index.test.js --exit"

如果测试失败,GitLab 管道失败;若所有测试通过,GitLab 将部署云函数。

2. 构建 API 网关收集遥测数据
问题

需要一种方法为运行在云函数上的 API 服务收集遥测数据。

解决方案

构建可扩展服务代理 v2 测试版(ESPv2 Beta)作为 API 网关来收集遥测数据。ESPv2 是一个 Envoy 代理,可让 Cloud Endpoints 提供 API 管理功能。具体步骤如下:
1. 记录项目 ID:导航到 Google Cloud 控制台,找到项目信息卡,记录项目 ID 和项目编号。后续步骤中 ESP_PROJECT_ID 即为项目 ID。
2. 记录项目编号:后续步骤中 ESP_PROJECT_NUMBER 即为项目编号。
3. 代码较长,使用 GitHub 仓库: https://github.com/ruiscosta/oreilly-google-cloud-cookbook ,将代码克隆或复制到本地工作站。
4. 在代码仓库根目录部署云函数:

gcloud functions deploy hello-http-function --entry-point hello --runtime nodejs12 --trigger-http
  1. 部署 ESPv2 Beta 到 Cloud Run:
gcloud run deploy CLOUD_RUN_SERVICE_NAME \
--image="gcr.io/endpoints-release/endpoints-runtime-serverless:2" \
--allow-unauthenticated \
--platform managed \
--project=ESP_PROJECT_ID

CLOUD_RUN_SERVICE_NAME 替换为所需名称, ESP_PROJECT_ID 替换为之前记录的项目 ID。
6. 提示选择区域时,选择 us-central1
7. 成功完成后,命令会显示类似以下消息:

Service [esphello] revision [esphello-00001-zuf] has been deployed and is serving 100 percent of traffic at https://esphello-6bc24kwh7a-uc.a.run.app
  1. 此例中, https://esphello-6bc24kwh7a-uc.a.run.app CLOUD_RUN_SERVICE_URL esphello-6bc24kwh7a-uc.a.run.app CLOUD_RUN_HOSTNAME
  2. 记录 CLOUD_RUN_HOSTNAME ,将在 OpenAPI yaml 文件的 host 字段中指定。
  3. 在浏览器中访问 CLOUD_RUN_SERVICE_URL ,可验证 ESPv2 Beta 初始版本是否部署在 Cloud Run 上,会看到缺少环境变量的警告消息,这是预期的。
  4. 使用喜欢的 IDE 打开代码仓库根目录下的 openapi-functions.yaml 文件。
  5. x-google-backend 部分的 Address 字段,将 REGION 替换为函数所在的 Google Cloud 区域, FUNCTIONS_PROJECT_ID 替换为 Google Cloud 项目 ID, FUNCTIONS_NAME 替换为函数名。示例如下:
x-google-backend:
  address: https://us-central1-project.cloudfunctions.net/hello
  protocol: h2
  1. host 字段指定 CLOUD_RUN_HOSTNAME ,即部署 ESPv2 Beta 时 Cloud Run 创建的 URL 的主机名部分。示例如下:
info:
  title: Cloud Endpoints + GCF
  description: Cloud Endpoints with a Google Cloud Functions
  version: 1.0.0
host: esphello-6bc24kwh7a-uc.a.run.app
  1. 部署 Endpoints 配置:
gcloud endpoints services deploy openapi-functions.yaml \
    --project ESP_PROJECT_ID

ESP_PROJECT_ID 替换为自己的项目 ID。
15. 记录 CONFIG_ID ,后续会用到。例如:

Service Configuration [2020-09-05r3] uploaded for service [esphello-6bc24kwh7a-uc.a.run.app]

此例中 CONFIG_ID 2020-09-05-r3
16. 启用 Endpoints 服务:

gcloud services enable ENDPOINTS_SERVICE_NAME
  1. 确定 ENDPOINTS_SERVICE_NAME :前往 Cloud 控制台的 Endpoints 页面。
  2. 构建服务配置到新的 ESPv2 Beta Docker 镜像:
chmod +x gcloud_build_image
./gcloud_build_image -s CLOUD_RUN_HOSTNAME \
    -c CONFIG_ID -p ESP_PROJECT_ID

示例:

chmod +x gcloud_build_image
./gcloud_build_image -s esphello-6bc24kwh7a-uc.a.run.app \
    -c 2020-09-05r3 -p ruicosta-blog
  1. 输出应如下所示:
blog/endpoints-runtime-serverless:2.17.0-esphello-6bc24kwh7a-uc.a.run.app-2020-09-05r3
  1. 记录 “serverless:” 后的完整 URI,将在下一步命令中作为 ESP_VERSION-CLOUD_RUN_HOSTNAME-CONFIG_ID 使用。
  2. 使用新镜像重新部署 ESPv2 Beta Cloud Run 服务:
gcloud run deploy esphello-6bc24kwh7a-uc.a.run.app \
--image="gcr.io/ESP_PROJECT_ID/endpoints-runtime-serverless:ESP_VERSION-CLOUD_RUN_HOSTNAME-CONFIG_ID" \
--allow-unauthenticated \
--platform managed \
--project=ESP_PROJECT_ID

示例:

gcloud run deploy esphello \
--image="gcr.io/ruicosta-blog/endpoints-runtime-serverless:2.17.0-esphello-6bc24kwh7a-uc.a.run.app-2020-09-05r3" \
--allow-unauthenticated \
--platform managed \
--project=ruicosta-blog
  1. 测试部署的云函数与 Cloud Endpoints:
curl --request GET \
   --header "content-type:application/json" \
   "https://YOUR_ENDPOINT_HOST/hello"
  1. 查看函数的遥测数据:在 Endpoints 中,选择 Google Cloud 控制台的 Services 页面。相关遥测数据包括端点请求、端点错误、端点延迟和端点摘要。
3. Google Cloud Run 概述

Google Cloud Run 是一种无服务器计算解决方案,可运行容器化应用,无需管理底层基础设施。它基于开源社区项目 Knative 构建,让开发者专注于应用代码,无需担心计算资源管理。Google Cloud Run 还提供自动扩展、冗余、日志记录和自定义域名等企业运行生产工作负载所需的功能。

Google Cloud 提供两种运行 Cloud Run 的方式:
- Cloud Run(完全托管) :可部署容器,无需担心底层基础设施。
- Cloud Run for Anthos :为应用提供一致的平台,可在云或本地环境运行。由 Knative 驱动,可将运行在 Cloud Run 上的应用部署到 Anthos 管理的平台。

本章主要关注 Cloud Run 完全托管。将学习如何从 Pub/Sub 主题触发 Cloud Run 服务以创建自动化管道,使用自定义域名提升品牌形象,以及进行蓝绿部署,通过在运行不同版本应用的两个 Cloud Run 服务之间引导流量来发布应用。

Google Cloud Run 可使用任意编程语言编写,本章使用 Go 语言。所有代码示例可在相关 GitHub 仓库中找到。运行示例前需满足以下先决条件:
1. 注册 Google Cloud 账户。
2. 创建 Google Cloud 项目。
3. 安装并配置 gcloud
4. 启用 Cloud Functions 和 Cloud Build API:

gcloud services enable run.googleapis.com
gcloud services enable cloudbuild.googleapis.com
gcloud services enable \
    containerregistry.googleapis.com
4. 部署预构建的 Hello World 容器
问题

想要部署第一个 Cloud Run 预构建容器应用,实现简单的 Hello World。

解决方案

利用位于 gcr.io/cloudrun/hello 的 Google Cloud 源仓库中运行的现有容器镜像,部署一个新的 Cloud Run 容器,以响应传入的 Web 请求。具体步骤如下:
1. 打开 Google Cloud 控制台中的 Cloud Run。
2. 点击“创建服务”。
3. 选择区域并输入名称,例如 helloworld
4. 点击“下一步”。
5. 在容器镜像 URL 中输入 gcr.io/cloudrun/hello
6. 点击“下一步”。
7. 身份验证选择“允许未经身份验证的调用”。
8. 点击“创建”。
9. 点击显示的 URL 启动新部署的 Cloud Run 容器,浏览器中应显示成功页面。

此示例虽简单,但展示了部署 Cloud Run 服务的一些基本概念:
- 使用以 gcr.io 引用的现有公共容器。
- 允许未经身份验证的调用,使公共匿名用户可访问应用。
- 使用完全托管环境,抽象所有基础设施,可快速运行应用。

5. 构建自己的 Hello World 容器
问题

想要为应用创建容器并部署到 Google Cloud Run。

解决方案

构建包含应用代码的容器,将其部署到 Google Cloud 容器注册表,最后将新部署的容器部署到 Cloud Run。具体步骤如下:
1. 在本地机器上创建名为 helloworld 的新目录。
2. 创建 go.mod 文件并输入以下代码:

module github.com/GoogleCloudPlatform/golang-samples/run/helloworld
go 1.15

go.mod 文件定义了模块的路径及其依赖要求。
3. 创建 main.go 文件并输入以下代码:

package main

import (
        "fmt"
        "log"
        "net/http"
        "os"
)

func main() {
        log.Print("starting server...")
        http.HandleFunc("/", handler)

        // Determine port for HTTP service.
        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
                log.Printf("defaulting to port %s", port)
        }

        // Start HTTP server.
        log.Printf("listening on port %s", port)
        if err := http.ListenAndServe(":"+port, nil); err != nil {
                log.Fatal(err)
        }
}

func handler(w http.ResponseWriter, r *http.Request) {
        name := os.Getenv("NAME")
        if name == "" {
                name = "World"
        }
        fmt.Fprintf(w, "Hello %s!\n", name)
}
  1. 创建 Dockerfile 文件进行容器化:
FROM golang:1.15-buster as builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
RUN go build -mod=readonly -v -o server
FROM debian:buster-slim
RUN set -x && apt-get update &&
DEBIAN_FRONTEND=noninteractive apt-get install -y \
    ca-certificates && \
    rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/server /app/server
CMD ["/app/server"]
  1. 创建 .dockerignore 文件排除容器中的文件:
# Exclude locally vendored dependencies.
vendor/
# Exclude "build-time" ignore files.
.dockerignore
.gcloudignore

# Exclude git history and configuration.
.gitignore
  1. 使用 Cloud Build 构建容器:
gcloud builds submit --tag gcr.io/PROJECT-ID/helloworld

PROJECT-ID 替换为 Google Cloud 项目 ID。
7. 部署 Cloud Run 服务:

gcloud run deploy --image gcr.io/PROJECT-ID/helloworld --platform managed
  1. 按回车键接受默认名称 helloworld
  2. 选择 Google Cloud 区域,如 us-central1
  3. 提示是否允许未经身份验证的调用时,输入 y

命令成功完成后,将显示 Cloud Run 服务 URL,可在浏览器中打开。此示例中,学会了使用 gcloud 命令部署 Cloud Run 服务,以及构建自己的容器、将其部署到容器注册表并在 Cloud Run 上运行应用。

6. 使用自定义域名运行 Cloud Run
问题

不想使用 Google Cloud 为服务生成的 URL,希望使用自己的域名。

解决方案

使用自己的域名将域名或子域名映射到之前创建的云 Hello World 服务。具体步骤如下:
1. 验证域名所有权:

gcloud domains list-user-verified

若命令无输出,则没有已验证的域名。
2. 验证域名:

gcloud domains verify BASE-DOMAIN

例如:

gcloud domains verify ruicosta.blog

若要映射子域名,需验证根域名。
3. 命令会打开新浏览器窗口,进入 Google Webmaster Central,按说明验证域名。
4. 打开 Google Cloud 控制台中 Cloud Run 服务页面的“管理自定义域名”页面。
5. 点击“添加映射”。
6. 选择要映射的服务。
7. 选择已验证的域名。
8. 可选:选择子域名。
9. 点击“继续”。
10. 在域名注册商(如 GoDaddy、Namecheap 或 Google Domains)更新 DNS 记录,使用上一步显示的 DNS 记录。
11. 在浏览器中访问映射的域名,测试新的自定义域名映射。注意,自定义域名映射可能需要几分钟才能更新。

使用自定义域名映射可将用户引导至使用自己域名运行的 Cloud Run 服务,易读易拼写的域名有助于提升品牌形象。但并非所有区域都支持自定义域名,需查看 Google Cloud Run 文档确认所在区域是否支持。

7. 从 Cloud Pub/Sub 触发 Cloud Run
问题

希望在消息发布到云 Pub/Sub 主题时触发操作。例如,Cloud Pub/Sub 收到应用的新消息,想对该消息执行操作。

解决方案

设置 Cloud Run 监听 Cloud Pub/Sub 中的传入消息。新消息到达时,Cloud Run 可对其执行操作。具体步骤如下:
1. 创建新的 Pub/Sub 主题:

gcloud pubsub topics create chapter-3-4-topic
  1. 在本地机器上创建名为 chapter-3-4 的新目录。
  2. 创建 go.mod 文件并输入以下代码:
module github.com/GoogleCloudPlatform/golang-samples/run/pubsub
go 1.15

go.mod 文件定义了模块的路径及其依赖要求。
4. 创建 main.go 文件并输入以下代码:

package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", HelloPubSub)
    // Determine port for HTTP service.
    port := os.Getenv("PORT")
    if port == "" {
         port = "8080"
         log.Printf("Defaulting to port %s", port)
    }
    // Start HTTP server.
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
         log.Fatal(err)
    }
}

type PubSubMessage struct {
    Message struct {
         Data []byte `json:"data,omitempty"`
         ID   string `json:"id"`
    } `json:"message"`
    Subscription string `json:"subscription"`
}

func HelloPubSub(w http.ResponseWriter, r *http.Request) {
    var m PubSubMessage
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
         log.Printf("ioutil.ReadAll: %v", err)
         http.Error(w, "Bad Request", http.StatusBadRequest)
         return
    }
    if err := json.Unmarshal(body, &m); err != nil {
         log.Printf("json.Unmarshal: %v", err)
         http.Error(w, "Bad Request", http.StatusBadRequest)
         return
    }

    name := string(m.Message.Data)
    if name == "" {
         name = "World"
    }
    log.Printf("Hello %s!", name)
}

返回 HTTP 200 或 204 表示已完全处理 Pub/Sub 消息,错误代码(如 HTTP 400 或 500)表示消息将重试。
5. 创建 Dockerfile 进行容器化:

FROM golang:1.15-buster as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY . ./

# Build the binary.
RUN go build -mod=readonly -v -o server

FROM debian:buster-slim
RUN set -x && apt-get update &&
DEBIAN_FRONTEND=noninteractive apt-get install -y \
    ca-certificates && \
    rm -rf /var/lib/apt/lists/*

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /server

# Run the web service on container startup.
CMD ["/server"]
  1. 构建容器镜像并推送到 Google Cloud 容器注册表:
gcloud builds submit --tag gcr.io/PROJECT_ID/pubsub
  1. 部署 Cloud Run 服务:
gcloud run deploy pubsub --image gcr.io/PROJECT_ID/pubsub --platform managed
- 按回车键接受默认名称 `pubsub`。
- 选择区域,如 `us-central1`。
- 提示是否允许未经身份验证的调用时,输入 `N`,后续会为 Pub/Sub 提供调用者服务账户权限。
  1. 启用 Pub/Sub 服务在项目中生成身份验证令牌:
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member=serviceAccount:service-PROJECT-NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
    --role=roles/iam.serviceAccountTokenCreator

PROJECT_ID 替换为 Google Cloud 项目 ID, PROJECT_NUMBER 替换为项目编号。
9. 创建用于 Pub/Sub 订阅的服务账户:

gcloud iam service-accounts create cloud-run-pubsub-invoker \
    --display-name "Cloud Run Pub/Sub Invoker"
  1. 为新创建的服务账户授予调用 Pub/Sub Cloud Run 服务的权限:
gcloud run services add-iam-policy-binding pubsub \
    --member=serviceAccount:cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com \
    --role=roles/run.invoker

PROJECT_ID 替换为 Google Cloud 项目 ID。
11. 创建 Pub/Sub 订阅:

gcloud pubsub subscriptions create myRunSubscription --topic chapter-3-4-topic \
    --push-endpoint=SERVICE-URL/ \
    --push-auth-service-account=cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com

SERVICE-URL 替换为 Cloud Run 服务 URL, PROJECT_ID 替换为 Google Cloud 项目 ID。
12. 测试新部署的 Cloud Run 服务:

gcloud pubsub topics publish chapter-3-4-topic --message "Test Runner"
  1. 查看 Cloud Run 服务日志:
    • 点击 pubsub 服务。
    • 选择“日志”选项卡。
    • 查找“Hello Test Runner!” 消息。

通过利用 Cloud Pub/Sub,可调用 Cloud Run 服务执行指定操作。Google Cloud Pub/Sub 是应用和服务之间交换消息数据的消息传递服务,通过抽象发送者和接收者,允许不同语言编写的多个应用相互通信。此过程可仅在 Cloud Pub/Sub 收到消息时触发操作,是在特定事件发生时执行后台操作的强大功能。例如,用户订阅服务时发送 SMS 消息,可将 Cloud Pub/Sub 作为中间件,接收用户消息负载,然后使用 Cloud Pub/Sub 订阅者收集消息负载并执行操作(如发送 SMS 确认订阅)。

8. 总结与应用场景分析

8.1 各方案总结
方案名称 核心目标 关键步骤 优势
GitLab 管道测试与部署 确保代码质量并部署云函数 测试阶段执行 apt update npm install 等操作,测试通过后部署 保证代码质量,自动化部署
构建 API 网关收集遥测数据 为 API 服务收集遥测数据 部署 ESPv2 Beta 作为 API 网关,配置相关参数 实现 API 管理和数据收集
部署预构建的 Hello World 容器 快速部署简单应用 利用现有容器镜像创建 Cloud Run 服务 快速上手,展示基本概念
构建自己的 Hello World 容器 自定义应用容器并部署 编写代码、构建容器、部署到 Cloud Run 深入理解容器化和部署流程
使用自定义域名运行 Cloud Run 提升品牌形象 验证域名、更新 DNS 记录 增强品牌辨识度
从 Cloud Pub/Sub 触发 Cloud Run 实现自动化操作 创建主题、订阅,配置权限 实现消息驱动的自动化
8.2 应用场景分析
  • Web 应用开发 :对于快速迭代的 Web 应用,可使用 Cloud Run 快速部署新版本,结合自定义域名提升用户体验和品牌形象。例如,一个电商网站的促销活动页面,可使用 Cloud Run 快速部署,使用自定义域名让用户更容易访问。
  • 数据处理 :当有大量数据需要处理时,可使用 Cloud Pub/Sub 作为消息队列,触发 Cloud Run 服务进行数据处理。例如,处理用户上传的图片或视频,将处理任务发送到 Pub/Sub 主题,由 Cloud Run 服务进行处理。
  • 微服务架构 :在微服务架构中,每个微服务可部署为一个 Cloud Run 服务,利用其自动扩展和无服务器特性,降低运维成本。例如,一个大型电商系统可拆分为用户服务、商品服务、订单服务等多个微服务,分别部署到 Cloud Run 上。

9. 注意事项与常见问题解答

9.1 注意事项
  • 权限管理 :在使用 Cloud Run 和相关服务时,要注意权限的配置。例如,在从 Cloud Pub/Sub 触发 Cloud Run 服务时,要确保 Pub/Sub 服务账户有足够的权限调用 Cloud Run 服务。
  • 资源限制 :虽然 Cloud Run 提供自动扩展功能,但要注意资源的使用限制。例如,每个 Cloud Run 服务有内存和 CPU 的限制,要根据应用的需求合理配置。
  • 域名验证 :在使用自定义域名时,域名验证和 DNS 记录更新可能需要一些时间,要耐心等待。
9.2 常见问题解答
  • :如果 GitLab 管道测试失败,如何排查问题?
    :首先查看测试阶段的日志,确认具体是哪个操作失败。例如,如果 npm run test 失败,检查 package.json 中的测试脚本和 index.test.js 文件是否有错误。
  • :在部署 ESPv2 Beta 时,提示区域不支持怎么办?
    :选择支持的区域,如 us-central1 。不同的服务在不同的区域可能有不同的支持情况,要根据实际情况选择。
  • :使用自定义域名时,DNS 记录更新后仍无法访问怎么办?
    :可能是 DNS 缓存的问题,等待一段时间让缓存更新。也可以检查 DNS 记录是否正确配置。

10. 未来趋势与展望

10.1 技术趋势
  • 更多集成 :未来 Cloud Run 可能会与更多的 Google Cloud 服务和第三方服务进行集成,提供更强大的功能。例如,与机器学习服务集成,实现智能的应用处理。
  • 性能优化 :随着技术的发展,Cloud Run 的性能可能会进一步优化,提供更快的响应时间和更高的吞吐量。
  • 安全增强 :安全是云计算的重要方面,未来 Cloud Run 可能会提供更多的安全功能,如加密传输、访问控制等。
10.2 应用拓展
  • 边缘计算 :结合边缘计算技术,将 Cloud Run 服务部署到离用户更近的边缘节点,降低延迟,提高用户体验。例如,在物联网场景中,将数据处理服务部署到边缘节点,实时处理传感器数据。
  • 混合云场景 :在混合云环境中,Cloud Run for Anthos 可以更好地发挥作用,实现云与本地环境的统一管理和部署。例如,企业可以将部分核心业务部署在本地,将一些弹性业务部署到 Google Cloud 上,通过 Cloud Run for Anthos 进行统一管理。

11. 流程图总结

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{选择方案}:::decision
    B -->|GitLab 管道测试与部署| C(测试阶段操作):::process
    C --> D{测试是否通过}:::decision
    D -->|是| E(部署云函数):::process
    D -->|否| F(排查问题):::process
    B -->|构建 API 网关收集遥测数据| G(记录项目信息):::process
    G --> H(部署 ESPv2 Beta):::process
    H --> I(配置相关参数):::process
    B -->|部署预构建的 Hello World 容器| J(选择容器镜像):::process
    J --> K(创建 Cloud Run 服务):::process
    B -->|构建自己的 Hello World 容器| L(编写代码):::process
    L --> M(构建容器):::process
    M --> N(部署到 Cloud Run):::process
    B -->|使用自定义域名运行 Cloud Run| O(验证域名):::process
    O --> P(更新 DNS 记录):::process
    P --> Q(测试自定义域名):::process
    B -->|从 Cloud Pub/Sub 触发 Cloud Run| R(创建主题和订阅):::process
    R --> S(配置权限):::process
    S --> T(测试服务):::process
    E --> U([结束]):::startend
    F --> U
    I --> U
    K --> U
    N --> U
    Q --> U
    T --> U

这个流程图总结了各个方案的主要步骤和决策点,帮助读者更好地理解整个流程。通过对 Google Cloud Run 及其相关服务的学习和应用,我们可以看到其在现代云计算环境中的强大优势和广泛应用前景。无论是开发者快速部署应用,还是企业构建复杂的系统,Google Cloud Run 都提供了一个高效、灵活的解决方案。希望本文能帮助读者更好地掌握这些技术,在实际项目中发挥更大的作用。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐