本教程介绍如何通过一个负责处理身份验证和授权逻辑的代理(而不是客户端凭据)将消息从移动应用或客户端应用发布到 Pub/Sub。
虽然您可以使用 Identity and Access Management (IAM) 对从客户端到 Pub/Sub 的消息进行身份验证,但这些凭据长期有效,永不过期。在客户端应用中,可以通过应用反编译和逆向工程等技术发现这些凭据。
不过,您可将身份验证和授权逻辑分流到一个执行以下任务的代理:
- 对传入请求进行身份验证以验证用户身份。
- 将请求以及适当的 IAM 权限转发到 Pub/Sub。
本教程介绍如何在 Google Kubernetes Engine (GKE) 上实现 Pub/Sub 代理。本教程适用于定义和实现移动应用和客户端应用设计的应用开发者和系统架构师。本文档假定您了解 Kubernetes 基础概念并熟悉 Cloud Endpoints。
本教程的请求流程
要了解 Pub/Sub 如何适用于流式传输流水线,请考虑执行点击流分析。在本用例中,您可能希望了解用户如何与您的移动应用进行交互。要获得这些数据分析,您需要实时捕获用户活动。下图为数据流程图。
应用捕获的数据通过代理推送到 Pub/Sub。Pub/Sub 可以具有下游订阅者(例如 Dataflow 或 Dataproc),他们会聚合数据,便于您可执行有意义的分析。
下图显示了本教程遵循的详细请求流程。
以下部分介绍了该图中各部分的交互方式。
用户身份验证
移动应用可使用各种方法对用户进行身份验证。身份验证流程取决于您的应用。本教程将介绍一种对用户进行身份验证的解决方案。本教程还阐述了该解决方案的实现。
从客户端应用发往 Pub/Sub 代理的请求
应用后端生成一个短期有效的身份验证令牌,客户端将其存储在本地(例如,使用 Android Keystore 系统或 iOS 密钥链服务)。本教程使用 OpenID Connect (OIDC) ID 令牌对客户端应用进行身份验证。OIDC ID 令牌由 Google 颁发和签名。
该客户端应用使用 OIDC ID 令牌向 Pub/Sub 代理发送请求。Pub/Sub 代理验证令牌,并将请求与适当的 IAM 凭据一起转发到 Pub/Sub。
。发布消息
对客户端应用成功进行身份验证后,Pub/Sub 代理会向 Pub/Sub 发送发布请求。通过 IAM,Pub/Sub 可帮助确保调用方(Pub/Sub 代理)具有相应的权限来发送发布请求。在本教程中,Pub/Sub 代理使用 Compute Engine 默认服务账号通过 Pub/Sub 进行身份验证。Compute Engine 默认服务账号具有 Editor IAM 角色 (roles/editor
),可提供对 Pub/Sub 代理的发布商访问权限。
目标
- 创建 GKE 集群以运行 Pub/Sub 代理。
- 创建 Pub/Sub 主题。
- 部署 Pub/Sub 代理。
- 配置 Endpoints,对发送到 Pub/Sub 代理的请求进行身份验证。
- 验证消息是否已发布到 Pub/Sub。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
-
在 Google Cloud Console 中,转到项目选择器页面。
-
选择或创建 Google Cloud 项目。
-
在 Google Cloud 控制台中,激活 Cloud Shell。
Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。
- 定义本教程中所需的环境变量:
export PROJECT=$(gcloud config get-value project) export REGION=us-central1 export ZONE=${REGION}-b export CLUSTER=pubsub-proxy export TOPIC=proxy-test export SERVICE_ACCOUNT=publish-test export ENDPOINTS_SERVICE="pubtest.endpoints.${PROJECT}.cloud.goog" export GENERATE_TOKEN="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts"
- 启用适用于 Cloud Build、Compute Engine、Google Kubernetes Engine、Artifact Analysis、Container Registry、Endpoints、Service Management、Service Control 和 Pub/Sub 的 API:
gcloud services enable \ cloudbuild.googleapis.com \ compute.googleapis.com \ container.googleapis.com \ containeranalysis.googleapis.com \ containerregistry.googleapis.com \ endpoints.googleapis.com \ servicemanagement.googleapis.com \ servicecontrol.googleapis.com \ pubsub.googleapis.com
创建 Pub/Sub 主题
在 Cloud Shell 中,创建一个要在其中发布消息的 Pub/Sub 主题:
gcloud pubsub topics create $TOPIC
创建 GKE 集群
在 Cloud Shell 中创建一个 GKE 集群:
gcloud container clusters create $CLUSTER \ --zone $ZONE \ --scopes "https://www.googleapis.com/auth/cloud-platform"
获取正在运行的集群的凭据:
gcloud container clusters get-credentials $CLUSTER \ --zone $ZONE \ --project $PROJECT
构建容器映像
在 Cloud Shell 中,克隆代码库:
git clone https://github.com/GoogleCloudPlatform/solutions-pubsub-proxy-rest
使用 Cloud Build 根据源构建容器映像,然后将其保存到 Container Registry 中:
cd solutions-pubsub-proxy-rest && \ gcloud builds submit --tag gcr.io/$PROJECT/pubsub-proxy:v1
创建静态外部 IP 地址
在 Cloud Shell 中,创建一个静态外部 IP 地址,该地址稍后会分配给 Pub/Sub 代理负载平衡器:
gcloud compute addresses create service-ip --region $REGION
将静态 IP 地址存储在环境变量
PROXY_IP
中:PROXY_IP=$(gcloud compute addresses describe service-ip \ --region $REGION --format='value(address)')
部署端点
Pub/Sub 代理使用 Endpoints 对用户发出的请求进行身份验证。Endpoints 使用 Extensible Service Proxy (ESP) 提供身份验证、监控、跟踪和 API 生命周期管理等 API 管理功能。本教程仅使用 Endpoints 对发送到 Pub/Sub 代理的传入请求进行身份验证。
在本教程中,您将使用 Pub/Sub 代理将 ESP 部署为辅助信息文件。ESP 拦截传入请求并对其进行身份验证,然后将其转发到 Pub/Sub 代理。
在 Cloud Shell 中,将
[PROJECT_ID]
占位符替换为openapi.yaml
文件中的 Google Cloud 项目 ID:sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" openapi.yaml
在 OpenAPI 清单文件中,将
[IP_ADDRESS]
占位符替换为PROXY_IP
的值:sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" openapi.yaml
将 OpenAPI 服务定义部署到 Endpoints:
gcloud endpoints services deploy openapi.yaml
上述命令会创建以下内容:
- 代管式服务,其名称是您在
openapi.yaml
文件 (pubtest.endpoints.project-id.cloud.goog
) 的主机字段中指定的名称,其中project-id
是您的 Google Cloud 项目的 ID。 - DNS A 记录,它使用
openapi.yaml
文件的x-google-endpoints
扩展程序中定义的服务名称和 Pub/Sub 代理负载平衡器 IP 地址映射。
在部署期间,您会看到一条警告。您可将其忽略,因为本教程使用 OIDC ID 令牌而不是 API 密钥进行身份验证。
WARNING: openapi.yaml: Operation 'post' in path '/publish': Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project. To enable API key all the SecurityRequirement Objects (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object) inside security definition must reference at least one SecurityDefinition of type : 'apiKey'.
- 代管式服务,其名称是您在
检查服务是否已正确部署:
gcloud endpoints services describe ${ENDPOINTS_SERVICE}
输出类似于以下内容:
[...] producerProjectId: project-id serviceConfig: documentation: summary: Pub/Sub proxy exposed as an Endpoint API [...] name: pubtest.endpoints.project-id.cloud.goog title: PubSub Proxy usage: {} serviceName: pubtest.endpoints.project-id.cloud.goog
在输出中:
project-id
:您的 Google Cloud 项目的 ID。
部署代理
在 Cloud Shell 中,生成自签名 SSL 证书以允许通过 HTTPS 连接到代理。
openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 -keyout ./nginx.key \ -out ./nginx.crt \ -subj "/CN=${ENDPOINTS_SERVICE}"
使用 SSL 证书和私钥创建 Kubernetes 密钥:
kubectl create secret generic nginx-ssl \ --from-file=./nginx.crt \ --from-file=./nginx.key
将部署清单文件中的
[PROJECT_ID]
占位符替换为您的 Google Cloud 项目 ID:sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" kube/deployment.yaml
将服务清单文件中的
[IP_ADDRESS]
占位符替换为PROXY_IP
的值:sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" kube/service.yaml
部署代理:
kubectl apply -f kube/
验证部署是否成功:
kubectl rollout status deployment/pubsub-proxy
输出类似于以下内容:
[...] deployment "pubsub-proxy" successfully rolled out
确保两个容器(ESP 和 Pub/Sub 代理)在 Pod 中运行:
kubectl get pods $(kubectl get pod \ -l app=pubsub-proxy \ -o jsonpath="{.items[0].metadata.name}") \ -o jsonpath={.spec.containers[*].name}
输出内容类似如下:
esp pubsub-proxy
观察
EXTERNAL-IP
的值从<pending>
更改为您之前创建的静态外部 IP 地址。kubectl get svc pubsub-proxy -w
输出类似于以下内容:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE pubsub-proxy LoadBalancer 10.7.247.212 <pending> 443:31104/TCP 6m32s pubsub-proxy LoadBalancer 10.7.247.212 <PROXY_IP> 443:31104/TCP 6m5s
要停止观察,请按
CTRL+C
。Pub/Sub 代理成功部署后将在
https://${ENDPOINTS_SERVICE}/publish
中公开。新的 DNS 配置可能需要几分钟的时间才能传播。验证 DNS 配置:
watch nslookup ${ENDPOINTS_SERVICE}
输出类似于以下内容:
Server: 169.254.169.254 Address: 169.254.169.254#53 Non-authoritative answer: Name: pubtest.endpoints.project-id.cloud.goog Address: gke-load-balancer-ip
在输出中:
gke-load-balancer-ip
:您的 GKE 负载平衡器的 IP 地址(代理 IP)。
要停止观察,请按
CTRL+C
。
如果在上述任一步骤遇到错误,请查看问题排查步骤。
生成身份验证令牌
以下身份验证令牌生成流程仅作为演示示例。对于生产环境,您需要一种方法来让用户生成自己的身份验证令牌。例如,您可以在 Identity-Aware Proxy 文档中找到以编程方式获取 OIDC ID 令牌的示例代码。
若要生成身份验证令牌,请执行以下操作:
创建一个要为其生成 OIDC ID 令牌的 Google Cloud 服务账号:
gcloud iam service-accounts create \ $SERVICE_ACCOUNT \ --display-name $SERVICE_ACCOUNT
获取服务账号的电子邮件标识:
SA_EMAIL=${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com
向该服务账号授予 Service Account Token Creator IAM 角色 (
roles/iam.serviceAccountTokenCreator
):gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \ --member user:$(gcloud config get-value account) \ --role roles/iam.serviceAccountTokenCreator
使用 IAM Credentials API 生成 OIDC ID 令牌。
TOKEN=$(curl -s ${GENERATE_TOKEN}/${SA_EMAIL}:generateIdToken \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -d '{"audience": "'${ENDPOINTS_SERVICE}'", "includeEmail": "true"}' | jq -r ".token")
Endpoints 服务名称是在
audience
字段中指定的。audience
声明可识别令牌的目标接收者。验证是否已成功创建令牌:
echo $TOKEN
JSON Web 令牌 (JWT) 如下所示:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjY4NjQyODlm[...].eyJhdWQiOiJwdWJ0ZXN0LmVuZHBvaW50cy52aXR hbC1vY3RhZ29uLTEwOTYxMi5jbG91ZC5nb[...].SjBI4TZjZAlYo6lFKkrvfAcVUp_AJzFKoSsjNbmD_n[...]
使用代理调用 Pub/Sub
在 Cloud Shell 中,发布一条测试消息:
curl -i -k -X POST https://${ENDPOINTS_SERVICE}/publish \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"topic": "'$TOPIC'", "messages": [ {"attributes": {"key1": "value1", "key2" : "value2"}, "data": "test data"}]}'
输出类似于以下内容:
HTTP/2 200 server: nginx date: Sun, 02 Jun 2019 03:53:46 GMT ...
检查消息是否已成功发布到 Pub/Sub 主题:
kubectl logs -f --tail=5 deployment/pubsub-proxy -c pubsub-proxy
Pub/Sub 代理部署日志会显示消息
Successfully published
:2019-06-02 03:49:39.723:INFO:oejs.Server:main: Started @2554ms Jun 02, 2019 3:53:44 AM com.google.pubsub.proxy.publish.PublishMessage getPublisher INFO: Creating new publisher for: proxy-test Jun 02, 2019 3:53:47 AM com.google.pubsub.proxy.publish.PublishMessage$1 onSuccess INFO: Successfully published: 569006136173844
问题排查
在 Cloud Shell 中,检查 Pub/Sub 代理 Pod 中两个容器的状态:
kubectl describe pods $(kubectl get pod -l app=pubsub-proxy \ -o jsonpath="{.items[0].metadata.name}")
在日志输出中,这两个容器的状态为
Running
:[...] Containers: esp: [...] State: Running Started: Fri, 21 Jun 2019 16:41:30 +0530 Ready: True Restart Count: 0 [...] pubsub-proxy: State: Running Started: Fri, 21 Jun 2019 16:41:42 +0530 Ready: True Restart Count: 0 [...]
(可选)查看容器日志,检查是否存在其他错误。例如,要检查 Pub/Sub 代理日志,请运行以下命令:
kubectl logs -f --tail=10 deployment/pubsub-proxy -c pubsub-proxy
若在排查问题时需要帮助,请参阅以下文档:
清除数据
为避免系统因本教程中所用的资源向您的 Google Cloud 账号收费,您可以删除为本教程创建的 Google Cloud 项目,或者删除与本教程相关的资源。
删除 Google Cloud 项目
若要避免产生费用,最简单的方法是删除您为本教程创建的项目。
- 在 Google Cloud 控制台中,进入管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
删除资源
如果您希望保留在本教程中使用的 Google Cloud 项目,请删除单个资源:
在 Cloud Shell 中,删除 GKE 集群:
gcloud container clusters delete $CLUSTER --zone $ZONE --async
删除下载的代码、工件和其他依赖项:
cd .. && rm -rf solutions-pubsub-proxy-rest
删除 Container Registry 中的映像:
gcloud container images list-tags \ gcr.io/$PROJECT/pubsub-proxy \ --format 'value(digest)' | \ xargs -I {} gcloud container images delete \ --force-delete-tags --quiet \ gcr.io/${PROJECT}/pubsub-proxy@sha256:{}
删除 Pub/Sub 主题:
gcloud pubsub topics delete $TOPIC
删除服务账号:
gcloud iam service-accounts delete $SA_EMAIL
删除 Endpoints:
gcloud endpoints services delete ${ENDPOINTS_SERVICE}
删除静态 IP 地址:
gcloud compute addresses delete service-ip --region $REGION
后续步骤
- 了解如何使用 Endpoints 进行身份验证。
- 了解使用 Pub/Sub 处理长时间运行的任务的架构。
- 探索有关 Google Cloud 的参考架构、图表和最佳实践。查看我们的 Cloud Architecture Center。