CloudFrontでmulti originをするときにハマったところ

## 概要

年末で暇だったので、個人サービスの画像配信部分をCloudFront経由で早くしてやろうと思い、見事にハマった出来事についての備忘録です。

ちゃんと調べずに自分でトライアンドエラーをしているので、かなりグダグダした内容になっています。ご了承ください :bow:

## TL;DR

内容がグダグダなので、何にハマったか知りたい人はこれだけ見て退散することをオススメします

```md
- BehaviorのPath PatternがOriginに渡るときにIgnoreされると思っていた
    - Path Patternは正規表現なのでIgnoreされずにOriginに届く
    - 例: 
        - Path Pattern(`image/*`)を持つmulti-origin(`image-bucket`)なCloudFrontに対してリクエストを送る
        - `https://www.app.com/image/full/sample1.png`がOriginに届く時には、`s3://image-bucket/full/sample1.png`になると思っていた
        - 実際にOriginに届くリクエストは `s3://image-bucket/image/full/sample1.png`
- ErrorになったBehaviorはスキップされて次にマッチするBehaviorに行く(っぽい)
```

## やったこと

### 前提条件

個人サービスはSPAでS3にアップロードしたものを、CloudFrontで配信していましたので、既にDistributionが一つあり、カスタムドメインも設定されていました。

画像はSPAのS3 Bucketとは別のBucketに保管されており、S3のWeb Hosting機能を使って配信をしていました。
この部分をCloudFrontでCacheさせて高速化を図るというのが、今回やりたいことでした。

別のDistributionを作成しても良かったのですが、設定を作り直すのも面倒だし、別ドメインだとCORSやDNS Lookupなど余計なものを気にしないといけない~~し面倒だな~~と思ったので、multi-originでやってみることに。

## CloudFrontでmulti-origin

CloudFrontにはmulti-origin機能が既に備えられており、それの通りに作成するだけ...、のハズでした。
これの設定方法については色々な記事で紹介されていました。
(何故かほとんどの記事で扱っていたのが、S3とELBのmulti origin)

やることは以下の3つです。

1. 既存のDistributionにoriginを追加
2. Behaviorを追加し、path patternで任意のpathのときに追加したoriginに向くようにする
3. CloudFrontに設定がDeployされるのを待つ

コンソールからDistributionを作成するときは一つのoriginとBehaviorしか設定できませんが、Distribution作成後追加できるようになります。

大体どの記事を見ても画像付きでこの説明がされていたのですが、自分はなぜかうまくいきませんでした。

### 1. 既存のDistributionにoriginを追加

画像がSPAとは別のBucketで保管していましたので、originを作成します。

![img1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120358/ea74dcb6-1db8-f27f-84de-6c4c7c743c57.png)



Origin Pathについては、リクエストが来たときにPrefixでoriginに追加されるPathになります。

例えばSingle Originで上のような設定とリクエストをしていた場合、CloudFrontは`s3://sample-image-bucket/thumbnail/sample1.png`からObjectをGetします。


```md

Origin: `image-bucket.s3.amazonaws.com`
Origin Path: `/thumbnail`
Request: `https://img.app.com/sample1.png`
```

今回の画像のBucketは以下のようなディレクトリ構造になっていたので、Origin Pathは設定していません。

```md
image-bucket
L full
  L ~.png
L thumbnail
  L ~.png
```

### 2. Behaviorを追加し、path patternで任意のpathのときに追加したoriginに向くようにする

BehaviorはEdgeロケーションのCacheの振る舞いを設定します。
Behaviorは複数設定することが可能で、Path Patternを設定することによって、複数のBefaviorに振り分けることが可能です。
コンソールからCloudFrontを作成し際には、DefaultのBehaviorだけ設定されています。

ここに画像配信用のBehaviorを作成します。

![img2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120358/b6b457f6-6a77-a2fb-8838-bd1fe525d88b.png)

Path Patternは正規表現で表します。今回は画像配信部分でしたので、`image/*`に設定しています。
これで、`https://cdn.app.com/image/full/~.png`にリクエストが来れば、画像を返してくれるはずでした...。

### 3. CloudFrontに設定がDeployされるのを待つ

待つだけです。だいたい10〜15分くらいでDeployされます。

## 起こったこと

何故かDefaultのBehaviorにルーティングされ、SPAのエラー画面が表示されます (404 Not found)。
CloudFrontの設定が良くないのかと思い色々調べてみました。

### 1. CloudFrontのログを出してみる

CloudFrontはDistributionの設定でログをS3に出力することができます。
これが設定されてなかったので、ログを有効化しデプロイされるのを待ちます。

デプロイされた後に、該当画像にアクセスしてみます。
するとログが出力されたのですが、CloudFrontのログは基本的にアクセスログしか残らないので、Errorが発生したかどうかはわかるのですが、エラーメッセージまでは見ることができませんでした。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#BasicDistributionFileFormat

とにかく、該当画像にアクセスした際にBehaviorのPath Patternの割り振りは動いているようでした。
権限的な問題かと思いましたが、既にWeb HostingでPublicになっているバケットでしたので、ありえませんでした。

### 2. 別のDistributionで画像BucketをDefaultのBehaviorのOriginにしてみる

最初の決意がゆらいで別のDistoributionでも良いかなと思い始めてきたので、新しくDistributionを作成し、DefaultのBehaviorを画像BucketのOriginに設定してみました。

すると、問題なくアクセスできました :thinking:
つまり、権限的な問題でもなくBehaviorの問題では無いことがわかりました。

ついでに、新しく作成したDistributionにSPAのOriginも設定してみます。
そのときに、Origin Pathのテストがしたくなったので、Origin Pathを `static/` にしてみました。

![img3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120358/b82f0828-bd8e-c0a8-3cd1-859c77513a9c.png)

BehaviorのPath Patternは `app/*` です。
それでもアクセスできませんでしたが、エラーの内容は変わりました。

![img4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120358/46553a35-6085-6450-ebcb-d48789fbaf73.png)

どうやら、DefaultのBehaviorである画像Bucketの方にリクエストがいっているようです。
予定では、`static/`以下のjsファイルが取得できる予定でした。

Origin Pathの設定が悪かった可能性もあったので、Origin Pathの設定を削除してみました。
それでも接続できません。どうやらOrigin Pathの設定ではないようです。

手詰まり感が出てきたので、一旦Path Patternを`static/*`にしてみました。
すると、アクセスできるようになりました！

## 原因

つらつらとトライアンドエラーしてきた内容を書いていきましたが、ようやく原因がわかりました。
BehaviorのPath Patternで設定したPathにマッチしたPathはOriginに送られる際に無視されるわけではなく、Path PatternのPathも含めてOriginに送られるのでした。

前提条件を整理すると以下のような感じでした。

```md
// CloudFrontの設定
- Origin: `image-bucket.s3.amazonaws.com`
- Behavior Path Pattern: `image/*`
- Request: `https://www.app.com/image/full/sample1.png`
```

```md
// S3のBucket構造
image-bucket
L full
  L ~.png
thumbnail
    L ~.png
```

この場合、画像Bucketに対するCloudFrontのリクエストは`s3://image-bucket/image/full/sample1.png`に飛んでいた訳です。
`image-bucket`には`image/`をPrefixに持つObjectは無いためCloudFrontはエラーを返し、DefaultのBehaviorであるSPAのOriginにリクエストが飛んでいたということです。
そして、SPAでは`/image`というルーティングは存在しないため`404 Not Found`が発生していたのです。

## まとめ

ということで、画像のBucketに`image/`というPrefixをつけるようにすることで、無事multi-originで画像配信が実現できました。

てっきりBehaviorのPath PatternはOriginに到達するときにIgnoreされているという思い込みが自分の中のハマりポイントでした。
しかし、ネタが割れてしまえば当たり前のことで、Path Patternは正規表現だし、元々Single-Originで配信したいObjectの種類に応じてCacheのパターン(Behavior)を変えたいときに使われていたと思われるので、そりゃそうだよなとなった。

大体思い通りに行かずにハマるときは自分の勘違いが招いていることが多い気がする...。