Gradleには標準でS3やCloud Storageをmavenリポジトリとして扱う機能がついているのですが、Google Cloudのライブラリが古すぎて認証に一部問題があるようです。
GitHubからOIDCを使って接続しようと思ったら古いライブラリを使っているせいで認証できませんでした。
Google CloudならArtifact Registry使えばいいのですが、無料枠が0.5GBまでで転送料もかかることからもっと安く済む手段を探します。
マネージドなサービスであることの良さはもちろんありますが、今回はよりコストを抑えることを目的としています。
ということでCloudflare R2をmavenリポジトリとして運用していきます。
今回検証に利用したコードは以下のリポジトリにあります。
Cloudflare R2とは
Cloudflareの公開しているS3互換のあるオブジェクトストレージです。
S3などと比べて無料枠が大きく、比較的コストの低いストレージになります。
S3互換であるため既存のAWS SDKから操作ができます。R2専用のライブラリを使用しなくてもよい部分も魅力的です。
課金は使用したストレージ容量と書き込みアクセス、読み取りアクセスの回数でなされます。
環境
- Java 17
- Gradle 8.4
- Terraform v1.6.4
事前準備
Cloudflareのアカウントを用意し、課金の設定をしてください。
無料枠の範囲内で使用していれば課金されることはありませんが、R2を使用するためには設定が必要です。
R2 Bucketの作成
Account IDとTokenの確認
まずR2にBucketを作成するためのAccount IDの確認とTokenの作成をします。
R2のページにアクセスして右側にAccount IDがあるのでメモしておきます。
その下にあるManage R2 API TokenからTokenの作成ができます。
Create Tokenと進み、以下のようにAdmin Read & Writeを選択して権限設定を行います。
他の設定項目はそのままで進めます。
次の画面に進むとTokenが作成されます。
またその下にAccess KeyとSecret Keyも表示されます。
このキーはS3クライアントからアクセスするために使用できます。
TokenやAccess Keyなどはこの画面でしか確認できないため、忘れずにメモしてください
今回はこのAccess KeyとSecret KeyでGradleからアップロードします。
Tokenの作成自体は https://dash.cloudflare.com/profile/api-tokens からでも行えます。
権限として、Worker R2 Storageを選択し、Editの権限とすることでR2の操作ができます。
ここから作成した場合には他の権限を付与することもできます。
R2のページから作成したTokenもここに表示されるので、権限を追加することもできます。
S3互換のAccess KeyとSecret KeyはR2のページからTokenを作成した場合のみ確認できるので、S3クライアントで使うTokenはR2のページから作成してください。
Bucketの作成
BucketはTerraformで作ります。
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4"
}
}
}
provider "cloudflare" {
api_token = var.cloudflare_token
}
resource "cloudflare_r2_bucket" "cloudflare-bucket" {
account_id = var.cloudflare_account
name = var.bucket_name
# See https://developers.cloudflare.com/r2/reference/data-location for location
# location = "apac"
}
api_token
と account_id
は上で確認したものを入れてください。
bucketの名前は適当なものを設定してください。
locationは5箇所ほど用意されているようです。
何も入力しないと近い地域のリージョンが選ばれます。
GDPRなど地理的な制限がある場合は指定する必要があるかなと思います。
locationについては https://developers.cloudflare.com/r2/reference/data-location/ で説明されています。
Applyすれば作成されます。
Gradleの設定
準備
認証情報として使うのでR2のAccess KeyとSecret Keyを用意してください。
Tokenの作成をすると一緒に表示されます。
build.gradle.kts
の設定
maven-publish
プラグインを設定し、以下のようにpublishの設定をしました。
publishing {
publications {
create("mavenJava", MavenPublication::class) {
from(components.named("java").get())
}
}
repositories {
val bucketName = project.findProperty("r2_bucket_name") ?: System.getenv("R2_BUCKET_NAME") ?: ""
maven {
name = "R2"
url = uri("s3://${bucketName}")
credentials(AwsCredentials::class) {
accessKey = (project.findProperty("r2_access_key") ?: System.getenv("R2_ACCESS_KEY") ?: "") as String
secretKey = (project.findProperty("r2_secret_key") ?: System.getenv("R2_SECRET_KEY") ?: "") as String
}
}
}
}
r2_bucket_name
にはBucketの名前、 r2_access_key
と r2_secret_key
にはそれぞれR2のAccess KeyとSecret Keyを設定しています。
R2はS3互換なので、URLにはS3のスキーマを設定しています。
https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:supported_transport_protocols に記述例が載っています。
R2へのPublish
build.gradle.kts
の設定ではS3のスキーマを指定していましたが、そのままではAWSのエンドポイントにアクセスしてしまいます。
そのためR2専用のエンドポイントにつなぐよう、Publishのコマンド実行時に設定します。
https://developers.cloudflare.com/r2/api/s3/tokens/ でエンドポイントの説明がされています。
通常であれば https://<ACCOUNT_ID>.r2.cloudflarestorage.com
の形式のエンドポイントを使用します。
EUのGDPRに対応するエンドポイントも用意されています。
以下のコマンドでPublishします。
./gradlew -Dorg.gradle.s3.endpoint=<EndPoint URL> publish
-Dorg.gradle.s3.endpoint
がGradleで使われるS3のエンドポイントを指定するためのキーです。
https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:s3-repositories にプロパティについての記述があります。
Publishが完了すれば以下のようにファイルがR2にアップロードされていることが確認できます。
既存のバージョンと同じバージョンでPublishしてしまうとエラーなしにファイルが更新されてしまいます。
別の仕組みで上書きしないようにするか、必ずバージョンがインクリメントされる仕組みを使うようにしましょう。
gradle.properties
の設定
上記の設定で必要なキーをまとめると以下のようになります。
r2_bucket_name=koma-maven
r2_access_key=<r2_access_key>
r2_secret_key=<r2_secret_key>
r2_maven_url=https://koma-maven.example.com
systemProp.org.gradle.s3.endpoint=https://<ACCOUNT_ID>.r2.cloudflarestorage.com
systemProp.org.gradle.s3.endpoint
を設定することで -Dorg.gradle.s3.endpoint
を毎回指定する必要がなくなります。
R2の公開
R2は2種類の方法でパブリックアクセスを有効化できます。
- r2.devのサブドメインでの公開
- 独自ドメインを使用する
r2.devのサブドメインでの公開
こちらの方法はレートリミットなどの制限があるため、本番環境ではお勧めしないと注意書きがされています。
有効にすると https://pub-<Bucket ID>.r2.dev
の配下で公開することができます。
Bucketの設定画面から、以下のところで有効にしたりURLの確認ができます。
R2のAPIを調べましたが、APIでサブドメインでの公開を有効にすることはできないようです。
https://developers.cloudflare.com/api/operations/r2-create-bucket
独自ドメイン
この方法ではドメインのDNSがCloudflareで管理されている必要があります。
こちらの方法ではCloudflareのキャッシュやアクセス制限を入れることができます。
Bucketの設定画面から、以下のところで有効にできます。
有効にするとドメインのレコード一覧に表示されるようになります。
レコードのAPIからR2のドメインのデータを取得すると以下のようになります。
{
"result": {
"id": "ID",
"zone_id": "ZONE",
"zone_name": "example.com",
"name": "koma-maven.example.com",
"type": "CNAME",
"content": "public.r2.dev",
"proxiable": true,
"proxied": true,
"ttl": 1,
"locked": false,
"meta": {
"auto_added": false,
"managed_by_apps": false,
"managed_by_argo_tunnel": false,
"r2_bucket": "koma-maven",
"read_only": true,
"source": "primary"
},
"comment": null,
"tags": [],
"created_on": "2023-11-23T14:09:13.132284Z",
"modified_on": "2023-11-23T14:09:13.132284Z"
},
"success": true,
"errors": [],
"messages": []
}
向き先が public.r2.dev
である、CNAMEのレコードとして作成されているようです。
meta
の中にどのBucketであるか示す r2_bucket
の項目があります。
レコードの作成APIにはmetaを設定する項目はなく、Terraformの cloudflare_record
でも読み取り専用になってます。
よってR2を公開設定に自動で変更するのは難しいのではないかと思います。
cloudflare_record
の、以下のリソースとしてimportすることはできます。
resource "cloudflare_record" "r2-domain" {
name = cloudflare_r2_bucket.cloudflare-bucket.name
type = "CNAME"
zone_id = data.cloudflare_zone.zone.id
proxied = true
ttl = 1
value = "public.r2.dev"
}
しかしmetaは読み取り専用で変更できないため、管理する意義はほとんどありません。
Bucketのファイルにアクセスする際には、ドメインの後にファイルのパスを繋げたURLを使用します。
jp/co/yumemi/koma/lib/maven-metadata.xml
のファイルへは https://koma-maven.example.com/jp/co/yumemi/koma/lib/maven-metadata.xml
でアクセスできます。
Gradleからの参照
上の手順で定義したURLを設定するだけです。
repositories {
maven {
name = "R2"
url = uri("https://koma-maven.example.com/")
}
}
dependencies {
implementation("jp.co.yumemi.koma:lib:1.1-SNAPSHOT")
}
のように指定することでR2に公開したファイルを使えます。