Cloudflare R2 の GraphQL Analytics を Grafana で可視化する

# 目的

Cloudflare では様々なデータを GraphQL API で提供しています。

- [Cloudflare GraphQL API で利用可能なデータセット一覧を取得する - Qiita](https://qiita.com/khayama/items/bbfb7f19eedfdd757a38)

例えば R2 では Storage や Class A, B 操作のメトリクスを Analytics 画面から確認できますが、実態は GraphQL API から取得データを表示しています。

（使われている GraphQL クエリの詳細は、Chrome Developer Tools 等で以下のように中身を確認できます。）

![image-20230216210517265](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216210517265.png)

ただ R2 メトリクスの Analytics 画面だと、バケットごとに1回ずつ確認したり、より詳細なデータが表示されていないことがあります。

今回はアカウント全体のメトリクスやより詳細な内訳を一目でわかるように Grafana で可視化します。

# R2 データセット

- [Cloudflare GraphQL データセットの保管期間を確認する - Qiita](https://qiita.com/khayama/items/4df78c2c8ce24f14d373)

2023年2月16日時点で Beta 提供されている `r2OperationsAdaptiveGroups`, `r2StorageAdaptiveGroups` は 最大で 90 日間を遡ることができ、一度に 30日間 の範囲でデータ取得できます。

```csvs
"r2OperationsAdaptiveGroups"                    "Beta. R2 operations with adaptive sampling"
"r2StorageAdaptiveGroups"                       "Beta. R2 storage with adaptive sampling"
```

# Grafana Cloud アカウント作成

「 https://grafana.com/products/cloud/ 」から無料アカウントを作成します。

![image-20230216172040540](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216172040540.png)

「Forever Free」の枠があるので無料アカウントでも、それなりに使えます。

![image-20230216172103405](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216172103405.png)

# プラグインインストール

「https://grafana.com/grafana/plugins/fifemon-graphql-datasource/ 」から GraphQL Data Source のプラグインを Install plugin します。

Community-created plugins なので、無料プランでも継続して利用できます。（https://grafana.com/pricing/?tab=free）

![image-20230216172734112](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216172734112.png)

Grafana Cloud の画面から GraphQL Data Source の設定に進みます。

![image-20230216173113880](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216173113880.png)

Create a GraphQL Data Source data source から以下のように設定します。

- Name > Cloudflare GraphQL Data Source
- URL > `https://api.cloudflare.com/client/v4/graphql`
- Custom HTTP Headers > `X-AUTH-EMAIL`, `X-AUTH-KEY`

![image-20230216173434257](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216173434257.png)

# ダッシュボードインポート

今回作成したダッシュボードを JSON ファイルにエクスポートしたので、GitHub からダウンロードしてインポートします。

- https://github.com/kyouheicf/cloudflare-graphql-grafana

![image-20230216211829191](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216211829191.png)



先ほど作成した「Cloudflare GraphQL Data Source」を選択してインポートして完了します。

![image-20230216173826095](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216173826095.png)

# Cloudflare-GraphQL-R2 ダッシュボード

`accountTag` としてテキストボックス変数があるので、そこに自分の Cloudflare アカウント ID を入れると R2 の各種メトリクスを表示できます。

アカウント単位で可視化できるので、今月どれぐらいの課金が発生しそうかを把握しながら、移行作業等を進められますね。

![image-20230216174208651](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20230216174208651.png)

# 参考：ベースとした GraphQL クエリ

```graphql:GetR2Metrics
query GetR2Metrics($accountTag: string, $startDate: string) {
  viewer {
    accounts(filter: {accountTag: $accountTag}) {
      storage: r2StorageAdaptiveGroups(orderBy: [bucketName_ASC], limit: 1000, filter: {date_geq: $startDate}) {
        dimensions {
          bucketName
        }
        max {
          metadataSize
          objectCount
          payloadSize
          uploadCount
        }
      }
      classA: r2OperationsAdaptiveGroups(limit: 1000, filter: {date_geq: $startDate, actionStatus: "success", actionType_in: ["ListBuckets", "PutBucket", "ListObjects", "PutObject", "CopyObject", "CompleteMultipartUpload", "CreateMultipartUpload", "UploadPart", "UploadPartCopy", "PutBucketEncryption", "ListMultipartUploads"]}) {
        dimensions {
          actionType
        }
        sum {
          requests
          responseObjectSize
        }
      }
      classB: r2OperationsAdaptiveGroups(limit: 1000, filter: {date_geq: $startDate, actionStatus: "success", actionType_in: ["HeadBucket", "HeadObject", "GetObject", "ReportUsageSummary", "GetBucketEncryption", "GetBucketLocation"]}) {
        dimensions {
          actionType
        }
        sum {
          requests
          responseObjectSize
        }
      }
    }
  }
}
```