Fastly Object Storage 上のコンテンツに Fastly CDN 経由でアクセスする

# はじめに
[Fastly Object Storage](https://www.fastly.com/blog/build-store-scale-fastly-object-storage-with-zero-egress-costs-here) は 2024年の12月12日に GA になった S3 コンパーチブルな Storage サービスです。

Fastly CDN のオリジンや Fastly Compute で使用する画像やメディアファイルなど静的なファイルの保存先として利用することが可能で、以下のような特徴があります

- **Zero-egress fees**
Object Storage から Fastly CDN/Compute への Egress (帯域)のコストは発生しません
※ 書き込み・読み込みリクエストの数に応じての課金は発生します。
　
- **S3-compatible API**
S3に準拠しているの、既存のコードに大きな変更なく利用が可能です
　
- **All the storage your apps could ever need**
静的コンテンツを保存するのに必要な機能が揃っています

機能の詳細や制約は[こちらの製品ページ](https://docs.fastly.com/en/guides/object-storage)で確認可能ですが、この記事では Fastly Object Storage にファイルを保存して Fastly CDN 経由でアクセスする最低限の手順を説明します。

**2024年12月現在では本機能を利用するには開発者アカウントではなく有料のアカウントである必要があります。**


# Object Storage を有効化する

Fastly を有料アカウントでご利用の方は コントロールパネルにログインして左側のメニューから 
Resources -> Object Storage
にアクセスすると以下の画面が表示されます。`Enable Object Storage` をクリックして機能を有効化することが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/6f435daf-991d-943a-9b7f-0f6550f7557b.png)


# Key の作成
機能が有効化されるとまずは Object Storage へのアクセス用の Key を作成します。Object Storage の設定画面で `Create Key` をクリックして下さい。

以下の画面が表示されます。コンテンツの Upload をしたいので Read and write を選択したまま Create をクリックして表示される `Access Key ID` と `Secret Access Key` をコピーしてセキュアな環境に保存して下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/95aafe82-76ae-a709-75bf-670e959e4de1.png)


# バケットの作成とファイルのアップロード

バケットの作成やファイルのアップロードは S3-compatible API が利用可能です。ここでは aws cli を利用して行ってみます。

※ 現在 Fastly Object Storage がサポートしている Region は `us-east`, `us-west`, `eu-central` の3箇所です。使用する Region に合わせて指定する内容を変更して下さい。


aws cli の configutration には以下の内容を設定して下さい。

```
$ aws configure
AWS Access Key ID [****************dadl]:  <- 先ほどコピーした Key ID
AWS Secret Access Key [****************BOc5]: <- 先ほどコピーした Secret Access Key
Default region name [us-west]: us-west
Default output format [None]:
```

バケットの作成、オブジェクトのUpload は以下のコマンドで実施できます。

```
バケットの作成
aws --endpoint-url https://us-west.object.fastlystorage.app s3 mb s3://myawsomebucket

バケットのリスト
aws --endpoint-url https://us-west.object.fastlystorage.app s3 ls

アップロード
aws --endpoint-url https://us-west.object.fastlystorage.app s3 cp index.html s3://myawsomebucket
upload: ./index.html to s3://myawsomebucket/index.html

ファイルを確認
aws --endpoint-url https://us-west.object.fastlystorage.app s3 ls s3://myawsomebucket 
2024-12-19 11:05:26       3770 index.html
```


# CDN Service の作成
Object Storage にオブジェクトをアップロードした後はその Object Storage に Fastly 経由でアクセスする設定を作成します。

CDN の配信設定(Service)がよく分からない場合は[こちらの記事](https://qiita.com/AtsushiFukuda/items/22302fcca3b13f79a4de#%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AE-cdn-service-%E3%81%AE%E4%BD%9C%E6%88%90%E6%89%8B%E9%A0%86) を参考にして作成して下さい。

Object Storage をオリジンとして利用する設定は [Amazon S3 をオリジンとして設定](https://docs.fastly.com/ja/guides/amazon-s3)する手順とほぼ同じですが、以下の2点に注意して下さい。

### Origin の Host 名を `us-west.object.fastlystorage.app` にする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128569/3c103618-2567-a117-fcee-3d6017fc3f6b.png)

### VCL Snippet の追加
vcl_miss に以下の Snippet を追加します。

set var.awsAccessKey = "xxxxx";   # Change this value to your own data
set var.awsSecretKey = "xxxxxx";   # Change this value to your own data
set var.awsS3Bucket = "xxxxx";   # Change this value to your own data
set var.awsRegion = "us-west";   # Change this value to your own data
set var.awsS3Host = "us-west.object.fastlystorage.app";

の箇所はご自身の環境にあわせて変更して下さい。

```:vcl_miss
if (req.backend.is_origin) {
declare local var.awsAccessKey STRING;
declare local var.awsSecretKey STRING;
declare local var.awsS3Bucket STRING;
declare local var.awsRegion STRING;
declare local var.awsS3Host STRING;
declare local var.canonicalHeaders STRING;
declare local var.signedHeaders STRING;
declare local var.canonicalRequest STRING;
declare local var.canonicalQuery STRING;
declare local var.stringToSign STRING;
declare local var.dateStamp STRING;
declare local var.signature STRING;
declare local var.scope STRING;

set var.awsAccessKey = "xxxxx";   # Change this value to your own data
set var.awsSecretKey = "xxxxxx";   # Change this value to your own data
set var.awsS3Bucket = "xxxxx";   # Change this value to your own data
set var.awsRegion = "us-west";   # Change this value to your own data
set var.awsS3Host = "us-west.object.fastlystorage.app";

if (req.method == "GET" && !req.backend.is_shield) {

  set bereq.http.x-amz-content-sha256 = digest.hash_sha256("");
  set bereq.http.x-amz-date = strftime({"%Y%m%dT%H%M%SZ"}, now);
  set bereq.http.host = var.awsS3Host;
  set bereq.url = querystring.remove(bereq.url);
  set bereq.url = "/" var.awsS3Bucket + regsuball(urlencode(urldecode(bereq.url.path)), {"%2F"}, "/");
  set var.dateStamp = strftime({"%Y%m%d"}, now);
  set var.canonicalHeaders = ""
    "host:" bereq.http.host LF
    "x-amz-content-sha256:" bereq.http.x-amz-content-sha256 LF
    "x-amz-date:" bereq.http.x-amz-date LF
  ;
  set var.canonicalQuery = "";
  set var.signedHeaders = "host;x-amz-content-sha256;x-amz-date";
  set var.canonicalRequest = ""
    "GET" LF
    bereq.url.path LF
    var.canonicalQuery LF
    var.canonicalHeaders LF
    var.signedHeaders LF
    digest.hash_sha256("")
  ;

  set var.scope = var.dateStamp "/" var.awsRegion "/s3/aws4_request";

  set var.stringToSign = ""
    "AWS4-HMAC-SHA256" LF
    bereq.http.x-amz-date LF
    var.scope LF
    regsub(digest.hash_sha256(var.canonicalRequest),"^0x", "")
  ;

  set var.signature = digest.awsv4_hmac(
    var.awsSecretKey,
    var.dateStamp,
    var.awsRegion,
    "s3",
    var.stringToSign
  );

  set bereq.http.Authorization = "AWS4-HMAC-SHA256 "
    "Credential=" var.awsAccessKey "/" var.scope ", "
    "SignedHeaders=" var.signedHeaders ", "
    "Signature=" + regsub(var.signature,"^0x", "")
  ;
  unset bereq.http.Accept;
  unset bereq.http.Accept-Language;
  unset bereq.http.User-Agent;
  unset bereq.http.Fastly-Client-IP;
}
}
```

上記の設定が完了したら Service を Activate して下さい。


# ブラウザ経由でアクセスを確認する

Service の展開が完了すると、Service のドメインに指定した FQDN 経由で Object Storage に保存したオブジェクトへのアクセスが可能になります。
VCL Snippet に指定したバケット内のコンテンツにアクセスが可能です。

なお、Fastly から Object Storage にリクエストを送信する際にはバケット名を自動的に追加するので URL を指定する際にバケット名を意識する必要はありません。

例:

クライアントからのリクエスト: http://`<Domain に指定した FQDN>`/dir1/index.html
バケット名: mybucket
Fastly から Object Storage へのリクエストパス: /mybucket/dir1/index.html


# まとめ
以上が Object Storage 上にコンテンツを保存して Fastly CDN 経由でアクセスする手順になります。TTL などはデフォルトのままですので必要に応じて長時間の TTL を設定したり Shielding を利用するなどコンテンツの特性に合わせて設定を最適化して下さい。