使用服务帐号向 Google Cloud 验证身份

本教程演示了如何创建 Google Cloud 服务帐号、如何分配角色以向 Google Cloud 服务进行身份验证,以及如何在 Google Kubernetes Engine (GKE) 上运行的应用中使用服务帐号凭据。

本示例使用的是 Pub/Sub,但这些说明可适用于任何 Google Cloud 服务。本教程中的示例应用使用服务帐号Pub/Sub 进行身份验证,并订阅从基于 Python 的应用发布到 Pub/Sub 主题的消息。

目标

本教程介绍以下步骤:

  • 如何创建服务帐号
  • 如何为服务帐号分配必要的角色以使用 Pub/Sub
  • 如何将帐号密钥保存为 Kubernetes Secret
  • 如何使用服务帐号配置和部署应用

本教程中使用的示例应用会订阅 Pub/Sub 主题,并将发布的消息输出到标准输出。您必须为应用配置正确的权限、使用 gcloud command-line tool 发布消息并检查容器的输出流,观察能否正确接收消息。

使用服务帐号进行身份验证

您可以使用 Workload Identity、默认 Compute Engine 服务帐号或 Secret 从 GKE 中通过服务帐号向 Google Cloud 服务进行身份验证。

使用 Workload Identity

Workload Identity 是从 GKE 向 Google Cloud 服务进行身份验证的推荐方法。Workload Identity 让您可以使用 Kubernetes 资源配置 Google Cloud 服务帐号。如果这种身份验证方法符合您的使用场景,则应首选它。此示例还涵盖不适合使用 Workload Identity 的用例。

使用默认 Compute Engine 服务帐号

GKE 集群中的每个节点都是一个 Compute Engine 实例。因此,默认情况下,在 GKE 集群上运行的应用将会尝试使用“Compute Engine 默认服务帐号”进行身份验证,并继承关联的范围。

此默认服务帐号可能没有权限使用您需要的 Google Cloud 服务。您可以扩展默认服务帐号的范围,但这样做可能会带来安全风险,因此不建议这样做。

使用 Secret 管理服务帐号凭据

您可以为应用创建服务帐号,并注入身份验证密钥作为 Kubernetes Secret。此教程主要介绍本主题。

为何使用服务帐号?

为不同应用使用单独的服务帐号具有以下优势:

  • 更好地了解和审核您的应用所发出的 API 请求。

  • 可以撤消特定应用的密钥,而非共享服务帐号,同时不必撤消所有应用的 API 访问权限。

  • 在存在潜在的安全事件的情况下,降低服务帐号凭据被盗用的风险。

准备工作

请按照以下步骤启用 Kubernetes Engine API:
  1. 访问 Google Cloud Console 中的 Kubernetes Engine 页面
  2. 创建或选择项目。
  3. 稍作等待,让 API 和相关服务完成启用过程。 此过程可能耗时几分钟。
  4. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

安装本教程中使用的以下命令行工具:

  • gcloud 用于创建和删除 Kubernetes Engine 集群。gcloud 包含在 Google Cloud SDK 中。
  • kubectl 用于管理 Kubernetes(即 Kubernetes Engine 使用的集群编排系统)。您可以使用 gcloud 安装 kubectl
    gcloud components install kubectl

从 GitHub 克隆示例代码:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
cd kubernetes-engine-samples/cloud-pubsub/deployment

gcloud 命令行工具设置默认值

如需节省在 gcloud 命令行工具中输入项目 IDCompute Engine 地区选项的时间,您可以设置以下默认值:
gcloud config set project project-id
gcloud config set compute/zone compute-zone

启用 API

在本教程中,请在项目上启用 Pub/Sub API 和 Resource Manager API:

gcloud services enable cloudresourcemanager.googleapis.com pubsub.googleapis.com

创建容器集群

创建一个名为 pubsub-test 的容器集群以部署 Pub/Sub 订阅者应用:

gcloud container clusters create pubsub-test

创建 Pub/Sub 主题

Pub/Sub 订阅者应用使用名为 echo 的 Pub/Sub 主题上名为 echo-read 的订阅。请在部署该应用之前创建这些资源。

首先,创建一个 Pub/Sub 主题:

gcloud

gcloud pubsub topics create echo

配置连接器

注意:此步骤需要使用配置连接器。按照安装说明在您的集群上安装配置连接器。

apiVersion: pubsub.cnrm.cloud.google.com/v1beta1
kind: PubSubTopic
metadata:
  name: echo
如需部署此清单,请将它以 topic.yaml 的形式下载到您的机器上,然后运行以下命令:
kubectl apply -f topic.yaml

然后,创建一个订阅:

gcloud

gcloud pubsub subscriptions create echo-read --topic=echo

Config Connector

apiVersion: pubsub.cnrm.cloud.google.com/v1beta1
kind: PubSubSubscription
metadata:
  name: echo-read
spec:
  topicRef:
    name: echo
如需部署此清单,请将它以 subscription.yaml 的形式下载到您的机器上,然后运行以下命令:
kubectl apply -f subscription.yaml

部署 Pub/Sub 订阅者应用

接下来,部署应用容器以检索发布到 Pub/Sub 主题的消息。此应用是使用 Google Cloud Pub/Sub 客户端库以 Python 语言编写的。您可以在 GitHub 上找到源代码。

以下清单文件展示了一个运行此应用的单个 Docker 映像实例的 Deployment

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pubsub
spec:
  selector:
    matchLabels:
      app: pubsub
  template:
    metadata:
      labels:
        app: pubsub
    spec:
      containers:
      - name: subscriber
        image: gcr.io/google-samples/pubsub-sample:v1

如需部署此清单,请运行以下命令:

kubectl apply -f pubsub.yaml

部署该应用后,运行以下命令来查询 Pod:

kubectl get pods -l app=pubsub
输出:
NAME                      READY     STATUS             RESTARTS   AGE
pubsub-2009462906-1l6bh   0/1       CrashLoopBackOff   1          30s

您会看到,容器无法启动并进入 CrashLoopBackOff 状态。运行以下命令来检查 Pod 中的日志:

kubectl logs -l app=pubsub

输出:

...
google.gax.errors.RetryError: RetryError(Exception occurred in retry method that
was not classified as transient, caused by <_Rendezvous (StatusCode.PERMISSION_DENIED, scopes.) of RPC that terminated with Request had insufficient authentication>)

堆栈轨迹和错误消息表示该应用没有查询 Pub/Sub 服务的权限。

创建服务帐号凭据

如需使 GKE 上运行的应用能够访问 Google Cloud 服务,请使用服务帐号。借助服务帐号,您可以定义一组与您的应用关联的 Identity and Access Management (IAM) 权限。

控制台

要创建服务帐号,请执行以下操作:

  1. 转到 Cloud Console 上的服务帐号

    转到“服务帐号”

  2. 点击 创建服务帐号

  3. 服务帐号详情下,输入服务帐号名称(例如 pubsub-app)。

  4. (可选)修改服务帐号 ID 并添加说明。

  5. 点击创建

  6. 向此服务帐号授予对项目的访问权限 (Grant this service account access to a project) 下的选择角色下拉列表中选择 Pub/Sub Subscriber

  7. 点击继续,然后点击完成以创建服务帐号。

  8. 在服务帐号列表中,点击您创建的服务帐号旁边的 操作 > 管理密钥

  9. 点击添加密钥 > 创建新密钥

  10. 密钥类型下,选择 JSON

  11. 点击创建

创建密钥后,包含服务帐号凭据的 JSON 文件会下载到您的计算机。您需要使用此密钥文件来配置应用,使其向 Pub/Sub API 进行身份验证。

配置连接器

首先,将以下资源下载为 service-account.yaml。

apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMServiceAccount
metadata:
  name: pubsub-app
spec:
  displayName: Service account for PubSub example

然后运行以下命令:

kubectl apply -f service-account.yaml

接下来,为服务帐号应用“Pub/Sub Subscriber”角色。将以下资源下载为 service-account-policy.yaml。请将 [PROJECT_ID] 替换为您的项目 ID。

apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMPolicyMember
metadata:
  name: policy-member-binding
spec:
  member: serviceAccount:pubsub-app@[PROJECT_ID].iam.gserviceaccount.com
  role: roles/pubsub.subscriber
  resourceRef:
    apiVersion: resourcemanager.cnrm.cloud.google.com/v1beta1
    kind: Project
    external: projects/[PROJECT_ID]

然后运行以下命令:

kubectl apply -f service-account-policy.yaml

导入凭据作为 Secret

现在您已经拥有服务帐号密钥,您需要一种将其加载到容器中的方法。您首先想到的可能是在 Dockerfile 中添加一个步骤,但服务帐号密钥是安全敏感文件,不应保存到容器映像中。

然而,Kubernetes 提供的 Secret 资源类型可以在运行时期间在 Pod 内安全地装载私有文件。

kubectl

如需将 JSON 密钥文件保存为名为 pubsub-key 的 Secret,请运行以下命令,其中的路径指向所下载的服务帐号凭据文件:

kubectl create secret generic pubsub-key --from-file=key.json=PATH-TO-KEY-FILE.json

此命令将创建一个名为 pubsub-key 的 Secret,其中包含一个 key.json 文件,该文件包含您从 Cloud Console 下载的私钥的内容。创建 Secret 后,请从您的计算机中移除密钥文件。

Config Connector

将以下资源下载为 service-account-key.yaml。

apiVersion: iam.cnrm.cloud.google.com/v1beta1
kind: IAMServiceAccountKey
metadata:
  name: pubsub-key
spec:
  publicKeyType: TYPE_X509_PEM_FILE
  keyAlgorithm: KEY_ALG_RSA_2048
  privateKeyType: TYPE_GOOGLE_CREDENTIALS_FILE
  serviceAccountRef:
    name: pubsub-app

然后运行以下命令:

kubectl apply -f service-account-key.yaml

使用 Secret 配置应用

如需在应用中使用 pubsub-key Secret,请将 Deployment 规范修改为:

  1. 使用此 Secret 定义一个卷。
  2. 将 Secret 卷装载到应用容器上。
  3. 设置 GOOGLE_APPLICATION_CREDENTIALS 环境变量,使其指向 Secret 卷装载中的密钥文件。

更新后的清单文件如下所示:

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pubsub
spec:
  selector:
    matchLabels:
      app: pubsub
  template:
    metadata:
      labels:
        app: pubsub
    spec:
      volumes:
      - name: google-cloud-key
        secret:
          secretName: pubsub-key
      containers:
      - name: subscriber
        image: gcr.io/google-samples/pubsub-sample:v1
        volumeMounts:
        - name: google-cloud-key
          mountPath: /var/secrets/google
        env:
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /var/secrets/google/key.json

此清单文件定义了以下字段,旨在确保凭据可供该应用使用:

  • 一个名为 google-cloud-key 卷,该卷使用名为 pubsub-key 的 Secret。

  • 一个卷装载,用于使 google-cloud-key 在容器内的 /var/secrets/google 目录中可用。

  • GOOGLE_APPLICATION_CREDENTIALS 环境变量(设置为 /var/secrets/google/key.json);当 Secret 作为卷装载到容器上后,该变量将包含凭据文件。

请注意,Google Cloud 客户端库(在本示例中为 Python 版 Pub/Sub 客户端)会自动识别 GOOGLE_APPLICATION_CREDENTIALS 环境变量。

如需部署此清单,请运行以下命令:

kubectl apply -f pubsub-with-secret.yaml

验证 Pod 状态是否为 Running

kubectl get pods -l app=pubsub
输出:
NAME                     READY     STATUS    RESTARTS   AGE
pubsub-652482369-2d6h2   1/1       Running   0          29m

测试 Pub/Sub 消息的接收情况

配置完应用后,您现在可以将消息发布到名为 echo 的 Pub/Sub 主题了:

gcloud pubsub topics publish echo --message="Hello, world!"

该应用会在几秒钟内获取消息并将其输出到输出流。要检查已部署 Pod 中的日志,请运行以下命令:

kubectl logs -l app=pubsub
输出:
Pulling messages from Pub/Sub subscription...
[2017-06-19 12:31:42.501123] ID=130941112144812 Data=Hello, world!

您已成功将 GKE 上的应用配置为使用服务帐号凭据向 Pub/Sub API 进行身份验证!

清理

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

  1. 清理 Pub/Sub 订阅和主题:

    gcloud pubsub subscriptions delete echo-read
    gcloud pubsub topics delete echo
  2. 删除容器集群:

    gcloud container clusters delete pubsub-test

后续步骤