【问题标题】:Creating AWS Athena View using Cloud Formation template使用 Cloud Formation 模板创建 AWS Athena View
【发布时间】:2021-01-12 14:34:47
【问题描述】:

是否可以通过 cloudformation 模板创建 Athena 视图。我可以使用 Athena Dashboard 创建视图,但我想使用 CF 模板以编程方式执行此操作。在 AWS 文档中找不到任何详细信息,因此不确定是否支持。

谢谢。

【问题讨论】:

    标签: amazon-web-services amazon-athena


    【解决方案1】:

    通常,CloudFormation 用于以可重复的方式部署基础架构。这不适用于数据库内部的数据,数据库通常单独保存到其他基础设施。

    对于 Amazon Athena,AWS CloudFormation 仅支持:

    • 数据目录
    • 命名查询
    • 工作组

    最接近您要求的是命名查询,它(我认为)可以存储可以创建视图的查询(例如CREATE VIEW...)。

    见:AWS::Athena::NamedQuery - AWS CloudFormation

    更新:@Theo 指出 AWS CloudFormation 还具有 AWS Glue 功能,包括:

    • AWS::Glue::Table

    这显然可以用来创建视图。请参阅下面的 cmets。

    【讨论】:

    • 使用命名查询,我可以存储查询,但如何从 CF 模板中启动/执行它?
    • 我不认为你可以。 CloudFormation 旨在部署基础架构,而不是“数据”。您可以创建一个连接到 Amazon Athena 并创建视图的 AWS Lambda 函数,但我仍然认为这不是 CloudFormation 的巧妙使用,因为它并没有真正“部署”任何东西,而且当堆栈被删除时,我怀疑你这样做了不希望它“破坏”视图。
    • 这个答案不正确。您可以使用 CloudFormation 创建 Athena 数据库和表,它们恰好位于 Glue 命名空间中,因为 Athena 使用 Glue 数据目录来处理此类事情。例如,请参阅AWS::Glue::Table。视图只是表格的一种特殊变体,也可以使用 CloudFormation 创建,尽管在实践中非常困难。
    【解决方案2】:

    可以使用 CloudFormation 创建视图,只是非常、非常、复杂。 Athena 视图存储在 Glue 数据目录中,就像数据库和表一样。实际上,Athena 视图是 Glue Data Catalog 中的表,只是内容略有不同。

    有关如何以编程方式创建视图的完整说明,请参阅此答案,您将对复杂性有所了解:Create AWS Athena view programmatically – 可以将其映射到 CloudFormation,但我不建议这样做。

    如果要使用 CloudFormation 创建数据库和表,资源为 AWS::Glue::Database 和 AWS::Glue::Table。

    【讨论】:

      【解决方案3】:

      我认为目前从 CloudFormation 模板创建 Athena 视图的最佳方法是使用自定义资源和 Lambda。我们必须提供视图创建和删除的方法。例如,使用 crhelper 库 Lambda 可以定义:

      from __future__ import print_function
      from crhelper import CfnResource
      import logging
      import os
      import boto3
      
      logger = logging.getLogger(__name__)
      helper = CfnResource(json_logging=False, log_level='DEBUG', boto_level='CRITICAL', sleep_on_delete=120)
      
      try:
          client = boto3.client('athena')
          ATHENA_WORKGROUP = os.environ['athena_workgroup']
          DATABASE = os.environ['database']
          QUERY_CREATE = os.environ['query_create']
          QUERY_DROP = os.environ['query_drop']
      except Exception as e:
          helper.init_failure(e)
      
      @helper.create
      @helper.update
      def create(event, context):
          logger.info("View creation started")
      
          try:
              executionResponse = client.start_query_execution(
                  QueryString=QUERY_CREATE,
                  QueryExecutionContext={'Database': DATABASE},
                  WorkGroup='AudienceAthenaWorkgroup'
              )
              logger.info(executionResponse)
      
              response = client.get_query_execution(QueryExecutionId=executionResponse['QueryExecutionId'])
              logger.info(response)
      
              if response['QueryExecution']['Status']['State'] == 'FAILED':
                  logger.error("Query failed")
                  raise ValueError("Query failed")
      
              helper.Data['success'] = True
              helper.Data['id'] = executionResponse['QueryExecutionId']
              helper.Data['message'] = 'query is running'
      
          except Exception as e:
              print(f"An exception occurred: {e}")
      
          if not helper.Data.get("success"):
              raise ValueError("Creating custom resource failed.")
      
          return
      
      
      @helper.delete
      def delete(event, context):
          logger.info("View deletion started")
      
          try:
              executionResponse = client.start_query_execution(
                  QueryString=QUERY_DROP,
                  QueryExecutionContext={'Database': DATABASE},
                  WorkGroup='AudienceAthenaWorkgroup'
              )
              logger.info(executionResponse)
      
          except Exception as e:
              print("An exception occurred")
              print(e)
      
      @helper.poll_create
      def poll_create(event, context):
          logger.info("Pol creation")
      
          response = client.get_query_execution(QueryExecutionId=event['CrHelperData']['id'])
      
          logger.info(f"Poll response: {response}")
      
          # There are 3 types of state of query
          # if state is failed - we stop and fail creation
          # if state is queued - we continue polling in 2 minutes
          # if state is succeeded - we stop and succeed creation
          if 'FAILED' == response['QueryExecution']['Status']['State']:
              logger.error("Query failed")
              raise ValueError("Query failed")
      
          if 'SUCCEEDED' == response['QueryExecution']['Status']['State']:
              logger.error("Query SUCCEEDED")
              return True
      
          if 'QUEUED' == response['QueryExecution']['Status']['State']:
              logger.error("Query QUEUED")
              return False
      
          # Return a resource id or True to indicate that creation is complete. if True is returned an id
          # will be generated
          # Return false to indicate that creation is not complete and we need to poll again
          return False
      
      def handler(event, context):
          helper(event, context)
      

      用于视图创建/更新/删除的 Athena 查询作为环境参数传递给 Lambda。 在CloudFormation 模板中,我们必须定义调用提到的Python 代码并创建/更新/删除Athena 视图的Lambda。例如

        AthenaCommonViewLambda:
          Type: 'AWS::Lambda::Function'
          DependsOn: [CreateAthenaViewLayer, CreateAthenaViewLambdaRole]
          Properties:
            Environment:
              Variables:
                athena_workgroup: !Ref AudienceAthenaWorkgroup
                database:
                  Ref: DatabaseName
                query_create: !Sub >-
                  CREATE OR REPLACE VIEW ${TableName}_view AS
                  SELECT field1, field2, ...
                  FROM ${DatabaseName}.${TableName}
                query_drop: !Sub DROP VIEW IF EXISTS ${TableName}_common_view
            Code:
              S3Bucket: !Ref SourceS3Bucket
              S3Key: createview.zip
            FunctionName: !Sub '${AWS::StackName}_create_common_view'
            Handler: createview.handler
            MemorySize: 128
            Role: !GetAtt CreateAthenaViewLambdaRole.Arn
            Runtime: python3.8
            Timeout: 60
            Layers:
              - !Ref CreateAthenaViewLayer
      
        AthenaCommonView:
          Type: 'Custom::AthenaCommonView'
          Properties:
            ServiceToken: !GetAtt AthenaCommonViewLambda.Arn
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-07
        • 2014-08-06
        • 2018-09-15
        • 1970-01-01
        • 2019-11-29
        • 1970-01-01
        • 2018-03-05
        • 1970-01-01
        相关资源
        最近更新 更多