Unity CCDを触ってみる


概要

これ

https://blogs.unity3d.com/2020/09/10/enterprise-gaming-cdn-get-started-in-an-hour-with-unitys-cloud-content-delivery


記事内では略してUCCDと呼ぶ。


サービスの概要をウルトラかんたんにまとめると、

・Unityアカウントを持っていてもいなくてもUnity使っていてもいなくても使える

・月50GBまでは無料な

・CDN



運用的にまとめると

・これはCDN on Rails。 めんどくさいところは全部Unityまかせにできる

・手元のフォルダをそのままuploadしてパブリッシュできるが、ファイルの削除は伝搬しない

・bucket、entry単位でのdeleteがあるがすごくクセが強い

・人力でinvalidate(キャッシュさせ直す処理)はない。キャッシュのTTLは1W

・データは1Wキャッシュされ同じURL上に残るが、badge経由のURLのみを公開するのであれば即時消せる(ただしrelease単位かbucket単位のみ)

・課金アラートや現在どの程度ギガを消費したかを確認できる画面はない


おことわり

自分はUnityの中の人ではないので色々試した結果を乗っけている + テスト時に作ったいろいろはだいたい既に削除済みだが、見れるものもあると思う。

この記事中のサンプルファイルのURLにアクセスしても404だよ~とかのご連絡は不要だが、

ここミスってない?とかは是非 https://twitter.com/toru_inoue まで。



チュートリアル

ステップバイステップでコマンドラインから操作する手段を教えてくれる。すっげー親切。

スクリーンショット 2020-09-12 1.32.37.png


ただそもそものこのCDNのチュートリアルの開始までにちょっとステップがある。

・unity Dashboardへのログイン = Unityアカウント作る

・プロジェクト作る


このへんの画面で Cloud Content Delivery のGet Startedボタンを押す(画像はすでに押してある状態だが)

スクリーンショット 2020-09-12 12.13.24.png

・クレジットカード情報をセットすると、UCCDに関する項目がUnity Developersの画面に出てくる。

そこでUCCD関連の全ての操作が行える。



ファイルのアップロードからCDNへの公開まで

フォルダに好きなものを入れる

-> フォルダをCLIでsync

-> web UIかCLIかweb APIから、アップ済みで未releaseな変更群(= latest)をrelease化すると、インターネッツにファイルが公開される。


latest -> releaseのこの工程なんか覚えがあるなーと思ったんだけど、思いあたった、gitじゃん。


・ある時点のローカルなフォルダの内容/構成をそのままlatestとしてupしてくれて(commit)

・変更点をまとめて一つのreleaseとして公開できる(push)


で、一個明確にgitではないポイントがあって、ファイルsyncで発生させられる変更のなかで、ファイルの削除に関する事象が無い。


これは例えばローカルにあったaというファイルをリネームしてbにしてsyncすると、aとb両方のファイルがlatestに残る。

そのままreleaseすると、releaseされるのも当然aとbで、ファイルsyncからはweb上のstageに該当するような要素を消す方法がない。


つまりファイルsyncした時点でファイルがアップロードされていて、それがそのままreleaseへと昇格していくイメージ。


で、ファイルってどうやって消すの?っていうと、syncでもweb UIからも消せないが、web APIからは消せる。

当然それも変更なので、次回releaseを行なって初めて削除が適応される。



project、bucket、entry、release、badgeの概念

Paper.アイデア.793 3.png

projectを幹として、bucketを幾つでも作ることができる。bucket名は公開されず、bucketに自動的に振られたidを元にアクセスを行ったりする。


bucketにはentryと呼ばれるファイルを持たせることができ、ファイル名ごとにentryが生成される。ファイル名は公開されないが、ファイル名を使ってアクセスする方法が提供される。


entryの集合 entriesからは、release = その瞬間のentryを集めたスナップショット を作成することができる。releaseには番号、idが振られる。

この時点でreleaseに含まれるentry = ファイルがWebへと公開され、外部からアクセスすることが可能になる。


そんで、最新のreleaseには自動的にlatest badgeがつく。

これは、UCCDがbadgeというエイリアスを使って「releaseが何番とか何もわからんけどファイル名がわかればファイルにアクセスできるAddressables URLという仕掛け」を提供しているため。

これがファイル=entryの削除の概念にも深く関係している。


releaseに対して自由にbadgeを追加したり削除したりすることができる。latestは例外的に勝手に最新についてくる。

画像だと、まあちょっと嘘をついている(一番右のReleaseにlatestがついてこないという状態はありえない)


Addressables URL はbucket と badge と ファイル名 を組み合わせてURLを生成する機能で、releaseを足すたびにURLが変わることがない。

例えばrelease2にあったファイルaをrelease3で更新した、という場合、Addressables URL経由では、bucket/latest?path=a で更新されたrelease3状態のファイルaへとアクセスできる。

例えばrelease2まではあったファイルaをrelease3で消した、という場合、Addressables URL経由では、bucket/latest?path=a へのアクセスで404が発生する。


具体的なURL例は UCCDが発行するURLについて に書いたから参考にするといい。

Paper.アイデア.794 3.png


もう一個おまけでpromotoという仕組みがあり、あるbucket Aの特定のreleaseを別のbucket Bへと「Bの新規releaseとして」発生させることができる。

ちなみにbadgeはbucketを跨がない。entry、releaseも。それはそう。



ここまでに紹介した要素をパズルのように用いて、運用に都合がいいようにCDNとして利用しよう!、というのが、UCCDの提供してくれるもの。



UCCDが発行するURLについて

次の3つのURLはすべて同一のデータを指すが、それぞれ価値が違っている。


次のパラメータがあったとき

1bfcb5bc-f825-4496-b0a8-fa05de8602bc : バケットid

latest : latestバッジ

logo.png : ファイル名



1. badge経由でのデータアクセスを提供するURL

https://787e3749-f29e-48d5-ba4a-d5fd3ca60935.client-api.unity3dusercontent.com/client_api/v1/buckets/1bfcb5bc-f825-4496-b0a8-fa05de8602bc/release_by_badge/latest/entry_by_path/content/?path=logo.png


これはbucketを消すか、entryを消したreleaseを行いbadgeをそのreleaseに移すか、badge自体を消すことで即時アクセス不可になる。

キャッシュする関係上でoriginを指してるわけではないが、bucket/entry自体を消すとbucket/entryに紐づくbadgeも消えて、経路ごとふっとぶみたいな感じだろうか。



2. ファイル単体のURL

https://787e3749-f29e-48d5-ba4a-d5fd3ca60935.client-api.unity3dusercontent.com/client_api/v1/buckets/1bfcb5bc-f825-4496-b0a8-fa05de8602bc/entries/bca891e4-5425-468b-90cb-c831ce5c86ac/versions/4fb4f9db-544b-4a37-abfc-0f8dbd268719/content/


このURLはoriginをダイレクトに指しているため、origin置き場 = bucket/entryの存在と完全に同期して消えるっぽい。



3. リダイレクト先URL

https://787e3749-f29e-48d5-ba4a-d5fd3ca60935.cloudcontent.unity3dusercontent.com/bd562994ee34eb410064276a2ea436ba

このURLは、1,2を経由してリダイレクトされるURLで、これはbucketを消してもentryを消しても残ってる。TTLを見れる機会があったんで1Wくらいはこのままだと思う、

とおもったが、

projectをアーカイブしたら流石に即時で消えた。unarchしても戻らなかったので、最終手段としてのアーカイブという手はアリなのかもしれない(まあ課金とかadも即死するので絶対に選べないが)



web APIとドキュメント一覧

web API

https://content-api.cloud.unity3d.com/doc/?_ga=2.3423414.709552200.1599716565-337588898.1599383967#/



ドキュメント

https://docs.unity3d.com/Manual/UnityCCDCLIWalkthrough.html?_ga=2.104094982.709552200.1599716565-337588898.1599383967



ここまでがウォーミングアップだ。ここから先が知りたかったこと。




invalidate(キャッシュの削除)どうやるの?

badgeを駆使すればアクセス先が存在しないという状態にはできるが、badgeを使ってURLを変える、という発想が必須

badgeを変えずにCDNにキャッシュされた情報をinvalidateする手段は無い。というかオリジンのファイル自体を更新できない。



じゃあファイルの削除ってどうすればいいの?

そのファイルが含まれないreleaseを作ってbadgeをそのreleaseにつける。

か、bucket自体を削除する。


前者が常套手段、後者はbadgeコントロールでどうしようもないURLを発行して使われてしまった場合には本当にどうしようもないけど使える手段。



アクセス制御とか、ユーザー単位の認証とかできるの?

むりそう。現状そういう機能は備わっていなそう。

bucketに対してmetadataをセットするとかができるようになるとアクセス制御ができるんでは? というのがあるが、

現状だとmodelにもそういう表記はないんで、どうやって何を入れたもんだか。


swaggar上のサンプルレスポンスだとなんかそういう「設定できてるmetadata」が返ってきてそうな、まあ空のmetadataの表示があるんだけど、実際のレスポンスにはmetadata自体がない。

一応この記事の末尾にreq/resを書いた。まあ所詮swaggarだしなあ。



課金アラートってないの?

冒頭の通り、月50GBまでは無課金でいける。が、さて? それを超えたらどうなっちゃうの?


アラートの類が一切ないのと、今何GB使ったかとかどこでみんのコレ、、、


Unity Dashboardとかで表示されるのこんな画面なんで、、、

スクリーンショット 2020-09-12 5.56.11.png


ちょっと全くわからなかったので問い合わせしてみようかな。ここで見れたよとかあったら教えて欲しい。


流石にクレジットカード設定なしで使われるとサービス提供側だけがものすごい損だけをするのでカード設定必須ってのはわかるんだけど、

アクセス制御もなし、ワンタイムURLみたいな構造もなし、その上でアラートがないと、ノーガードで破産するまで殴られそうな気がするが、、、



HTTP/2使える?

つかえない。disableしてある。



まとめ

調査を手伝ってくれた友人の言葉を借りるとまさに「CDN on Rails」。


ファイルの追加や更新はものすごく簡単にできる。

反面、間違ってreleaseしたファイルの削除をするには、新規releaseとbadge移動操作を組み合わせなければいけないのがちょっと難しい。


releaseの単位のデザインを守るためにrelease内のentryの個別削除がない、という風に見える。


ファイルsyncで消えたファイルに対してdeleteを走らせてくれないので、sync操作で直感に反してファイルが増えまくることが無限に起きて辛いというのはあるが、まあAPIも難しくないし、、、

CLIを使う旨味がsyncとチュートリアル用という感じ。でも初期体験はすごく良かった。


各ツールでできることを表にするとだいたいこんな感じで。

スクリーンショット 2020-09-12 12.35.56.png



いろんなツールでいろんなことができるが、CLIはsync = add entryしかしないupload専用、あとはwebUIとwebAPIでやるといいと思う。認証も難しくないし。



Addressablesに無理に寄せることもなく、アクセス制御以外の物事はそつなくだいたい大まかにフワッと叶えられると思う。素敵。

おかげで運用における自由度もそれなりに稼げる。そして何より安い!! すごくやすい。


ただめちゃくちゃ「貧者のCDN」なので、個人開発者にお勧めするが課金は気にした方がいいと思った。アラートないので個人ほど死にそうな気はするが、まあうん、、

困ったら即、空のreleaseを作って該当のbadgeをつけるか、最後の手段としてはbucketを消すんだ、、、


逆に、企業がこのサービスを使うには、存在しないアクセス制御の時点でほぼ詰みつつ、クセがあるbadge運用、ちょっと脳味噌が痛くなるpromoteとかを使いまくるのが正しいとは思う。


最後に、とにかく「何を消したらどのURLがどうなるのか」は実体験をして知っておいたほうがいい。

UCCDについては、とにかくbadgeを信じろと言い換えてもいい。 


badgeではないURLを公開したり、appが解析されて組み込まれてるURLが露出した時とかに、1W程度の間CDN上のURLを自前で消せないパターンがあり、その間どうなっちゃうのかすごく興味がある。



自分? 自分は怖いから実験に使ったURLとかはbucketどころかprojectごと消したさ。


今後はUnity関連のOSSで静的ファイルをホストするときに是非使わせてもらう。




以下メモ


curl -X GET "https://content-api.cloud.unity3d.com/api/v1/buckets/948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32/" -H "accept: application/json" -u:YOUR_API_KEY

{

"id": "948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32",

"name": "testdata",

"projectguid": "787e3749-f29e-48d5-ba4a-d5fd3ca60935",

"description": "the bucket for test data",

"changes": {

"unchanged": 2,

"add": 0,

"update": 0,

"delete": 0

},

"last_release": {

"releaseid": "a97d449c-80b6-4222-ac9c-379bbb70927e",

"releasenum": 5,

"created": "2020-09-11T18:27:21.811Z",

"created_by": "39597",

"created_by_name": "sassembla",

"notes": "5th, remove unnecessary",

"entries_link": "/api/v1/buckets/948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32/releases/a97d449c-80b6-4222-ac9c-379bbb70927e/entries/",

"content_hash": "8f50d37f7fe37160beea060743f17b59",

"content_size": 1136480,

"badges": [{

"name": "latest",

"releaseid": "a97d449c-80b6-4222-ac9c-379bbb70927e",

"created": "2020-09-11T18:27:21.811Z",

"created_by": "39597",

"created_by_name": "sassembla",

"releasenum": 5

}],

"changes": {

"unchanged": 1,

"add": 1,

"update": 0,

"delete": 0,

"last_modified": "2020-09-11T18:24:46.937Z",

"last_modified_by": "39597",

"last_modified_by_name": "sassembla"

}

}

}



curl -X DELETE "https://content-api.cloud.unity3d.com/api/v1/buckets/948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32/entries/8d83ae5d-9d72-4539-9239-b7dbb55380f0/" -H "accept: application/json" -u: YOUR_API_KEY



curl -X GET "https://content-api.cloud.unity3d.com/api/v1/projects/787e3749-f29e-48d5-ba4a-d5fd3ca60935/buckets/?page=0&per_page=10" -H "accept: application/json" -u: YOUR_API_KEY

[{

"id": "948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32",

"name": "testdata",

"projectguid": "787e3749-f29e-48d5-ba4a-d5fd3ca60935",

"description": "the bucket for test data",

"changes": {

"unchanged": 1,

"add": 0,

"update": 0,

"delete": 0

},

"last_release": {

"releaseid": "9f68afd7-0fe6-4b2a-b677-bee97142fb4b",

"releasenum": 6,

"created": "2020-09-11T19:55:50.100Z",

"created_by": "39597",

"created_by_name": "sassembla",

"notes": "6th, truly deleted.",

"entries_link": "/api/v1/buckets/948b1e2c-6ad8-4bf3-8d11-3e9f9faeeb32/releases/9f68afd7-0fe6-4b2a-b677-bee97142fb4b/entries/",

"content_hash": "b2a23ed64584de6f8cb31dca2658ba84",

"content_size": 601354,

"badges": [{

"name": "latest",

"releaseid": "9f68afd7-0fe6-4b2a-b677-bee97142fb4b",

"created": "2020-09-11T19:55:50.100Z",

"created_by": "39597",

"created_by_name": "sassembla",

"releasenum": 6

}],

"changes": {

"unchanged": 1,

"add": 0,

"update": 0,

"delete": 1,

"last_modified": "2020-09-11T19:47:10.262Z",

"last_modified_by": "39597",

"last_modified_by_name": "sassembla"

}

}

}]



URLの組み合わせ詳解


1. badge経由でのデータアクセスを提供するURL

https://PROJECT_ID.client-api.unity3dusercontent.com/client_api/v1/buckets/BUCKET_ID/release_by_badge/BADGE_NAME/entry_by_path/content/?path=ORIGINAL_FILE_NAME



2. ファイル単体のURL

https://PROJECT_ID.client-api.unity3dusercontent.com/client_api/v1/buckets/BUCKET_ID/entries/ENTRY_ID/versions/ENTRY_VERSION/content/



3. リダイレクト先URL

https://PROJECT_ID.cloudcontent.unity3dusercontent.com/個別のFILE_ID