这是对课程《在 Cloud Run 上开发无服务器应用》中的“挑战实验” 的 walkthrough。我将向你展示如何完成实验室,并帮助解释相关概念。
本实验主要是关于构建容器镜像,然后将这些镜像部署到 Google Cloud Run 上。但这不只是关于利用服务账户确保后端服务部署到 Cloud Run 后只能被授权的前端 Cloud Run 服务访问。
实验室难度不大。很容易不小心弄混服务账号。大概需要40分钟左右。
Google 提供了一个名为 Google Cloud Skills Boost 的在线学习平台,该平台之前被称为 QwikLabs。你可以跟随相关的培训课程,这些课程与学习路径、特定产品或特定解决方案有关。
在这个平台上有一种学习体验叫做任务。在这里,你需要完成一系列引导性的动手实验,最终完成一个挑战性任务。与其他实验不同,挑战性任务中目标明确,但几乎没有提供如何实现这些目标的指导。
我偶尔写一些这些挑战实验的指南。我的目的不是帮助你作弊通过这些挑战实验,而是希望帮助你更好地理解它们。
如果你需要帮助完成这个挑战实验,那么你找对地方了。但我强烈建议你在继续阅读之前,先自己试着完成这个任务,并自己动手试试这个实验。
有了这么多实验室工具或功能,解决问题的方法总是有很多不同的方法。我通常偏好使用 Cloud Shell 解决这些问题,因为这样我可以记录一个更可重复和程序化的解决步骤。当然,你也可以通过 Cloud 控制台来解决。
Google关键服务及其基础知识简介
这是Google的无服务器、完全托管的容器运行时,适合用于处理无状态的、事件驱动的应用程序,比如响应Web请求或Pub/Sub(发布/订阅模式)事件。还可以用来运行异步任务。
Cloud Run 处理负载均衡和自动扩展,并且你只需为使用的资源付费;即为处理请求的容器实例付费。它可以从 0 快速扩展,这使得 Cloud Run 非常适合处理间歇性任务,并且成本效益相当好。
我们可以将工作负载使用容器镜像(“基于容器的工作流”)或直接从代码使用Buildpacks(“基于源代码的工作流”)部署到Cloud Run。由于Cloud Run服务只是容器的实例化,这意味着我们部署到Cloud Run的任何工作负载都非常灵活;我们可以将我们的工作负载部署到其他容器环境中,例如Kubernetes或GKE。
在Cloud Run中的主要部署单元是服务。一个服务由一个或多个不可变版本组成。当我们更新一个Cloud Run服务时,就会创建一个新的版本。
服务帐户是用于授权应用程序和Google服务使用其他GCP服务,同时也是服务器间交互的标识。服务帐户使用密钥而不是用户密码来进行验证。
服务帐号使用一个独特的电子邮件地址来标识,该地址始终采用以下格式:——<account-name>@<project ID>.gserviceaccount.com——。
一个常见的需求是,一个 Cloud Run 服务需要调用另一 Cloud Run 服务并进行身份验证。在这种场景下,为每个 Cloud Run 服务分配一个专用的服务身份通常是最好的做法。
为每个服务使用单独的账号是最佳做法
我们在部署服务时为其分配一个专用的服务账户,例如:
gcloud run 部署 $MY_SERVICE \ --image gcr.io/$PROJECT_ID/$MY_SERVICE_IMAGE \ --region $REGION \ --service-account=$MY_SERVICE_SA@$PROJECT_ID.iam.gserviceaccount.com \ --no-allow-unauthenticated # 禁止匿名访问
然后我们可以配置接收 Cloud Run 服务来接受来自调用服务的请求,并使其能够拒绝任何未经过身份验证的请求。
强制要求 Cloud Run 服务之间进行认证
我们让调用服务的账号成为接收服务的主体来实现这一点。然后我们给调用服务赋予run.invoker
角色。
gcloud run services add-iam-policy-binding $TARGET_SERVICE \ # 目标服务 --member=serviceAccount:$CALLING_SERVICE_SA@$PROJECT_ID.iam.gserviceaccount.com \ # 服务账户成员 --role=roles/run.invoker \ # 角色: 运行调用者 --region $REGION \ # 地区 --platform managed # 平台托管
这是 Google 容器注册表(GCR) 的后续者。它是一个完全托管的工件管理服务,可以存储和管理各种类型的软件工件。这包括容器镜像和各种语言包,比如 Maven 包、npm 模块、Python 包等。(撰写本文时,该实验仅在使用旧版 GCR 时才能完成。)
Cloud Build 是一款托管的、无服务器的、云原生的 CI/CD 工具。我们可以用 Cloud Build 做许多 CI/CD 相关的工作,比如在上游代码发生变化时自动启动构建流程。这些流程可以执行测试、构建,推送部署包,甚至直接部署到 Google Cloud 上的服务。
在这个实验室中,我们仅使用它最基本的功能:根据本地源代码和 Dockerfile 创建容器镜像,并将镜像推送到 GCR 或 Artifact Registry。
我们的目标是实现这种无服务器架构。
目标体系结构
关于这点的一些备注。
我们将先部署 staging 环境,再部署生产环境。在实际运行中,需要保证服务间的认证安全。
让我们从定义一些在整个过程中可以使用的变量开始。实际的几个变量会在你开始任务时提供给你。
我更喜欢一开始就定义所有需要的变量。不过如果你更喜欢,你也可以根据每个任务的需要去设置它们。
gcloud auth list # the following is the environment variable setup PROJECT_ID=$(gcloud projects list --format='value(PROJECT_ID)' --filter='qwiklabs-gcp') gcloud config set project $PROJECT_ID REGION=<请输入区域> BILLING_IMAGE=billing-staging-api:0.1 # 任务1中提供值 BILLING_SERVICE=<请输入任务1中的值> # 任务1中提供 BILLING_SERVICE_CODE=~/pet-theory/lab07/unit-api-billing # 任务1 FRONTEND_IMAGE=frontend-staging:0.1 # 任务2中提供 FRONTEND_SERVICE=<请输入任务2中的值> # 任务2中提供 FRONTEND_SERVICE_CODE=~/pet-theory/lab07/staging-frontend-billing # 任务2 PRIVATE_BILLING_IMAGE=billing-staging-api:0.2 # 任务3中提供 PRIVATE_BILLING_SERVICE=<请输入任务3中的值> # 任务3中提供 PRIVATE_BILLING_SERVICE_CODE=~/pet-theory/lab07/staging-api-billing # 任务3 BILLING_SERVICE_ACCT=<请输入任务4中的值> # 任务4 BILLING_SERVICE_ACCT_DISPLAY="计费服务Cloud Run" # 任务4 PROD_BILLING_IMAGE=billing-prod-api:0.1 PROD_BILLING_SERVICE=<请输入任务5中的值> # 任务5中提供 PROD_BILLING_SERVICE_CODE=~/pet-theory/lab07/prod-api-billing # 任务5 FRONTEND_SERVICE_ACCT=<请输入任务6中的值> # 任务6. FRONTEND_SERVICE_ACCT_DISPLAY="计费服务Cloud Run调用者" # 任务6 PROD_FRONTEND_SERVICE_IMAGE=frontend-prod:0.1 # 任务7中提供 PROD_FRONTEND_SERVICE=<请输入任务7中的值> # 任务7中提供 PROD_FRONTEND_SERVICE_CODE=~/pet-theory/lab07/prod-frontend-billing # 任务7 gcloud config set run/region REGION gcloud config set run/platform managed # 下载应用程序 git clone https://github.com/rosera/pet-theory.git cd pet-theory/lab07
据要求,我们要为计费服务构建镜像,并将其作为未认证服务部署。(之后我们会让该服务需要认证。)我们被告知使用 Google 容器 Registry,而不是 Artifact Registry。
那么我们使用Cloud Build从克隆的源代码创建镜像文件,并推送到GCR。
# 启用APIs gcloud services enable \ container.googleapis.com (容器服务)\ cloudbuild.googleapis.com (云构建服务)\ run.googleapis.com # 使用 Cloud Build 构建并推送镜像 gcloud builds submit \ --tag gcr.io/$PROJECT_ID/$BILLING_IMAGE $BILLING_SERVICE_CODE (指定项目 ID 和账单镜像)
现在我们可以将应用部署到 Cloud Run。
# 部署到 Cloud Run # 允许未认证的调用 gcloud run deploy $BILLING_SERVICE \ --image gcr.io/$PROJECT_ID/$BILLING_IMAGE \ --region $REGION \ --allow-unauthenticated # 测试这个服务 BILLING_URL=$(gcloud run services describe $BILLING_SERVICE --platform managed --region $REGION --format="value(status.address.url)") curl $BILLING_URL
我们正在寻找像这样的回复:
成功启动。
基本上来说,这与任务一相同,但是图片素材不同。
# 构建前端 gcloud builds submit \ --tag gcr.io/$PROJECT_ID/$FRONTEND_IMAGE $FRONTEND_SERVICE_CODE # 部署到 Cloud Run 服务 gcloud run deploy $FRONTEND_SERVICE \ --image gcr.io/$PROJECT_ID/$FRONTEND_IMAGE \ --region $REGION \ --allow-unauthenticated # 测试前端服务 FRONTEND_SERVICE_URL=$(gcloud run services describe $FRONTEND_SERVICE --platform managed --region $REGION --format="value(status.address.url)") # 使用 curl 请求前端服务的 URL curl $FRONTEND_SERVICE_URL
如果我们用这个浏览器打开这个服务,它看起来会是这样的。
云前端服务
我们将再次部署计费服务。但这次服务需要认证。我们将使用no-allow-unauthenticated
标志。我们只能从被授权的用户调用此服务。
# 注:删除现有的服务 gcloud run services delete $BILLING_SERVICE \ --region $REGION # 注:构建新的镜像 gcloud builds submit \ --tag gcr.io/$PROJECT_ID/$PRIVATE_BILLING_IMAGE $PRIVATE_BILLING_SERVICE_CODE # 注:部署到Cloud Run服务 gcloud run deploy $PRIVATE_BILLING_SERVICE \ --image gcr.io/$PROJECT_ID/$PRIVATE_BILLING_IMAGE \ --region $REGION \ --no-allow-unauthenticated # 注:需要使用BILLING_URL # 这很重要,因为代码使用此环境变量 BILLING_URL=$(gcloud run services describe $PRIVATE_BILLING_SERVICE --platform managed --region $REGION --format="value(status.address.url)") # 注:未认证的调用应该失败 curl -X get $BILLING_URL # 注:认证的调用应该成功 curl -X get -H "Authorization: Bearer $(gcloud auth print-identity-token)" $BILLING_URL
我们需要为私有的计费服务设置一个服务账号。记得,每个服务最好都有自己的服务账号。
# 创建计费服务账户: gcloud iam service-accounts create $BILLING_SERVICE_ACCT \ --display-name "${BILLING_SERVICE_ACCT_DISPLAY}"
我们现在将把计费服务部署到生产环境。这次我们会把服务和新创建的账号关联起来。
# 构建新的镜像文件 gcloud builds submit \ --tag gcr.io/$PROJECT_ID/$PROD_BILLING_IMAGE $PROD_BILLING_SERVICE_CODE # 部署到Cloud Run上 gcloud run deploy $PROD_BILLING_SERVICE \ --image gcr.io/$PROJECT_ID/$PROD_BILLING_IMAGE \ --region $REGION \ --service-account=$BILLING_SERVICE_ACCT@$PROJECT_ID.iam.gserviceaccount.com \ --no-allow-unauthenticated PROD_BILLING_URL=$(gcloud run services describe $PROD_BILLING_SERVICE \ --platform managed \ --region $REGION \ --format "value(status.url)") # 我们需要做一次认证调用 curl -X get -H "Authorization: Bearer \ $(gcloud auth print-identity-token)" \ $PROD_BILLING_URL
注意,你的学生账户实际上没有权限运行私有端点。所以测试调用应该会失败!实验室对此没有提及,这可能会让人感到困惑!不管怎样……这一步现在已经完成。
在这里我们为前端服务创建了一个服务账号。我们将使用这个服务账号使前端服务能够对生产计费服务进行经过验证的调用。
如前所述,我们给调用方(前端)服务账号赋予 run.invoker
角色,并把这个角色绑定到目标(计费)服务。
# 创建前端服务账号 gcloud iam service-accounts create $FRONTEND_SERVICE_ACCT \ --display-name "${FRONTEND_SERVICE_ACCT_DISPLAY}" # 将前端服务账号添加到计费服务的角色中,以便它可以调用该服务 (赋予调用权限) gcloud run services add-iam-policy-binding $PROD_BILLING_SERVICE \ --member=serviceAccount:$FRONTEND_SERVICE_ACCT@$PROJECT_ID.iam.gserviceaccount.com \ --role=roles/run.invoker \ --region $REGION \ --platform managed
我们需要重新配置,使得我们的前端服务现在可以与新的前端服务账号绑定。从而,前端服务就能调用后端计费服务。
# 构建新的 PROD_FRONTEND_SERVICE 镜像 (image) gcloud builds submit \ --tag gcr.io/$PROJECT_ID/$PROD_FRONTEND_SERVICE_IMAGE $PROD_FRONTEND_SERVICE_CODE # 将重新部署到 Cloud Run --平台 managed (管理平台) gcloud run deploy $PROD_FRONTEND_SERVICE \ --image gcr.io/$PROJECT_ID/$PROD_FRONTEND_SERVICE_IMAGE\ --region $REGION \ --service-account=$FRONTEND_SERVICE_ACCT@$PROJECT_ID.iam.gserviceaccount.com \ --allow-unauthenticated PROD_FRONTEND_URL=$(gcloud run services describe $PROD_FRONTEND_SERVICE \ --platform managed \ --region $REGION \ --format "value(status.url)") echo $PROD_FRONTEND_URL (回显 $PROD_FRONTEND_URL)
如果我们接着在浏览器中打开这个URL,我们应该看到这样的东西。
前端提供的私人账单功能
这表明公开的前端服务能够调用私有后端的计费服务。
我们搞定了!耶!
点击关注并订阅