QuickSightのSPICEデータ使用量をLambdaで監視している話

目次

はじめに

こんにちは、スタメンの田中、若園です。

こちらの記事でカスタムダッシュボード機能の全体像を紹介しました。🎉

この記事では、続編その2としてカスタムダッシュボード機能におけるLambdaとCloudWatchを活用したマルチテナント別のSPICEデータ使用量の監視方法と実装について紹介していきます。

使用量監視の課題

名前空間を用いたQuickSightのマルチテナント環境を運用するに当たり、SPICEの使用量に関して以下の課題がありました。

  • ①名前空間別にSPICEの使用量を把握したい。
    • 1つのAWSアカウントのQuickSightでは、SPICEは全名前空間で共通して使用されるため、QuickSightコンソールや既存のAPIでは、名前空間別のSPICE使用量を取得することができません。
  • ②意図せず大容量のデータセットが作成され、特定の名前空間でSPICE容量を大幅に専有された際に気づくことができるようにしたい。

そこで、上記の課題が解決でき、SPICE使用量が無料枠内に収まっているかを監視できる仕組みを実装しました。 (ちなみに、QuickSightでは、AUTHORアカウント1人につき、SPICEの10GB分が無料枠*1として付与されます。)

LambdaとCloudWatchを用いた監視方法

前述の課題の解決策として、下図の構成で監視の仕組みを実装しました。

  • EventBridgeによる定期実行
    • ルールを設定し、毎日6時間毎にLambda関数を起動しています。
  • 名前空間別にSPICE使用量を集計してCloudWatchにメトリクスを送信するLambda関数の実装
    • ListNamespaces APIとListDataSets APIを用いて、全名前空間および全データセットの情報を取得します。
    • DescribeDataSet APIとDescribeDataSetPermissions APIを使用し各データセット毎のSPICE容量と属している名前空間を確認します。
    • ListUsers APIを用いてAUTHORアカウント数を取得します。
    • 上記で取得した情報から名前空間別にSPICE使用量を集計、AUTHOR1人あたりのSPICE使用量を算出し、CloudWatchにメトリクスを送信します。
  • CloudWatchアラートの設定・ダッシュボードの作成
    • 閾値を超えた場合のアラートの設定とダッシュボードでの見える化を行っています。

名前空間別にSPICE使用量を集計してCloudWatchにメトリクスを送信するLambda関数

Lambda関数のコードを記載しておきます。

require 'json'
require 'aws-sdk'

def handler(event: nil, context: nil)
  @quicksight_client = Aws::QuickSight::Client.new(region: ENV['REGION'])
  @cloudwatch_client = Aws::CloudWatch::Client.new(region: ENV['REGION'])
  
  # 名前空間の一覧を取得
  list_namespaces = get_list_namespaces
  # データセット一覧を取得
  list_data_sets = get_list_data_sets

  data_sets_per_company = {}
  
  list_namespaces.each do |namespace|
    # 名前空間は company-#{company_id}の形式で作成している
    company_id = match[0].match(/\d+/)[0]
    data_sets_per_company[company_id] = []
  end

  # company_idをkeyとして、企業毎のデータセットの一覧を配列で持つ、Hashを作成する
  # DescribeDataSet APIでデータセット毎の使用量を取得することが出来る
  data_sets_per_company = classification_per_company(list_data_sets, data_sets_per_company)

  data_sets_per_company.keys.each do |company_id|
    total_bytes = data_sets_per_company[company_id].map do |data_set|
      data_set[:consumed_spice_capacity_in_bytes]
    end.sum

    total_gigabytes = convert_gigabytes(total_bytes)

    author_count = get_author_count("company-#{company_id}")

    put_metric_data(company_id, total_gigabytes, author_count)
  end
end

def classification_per_company(list_data_sets, data_sets_per_company)
  list_data_sets.each do |data_set|
    response_data_set = @quicksight_client.describe_data_set(
      aws_account_id: ENV['AWS_ACCOUNT_ID'],
      data_set_id: data_set.data_set_id
    ).data_set

    response_data_set_permissions = @quicksight_client.describe_data_set_permissions(
      aws_account_id: ENV['AWS_ACCOUNT_ID'],
      data_set_id: data_set.data_set_id
    ).permissions

    next if response_data_set_permissions.empty?

    # principal: arn:aws:quicksight:{region}:{aws_account_id}:user/#{namespace}/user_nameの形式で取得できる
    namespace = response_data_set_permissions[0].principal.split('/')[1]

    data_set_hash = {
      data_set_id: response_data_set.data_set_id,
      data_set_name: response_data_set.name,
      consumed_spice_capacity_in_bytes: response_data_set.consumed_spice_capacity_in_bytes
    }

    company_id = namespace.match(/\d+/)[0]

    data_sets_per_company[company_id] << data_set_hash
  end

  data_sets_per_company
end

def get_author_count(namespace)
  response_list_data_sets = @quicksight_client.list_users(
    aws_account_id: ENV['AWS_ACCOUNT_ID'],
    namespace: namespace
  )

  response_list_data_sets.user_list.count { |user| user.role == 'AUTHOR' || user.role == 'ADMIN' }
end

def convert_gigabytes(bytes)
  bytes.fdiv(1024 * 1024 * 1024)
end

def put_metric_data(company_id, total_gigabytes, author_count)
  data = {
    namespace: 'tunag/quicksight',
    metric_data: [
      {
        metric_name: 'spice_usage',
        value: total_gigabytes,
        unit: 'Gigabytes',
        dimensions: [
          { name: 'company_id', value: company_id }
        ]
      }
    ]
  }

  data[:metric_data] << {
    metric_name: 'spice_usage_per_author',
    value: total_gigabytes.fdiv(author_count),
    unit: 'Gigabytes',
    dimensions: [
      { name: 'company_id', value: company_id }
    ]
  }

  @cloudwatch_client.put_metric_data(data)
end

CloudWatchアラート、ダッシュボードの作成

下図のようにアラートとダッシュボードを作成しました。

アラート

ダッシュボード

まとめ

以上が、LambdaとCloudWatchを活用したマルチテナント別にSPICEデータ使用量の監視方法の内容となります。 QuickSightを利用していて運用を効率化したいと考えている方にとって、少しでも参考になれば幸いです。

エンジニアとして、やりがいのある仕事がしたい!わくわくする仕事をしたい!という方がいらっしゃいましたらぜひ、弊社採用窓口までご連絡ください。

弊社開発部門では、開発体制や開発の流れ、採用している技術をエンジニアリングハンドブックにまとめております。ご興味がある方は下記のリンクからぜひご覧ください。

stmn, inc. Engineering Handbook