[AWS]CloudFrontのキャッシュをEC2から削除する

CloudFrontのキャッシュ削除はAWSコンソールから手動で行うことができますが、
デプロイ時に自動化するために、EC2からキャッシュ削除する方法をまとめました。

**EC2+S3+CloudFrontは構築済み**の前提で説明します。

![CloudFront.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/ec3823d6-4f3d-4877-2490-e05129833d8d.png)

## 1. 準備
デプロイ時の状況をシミュレーションするための準備をします。

### 1-1. S3へファイルアップロードする
まずはテスト用のファイルをアップロードします。
AWSコンソール > サービス > S3 > 該当バケット > アップロード
でS3へ適当な画像ファイルやHTMLファイルなどをアップロードします。
<img width="1440" alt="スクリーンショット 2020-06-24 17.09.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/b28ff964-3129-3e14-a251-7172a05e2e44.png">

次へを押して、後の確認のためパブリックアクセス権を付与してください。
<img width="1440" alt="スクリーンショット 2020-06-24 17.13.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/546766df-ea86-391e-a16e-e5c17f02b879.png">

それ以外の設定はデフォルトでOKで、アップロードを押してください。

アップロードされたもの詳細を表示し、オブジェクトURLへアクセスし内容を確認してください、
<img width="1440" alt="スクリーンショット 2020-06-24 17.17.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/257386e5-10b2-6347-73b5-19b972bf127d.png">

ちなみに今回アップロードしたものはこのような内容です。
<img width="679" alt="スクリーンショット 2020-06-24 17.19.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/d124ea30-0c3d-c6c0-dbab-d61d59da6131.png">

CloudFrontのURLへアクセスします。
AWSコンソール > サービス > CloudFront > 該当のディストリビューション
で詳細を表示し、Domain Nameを確認します。
<img width="1440" alt="スクリーンショット 2020-06-24 17.30.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/be574b74-3946-5d4b-2576-d5ae161991d2.png">

その後ろに、S3でアップロードしたオブジェクトのパスを付与すると該当のファイルへのURLとなります。
https://\<domain name>/\<object name>
<img width="667" alt="スクリーンショット 2020-06-24 18.19.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/38a40607-9de8-8649-c40a-e45f11ea582f.png">


同じ内容が表示されていればOKです。

ちなみに自分は↓の現象に当たったので、S3バケットを作り直しました。
[S3+CloudFrontでS3のURLにリダイレクトされてしまう場合の対処法 | Developers.IO](https://dev.classmethod.jp/articles/s3-cloudfront-redirect/)
でも作り直さなくても気長に反映されるまで待てば問題ないです。


### 1-2. S3のファイル上書きする 
オリジンサーバ(S3)とキャッシュサーバ(CloudFront)の内容に差異をつけるため、S3の内容を変更します。
先ほどと同じ手順で、同じファイル名かつ違う内容のファイルをS3にアップロードすればOKです。

再アップロードした後、先ほどアクセスしたS3のURLとCloudFrontのURLへそれぞれアクセスし、
**S3は変更後**の内容、**CloudFrontは変更前**の内容になっていることを確認します。

CloudFrontのステータスコードは304(リソース未更新)が返ってきていると思います。
<img width="1439" alt="スクリーンショット 2020-06-24 18.22.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/8c31637f-e2af-6a60-3e22-d1b5903e8efa.png">


## 2. キャッシュ削除する

CloudFrontキャッシュの削除とは、公式だと[ファイルの無効化(CreateInvalidation)](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html)を指します。
これを行うために[CloudFront API](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateInvalidation.html)を利用します。
このAPIを利用する方法として、**シェルスクリプト**で行う場合と、**PHP**で行う場合を説明します。

### 2-1. シェルスクリプトで削除する
[AWS CLIをインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html)＆[設定](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration)したのちに
`$ aws cloudfront create-invalidation`
のコマンドでキャッシュ削除することができます。
詳細：[create-invalidation — AWS CLI 2.0.24 Command Reference]
(https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudfront/create-invalidation.html)

#### EC2の任意フォルダ内に以下のファイルを作成

```clear_cache.sh
#!/bin/bash

echo "CloudFront cache clear..."
aws cloudfront create-invalidation \
--distribution-id "EDFDVBD6EXAMPLE" \
--paths "/*"
```

#### 権限付与して実行

```bash
$ chmod 755 clear_cache.sh
$ ./clear_cache.sh
```

#### 出力結果

```bash
CloudFront cache clear...
{
    "Invalidation": {
        "Status": "InProgress", 
        "InvalidationBatch": {
            "Paths": {
                "Items": [
                    "/*"
                ], 
                "Quantity": 1
            }, 
            "CallerReference": "cli-1592983262-260490"
        }, 
        "Id": "XXXXXXXXXX", 
        "CreateTime": "2020-06-24T07:21:03.500Z"
    }, 
    "Location": "https://cloudfront.amazonaws.com/2019-03-26/distribution/EDFDVBD6EXAMPLE/invalidation/XXXXXXXXXX"
}
```

**参考**
[CloudFrontキャッシュを削除するシェルスクリプトを書いてみた。 - Qiita](https://qiita.com/yuusuke510/items/72c2bcfb4ab0de77d00b)

### 2-2. PHPで削除する

今回はPHPですが、[AWS SDKは他の言語もいろいろ](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateInvalidation.html#API_CreateInvalidation_SeeAlso)あるのでなんでも良いと思います。

[AWS SDK for PHPをインストール](https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/getting-started_installation.html)したのち
Aws\CloudFront\CloudFrontClient のオブジェクトを利用して
`$result = $client->createInvalidation();`または
`$promise = $client->createInvalidationAsync();`
のコマンドでキャッシュ削除することができます。
[AWS SDK for PHP 3.x](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-cloudfront-2019-03-26.html#createinvalidation)

#### EC2の任意フォルダ内に以下のファイルを作成

```clear_cache.php
<?php
require 'vendor/autoload.php';

use Aws\CloudFront\CloudFrontClient;
use Aws\Exception\AwsException;

echo('CloudFront cache clear...'."\n");

$access_key = "xxxxxx";
$access_secret = "xxxxxx";
$distribution_id = "XXXXXX";
$paths = [
    '/*',
];

$client = new CloudFrontClient([
    'region'  => 'us-east-1',
    'version' => '2019-03-26',
    'credentials' => [
        'key'    => $access_key,
        'secret' => $access_secret,
    ],
]);

/** @var \Aws\Result $result */
$result = $client->createInvalidation([
    'DistributionId' => $distribution_id,
    'InvalidationBatch' => [
        'Paths' => [
            'Quantity' => count($paths),
            'Items' => $paths,
        ],
        'CallerReference' => time(),
    ],
]);

var_dump($result);
?>
```

#### 実行
```bash

$ php clear_cache.php
```

#### 出力結果
```bash
CloudFront cache clear...
/home/ec2-user/clear_cache.php:37:
class Aws\Result#90 (2) {
  private $data =>
  array(3) {
    'Invalidation' =>
    array(4) {
      'Id' =>
      string(14) "xxxxxxx"
      'Status' =>
      string(10) "InProgress"
      'CreateTime' =>
      class Aws\Api\DateTimeResult#109 (3) {
        ...
      }
      'InvalidationBatch' =>
      array(2) {
        ...
      }
    }
    'Location' =>
    string(99) "https://cloudfront.amazonaws.com/2019-03-26/distribution/XXXXXX/invalidation/I2WADC1K0L962S"
    '@metadata' =>
    array(4) {
      'statusCode' =>
      int(201)
      'effectiveUri' =>
      string(84) "https://cloudfront.amazonaws.com/2019-03-26/distribution/XXXXXX/invalidation"
      'headers' =>
      array(5) {
        ...
      }
      'transferStats' =>
      array(1) {
        ...
      }
    }
  }
  private $monitoringEvents =>
  array(0) {
  }
}
```

**参考**
[AWS PHP SDK v3 で Amazon CloudFront のキャッシュをパージする | コンクリートファイブジャパン - concrete5 Japan Inc.](https://concrete5.co.jp/blog/aws-php-sdk-v3-amazon-cloudfront)


## 3. 確認
CloudFrontのステータスが「**配備済み(APPROVED)**」に変わってから
CloudFrontのURL **https://\<domain name>/\<object name>** へアクセス。
内容が変更後のものになったことを確認します。
ステータスコードも304ではなく200になっています。

<img width="1440" alt="スクリーンショット 2020-06-24 16.32.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/970b6b8a-b510-d000-408d-90ef94bed14a.png">


#### コンソールでログ確認
一応 AWSコンソール > サービス > [CloudTrail](https://console.aws.amazon.com/cloudtrail/home) でログを確認します。
（他にCloudFrontのログ設定しているものがあればそちらを見れば良いと思います。）

<img width="1440" alt="スクリーンショット 2020-06-24 16.36.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/947859dd-294a-6ee2-03a1-78b682e3b80b.png">

エラーコードに何も表示されていなければ成功です。