使用 Cloud Scheduler 安排 Memorystore for Redis 数据库导出

本教程介绍如何使用 Cloud SchedulerCloud Functions,自动将 Memorystore for Redis 数据库导出到 Cloud Storage。通过将数据库导出到 Cloud Storage,您可以创建可靠且多样的灾难恢复计划。例如,您可以导出到其他地区,并导入到其他 Memorystore for Redis 实例。

架构

本教程涉及以下 Google Cloud 组件:

Cloud Scheduler 作业在 Pub/Sub 主题上发布一条消息,其中包含有关 Memorystore 实例 ID、项目 ID、它所在的地区以及用于存储备份的 Cloud Storage 位置的信息。此事件会触发 Cloud Functions 函数,该函数获取此载荷并通过其 API 在 Memorystore for Redis 上启动数据库导出。数据库会生成导出并将其保存到 Cloud Storage。下图展示了此工作流。

从 Cloud Scheduler 到 Pub/Sub 的工作流,该工作流会触发启动导出的 Cloud Functions 函数。

目标

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。

    转到项目选择器页面

  2. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

  3. 在 Cloud Console 中,激活 Cloud Shell。

    激活 Cloud Shell

  4. 启用 Memorystore for Redis、Cloud Functions、Cloud Scheduler、App Engine API。

    启用 API

在本教程中,您将通过 Cloud Shell 运行所有命令。

设置您的环境

首先,先配置环境,然后创建具有本教程所需权限的自定义角色。

  1. 在 Cloud Shell 中,配置以下环境变量:

    export PROJECT_ID=`gcloud config get-value project`
    export DEMO="mem-exporter"
    export BUCKET_NAME=${USER}-mem-$(date +%s)
    export MEM_INSTANCE="${DEMO}-instance"
    export GCF_NAME="${DEMO}-gcf"
    export PUBSUB_TOPIC="${DEMO}-topic"
    export SCHEDULER_JOB="${DEMO}-job"
    export MEM_ROLE="memExporter"
    export STORAGE_ROLE="simpleStorageRole"
    export REGION="us-central1"
    
  2. 创建两个仅具有本教程所需权限的自定义角色:

    gcloud iam roles create ${STORAGE_ROLE} --project=${PROJECT_ID} \
        --title="Simple Storage Role" \
        --description="Grant permissions to view and create objects in Cloud Storage" \
        --permissions="storage.objects.create,storage.buckets.get"
    
    gcloud iam roles create ${MEM_ROLE} --project=${PROJECT_ID} \
        --title="Memorystore Exporter Role" \
        --description="Grant permissions to export data from a Memorystore instance to a Cloud Storage bucket" \
        --permissions="redis.instances.export"
    

    这些角色可以缩小 Cloud Functions 和 Memorystore 服务帐号的访问权限范围,从而遵循最小权限原则

创建 Cloud Storage 存储分区和 Memorystore 实例

在本部分中,先创建 Cloud Storage 存储分区和 Memorystore for Redis 实例。然后,使用示例数据填充 Memorystore。

创建 Cloud Storage 存储分区

您将使用 gsutil 命令行工具创建 Cloud Storage 存储分区。

  • 在您要保存数据导出的位置创建 Cloud Storage 存储分区:

    gsutil mb -l ${REGION} gs://${BUCKET_NAME}
    

创建 Memorystore 实例并向其服务帐号授予权限

接下来,创建 Memorystore 实例并向其服务帐号授予将数据导出到 Cloud Storage 的权限。

  1. 创建一个 Memorystore for Redis 4 实例:

    gcloud redis instances create ${MEM_INSTANCE} --size=1 --region=${REGION}
    

    此操作需要几分钟时间才能完成。

  2. 验证 Memorystore 实例是否为 READY

    gcloud redis instances list --region=${REGION}
    

    输出类似于以下内容:

    INSTANCE_NAME   VERSION    REGION       TIER   SIZE_GB  HOST          PORT  NETWORK  RESERVED_IP      STATUS  CREATE_TIME
    redis-instance  REDIS_4_0  us-central1  BASIC  1        10.61.20.131  6379  default  10.61.20.128/29  READY   2020-04-23T18:38:54
    
  3. 使用 Simple Storage 角色向您的 Memorystore 服务帐号授予将数据导出到 Cloud Storage 的权限:

    export MEM_SA=$(gcloud redis instances describe ${MEM_INSTANCE} --region ${REGION} \
        --project ${PROJECT_ID} \
        --format "value(persistenceIamIdentity)")
    
    gsutil iam ch ${MEM_SA}:projects/${PROJECT_ID}/roles/${STORAGE_ROLE} gs://${BUCKET_NAME}
    

创建 Pub/Sub 主题、Cloud Functions 函数和 Cloud Scheduler 作业

在本部分中,您将创建一个自定义服务帐号并将其绑定到您创建的自定义 Redis 角色。然后,您将创建一个用于触发 Cloud Functions 函数执行的 Pub/Sub 主题。您还将创建一个 Cloud Scheduler 作业以定期执行数据导出函数。

为 Cloud Functions 函数创建服务帐号

第一步是创建一个自定义服务帐号并将其绑定到您创建的自定义 Redis 角色。

  1. 创建 Cloud Functions 函数要使用的 IAM 服务帐号并将其保存到变量中:

    gcloud iam service-accounts create ${GCF_NAME} \
        --display-name="Service Account for GCF and Memorystore"
    
    export GCF_SA=$(gcloud iam service-accounts list --filter="${GCF_NAME}" --format="value(email)")
    
  2. 向 Cloud Functions 函数服务帐号授予对自定义 Redis 角色的访问权限:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${GCF_SA}" \
        --role="projects/${PROJECT_ID}/roles/${MEM_ROLE}"
    
  3. 向 Cloud Functions 函数服务帐号授予对自定义 Storage 角色的访问权限:

    gsutil iam ch \
        serviceAccount:${GCF_SA}:projects/${PROJECT_ID}/roles/${STORAGE_ROLE} \
        gs://${BUCKET_NAME}
    

创建 Pub/Sub 主题

下一步是创建一个 Pub/Sub 主题,用于触发与 Memorystore 数据库交互的 Cloud Functions 函数。

  • 创建 Pub/Sub 主题:

    gcloud pubsub topics create ${PUBSUB_TOPIC}
    

创建 Cloud Functions 函数

接下来,您将创建 Cloud Functions 函数。

  1. 为 Cloud Functions 函数代码创建文件夹:

    mkdir scheduler_gcf_code && cd scheduler_gcf_code
    
  2. 通过将以下内容粘贴到 Cloud Shell 中来创建 main.py 文件:

    cat <<EOF  > main.py
    
    import base64
    import logging
    import json
    
    from datetime import datetime
    from httplib2 import Http
    
    from googleapiclient import discovery
    from googleapiclient.errors import HttpError
    from oauth2client.client import GoogleCredentials
    
    def main(event, context):
        pubsub_message = json.loads(base64.b64decode(event['data']).decode('utf-8'))
        credentials = GoogleCredentials.get_application_default()
    
        service = discovery.build('redis', 'v1beta1', http=credentials.authorize(Http()), cache_discovery=False)
    
        datestamp = datetime.now().strftime("%Y%m%d%H%M") # format timestamp: YearMonthDayHourMinute
        uri = f"{pubsub_message['gs']}/backup-{datestamp}.rdb"
    
        request_body = {
            "outputConfig": {
                "gcsDestination" : {
                    "uri": uri
                }
            }
        }
    
        try:
            request = service.projects().locations().instances().export(
                name=pubsub_message['name'],
                body=request_body
            )
    
            response = request.execute()
        except HttpError as err:
            logging.error(f"Could NOT run backup. Reason: {err}")
        else:
            logging.info(f"Backup task status: {response}")
    EOF
    
  3. 通过将以下内容粘贴到 Cloud Shell 中来创建 requirements.txt 文件:

    cat <<EOF > requirements.txt
    
    google-api-python-client
    Oauth2client
    EOF
    
  4. 部署代码。如果系统询问您是否要允许对新函数进行未经身份验证的调用,请回答 no

    gcloud functions deploy ${GCF_NAME} \
        --trigger-topic=${PUBSUB_TOPIC} \
        --runtime=python37 \
        --entry-point=main \
        --service-account=${GCF_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
    

创建 Cloud Scheduler 作业

最后,您将创建一个 Cloud Scheduler 作业以定期执行数据导出函数。

  1. 为 Cloud Scheduler 作业创建 App Engine 实例:

    gcloud app create --region=${REGION::-1}
    
  2. 将 Memorystore 完整名称保存到变量中:

    export MEM_NAME=$(gcloud redis instances describe ${MEM_INSTANCE} --region ${REGION} --format "value(name)")
    
  3. 创建一个 Cloud Scheduler 作业以定期执行数据导出函数:

    gcloud scheduler jobs create pubsub ${SCHEDULER_JOB} \
        --schedule='0 23 * * *' --topic=${PUBSUB_TOPIC} \
        --message-body='{"name":'\"${MEM_NAME}\"',"gs":'\"gs://${BUCKET_NAME}\"'}' \
        --time-zone='America/Los_Angeles'
    

    此作业安排在每天晚上 11 点运行。

测试您的解决方案

最后一步是测试您的解决方案。首先,您需要运行 Cloud Scheduler 作业。

  1. 手动运行 Cloud Scheduler 作业以触发数据库的 Memorystore 导出。

    gcloud scheduler jobs run ${SCHEDULER_JOB}
    
  2. 列出在 Memorystore 实例上执行的操作,并验证是否存在类型为 EXPORT 的操作:

    gcloud redis operations list --region=${REGION} --filter="${MEM_INSTANCE}"
    

    输出会显示已完成的导出作业,例如:

    OPERATION_NAME                                           REGION       TYPE    TARGET                 DONE  CREATE_TIME          DURATION
    operation-1592329364987-5a837122a600c-b22c2703-5077c6b7  us-central1  export  mem-exporter-instance  True  2020-06-16T17:42:45  16S
    
  3. 检查 Cloud Storage 存储分区,以查看导出文件是否已创建:

    gsutil ls gs://${BUCKET_NAME} | grep rdb
    

    在上一步的 STATUS 操作返回 DONE 之后,您会看到一个名为 backup-mem-timestamp.rdb 的文件。

清理

您可以按照以下步骤操作,以避免系统因本教程中使用的资源而向您的 Google Cloud 帐号收取费用。为避免支付费用,最简单的方法是删除您为本教程创建的项目。

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤