[パート２：実行]S3に保存する前ラムダでサムネイルに画像圧縮の前処理

# 前提

せめてS3・Lambda・IAM権を持っているAWSアカウントそれともIAMアカウント用意

# 概要
こちら記事では詳細な実行を説明させて頂きます。


<details><summary>主な内容が下記のように進めています。</summary>

1. バケツ作成
    1.1 バケツ作成
    2.2 ライフサイクル作成
2. ポロシー・ロール作成
    2.1 ポリシー作成
    2.2 ロール作成
3. ラムダファンクション作成
    3.1 環境作成
        3.1.1 初期設定
    3.2 ファンクション作成
        3.2.1 ロールや処理時間を設定
        3.2.2 トリガー設定
        3.2.3 環境変数
    3.3 ラムダのNode modulesについて
4. テスト行い
    4.1 S3アップロード
    4.2 結果をチェック
    4.3 ログをチェック
</details>

 

## 1. バケツ作成

2つのバケツを作成し、そのうち1つを一時的なバケツとして使用します。この一時的なバケツにはライフサイクルを設定します。

一時的なバケツとは、画像を一時的に保存し、ある期間を経過する後にライフサイクルポリシーに従って画像を自動的に削除するバケットのことです。

### 1.1 バケツ作成
**手順**

    1. S3に入る
    2.「バケツ作成」を押下

![create s3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/d2a923b0-fc9f-ed58-053f-58c1b6113294.png)

    3.バケツ作成画面に遷移される
    4.バケツ名のみを設定して、他の項目をそのままにしてもいいです 
![s3 step 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/efb0eab3-2fee-12c8-c051-d305c510507b.png)

第２台目も同じようにしてください

### 1.2 ライフサイクル作成

    1. 一時的なバケツ「temp-images-bucket」を選ぶ
    2. マネジメント
    3. ライフサイクル作成
    
![create ライフサイクル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/e1f96ebf-5c22-ea2b-1ca3-e34abf364390.png)
   
    3. ライフサイクル作成に遷移された
    4. 以下のように設定してくだい

![life cycle 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/37ef3598-206b-9dc6-8b58-fe09331e2636.png)

![life cycle2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/91ec90dd-994b-165d-ca99-f02a7b118197.png)
貴方の要件次第でルールを作成してください。
この記事の限りでは載せった画像を5日後削除させます。

:::note info
ルールについて詳しく[AWSドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html?icmpid=docs_amazons3_console#glacier-pricing-considerations)に参考して下さい。
:::





## 2. ロール作成

AWSリソースをアクセスするために該当するポリシーが必要です。そして、あんなポリシーをユーザー（ユーザグループ経過※）やロールなどに付けます。

:::note info
<i>（※）AWSのベストプラクティスによると、ユーザーに直接ポリシーを付与するのではなく、ユーザーをユーザーグループに追加し、ユーザーグループにポリシーを付与する方が良いとされています。

<i/>
:::



[前述の記事](https://qiita.com/17021084/items/e1078bacc2fbc32d3183#4%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89)では、ユーザーのクレデンシャルを使用してS3へのアップロードやダウンロードを行いましたが、これはベストプラクティスではありません。本番環境では、クレデンシャルが漏れるリスクがあるためです。

ベストプラクティスとしては、適切な権限を持つロールを作成し、そのロールをLambdaやEC2などのAWSリソースに付与することです。

### 2.1 ボリシー作成
**手順**

    1. IAMサービスに入る -> 左メニューにてポリシーを選ぶ
    2. ポリシー作成を押下
    
![create polici.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/5fe8e92a-8980-4538-4fbe-2f7d32d6d3a0.png)

    3.JSONを選んでください
![create policy step1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/f2bfadce-f0ad-d30f-eafb-be014709cdb2.png)

最小特権アクセス（Least Privilege）によって

| サービス | 対象| 権利 |
|:-:|:-:|:-:|
| Cloud watch | 全て   | ログ記録  |
| S3   | 一時的なバケツ  <br> ```temp-images-bucket```  | 画像読み込む（GET)  |
| S3   | 主なバケツ名 <br> ```final-images-bucket```  | 画像アップロード（PUT) |

```json-doc
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"logs:PutLogEvents",
				"logs:CreateLogGroup",
				"logs:CreateLogStream"
			],
			"Resource": "arn:aws:logs:*:*:*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"s3:GetObject"
			],
			"Resource": "arn:aws:s3:::<一時的なバケツ名（貴方次第）>/*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"s3:PutObject"
			],
			"Resource": "arn:aws:s3:::<主なバケツ名（貴方次第）>/*"
		}
	]
}
```

    4. ポリシー名を設定して完了します。

*記事では```demo-resized-image-s3-policy```を設定しました*

### 2.2ロール作成
**手順**

    1. 左メニューにロールを押下

![TAO ROLE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/64a7e9c4-e638-783a-1791-a5953be09a9c.png)

    2.以下のように選択
![tạo role step1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/6f65463a-7597-eddf-c3d7-4b111385ab24.png)

    3.次は上に作成したポリシー```demo-resized-image-s3-policy```を選んでください

*(サーチで検索した方がいいです)*

![chọn policy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/23702640-0a4e-7928-85b9-fd0f380d122f.png)

    4.最後ステップはロール名を設定
![review dat ten role.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/11af548e-a4b1-e513-1f8f-b546ffd4f57e.png)

## 3. ラムダファンクション作成
### 3.1 環境構成

#### 3.1.1 初期設定
**手順**

    1. 以下のように設定
    2. Advance settingをそのままにします



![setting lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/f452fe60-b0c9-58fb-4fa3-21029d80de14.png)

:::note warn
以下のSharpライブラなので、Architerture の設定を注意してください。
:::

### 3.2 ファンクション設定

#### 3.2.1 ロールや処理時間を設定

**手順**

     1. Configuration -> Permission -> Edit

![permissonst1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/aaf7d9f2-6e51-1729-68ea-7ded4047ca4a.png)

    2. 設定画面に遷移します。

![permission2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/f415728e-4a4f-1e69-20e3-853451d29c85.png)

| 項目 | 意味 |
|:-:|:-:|
|  Memory  |   重い画像を処理する場合は高い値を設定 |
|   Time out  |  画像処理は時間が結構かかって、念の為1分を設定します。設定しない場合は以下のエラーのように出てくる <br>　```"errorMessage": "2018-04-26T08:40:15.398Z …..Task timed out after 3.00 seconds"```|
|   Role | ロールをファンクションに付ける<br>設定しない場合は```Access deny``` というエラーが出て来る |




#### 3.2.2 トリガー設定

**手順**

    1. 左メニューにてTriggers 
    2. Add trigger
    3. 以下のように設定*

![trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/41f672ed-e6cf-0805-8cd4-dbf02d6ffeda.png)

:::note alert
バケツはなぜ2台が必要ですか？
:::


**理由：** recursive invocationを避けます。サークルトリガー形でラムダが繰り替えてトリガーして止まりません。したがって、利用金がすごく発生します

```mermaid
graph TD;
    画像-- PUTイベン-->バケツA;
    バケツA--処理ずみ-->画像;
```

#### 3.2.3環境変数

**手順**

    1. 左メニューにて**Environment variables 
    2. EDIT
    ３.以下のように設定

![env.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/3fd73102-14ba-d96f-4c35-0f66bbc7b8fd.png)


### 3.3 ラムダのNode modulesについて
:::note warn
ラムダでライブラを輸入するために、Node modulesをアップロードが必要です
:::

方法としては２つあります

- **処理ファイルと```Node modules```を圧縮してアップロードします**
    
![upload code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/d47cf82b-46b3-61bf-1867-f7f8c01ac7eb.png)

:::note info
こちらファンクションしか利用できません。そのため一番良い方向ではありません。
:::

-  **Layer の方向**

そうしたら他のファンクションに共有できます。ファンクションサイズを下げて料金が下がる

**手順**

    1. ローカルにてライブラリをダウンロード

:::note alert
Sharp は環境によってエラーが発生しますか。しっかりOSとCPUを指摘するのは必要です
:::

```bash
## 2. ローカル環境で適当なOSとCPUをダウンロード
npm install --os=linux --cpu=x64 sharp
```


    3.「node_modules」を圧縮します

![compress.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/c6a09548-0b21-ae41-3927-6f512f33e98c.png)

    4.レイヤを作成


![created layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/bbe1fbf4-8da5-f796-47bd-40400e73a70e.png)


    5.環境区別のため、「compatible architertures」指摘した方がいいです

![tao layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/efcb58ae-bac4-c32f-c13a-97ba3ecd94bd.png)

私は**for-test-sharp**を既に作成ました。

    6.ラムダに戻って下スクロールしてレイヤを追加しよう

![add layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/04857d20-95d2-75f9-0017-49c28dcb5d81.png)

    7.作成したレイヤのARNを貼り付け

![add layor 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/a8ec88c4-9cd3-3f1f-0fb5-43da985b4859.png)


### 3.4 処理ソースコード


:::note warn
- node_modulesがレイヤに保存していますから、先頭のパーツは```/opt/node_modules/<lib>/...```
のようにする
- デプロイを押下しないと新しいコードが反映されません
:::


```javascript
import {
  S3Client,
  GetObjectCommand,
  PutObjectCommand,
} from "@aws-sdk/client-s3";
import Sharp from '/opt/node_modules/sharp/lib/index.js'; 

const IMAGE_HEIGH = parseInt(process.env.IMAGE_HEIGH) || 200;
const IMAGE_WIDTH = parseInt(process.env.IMAGE_WIDTH) || 200;
const DEST_BUCKET_NAME = process.env.DEST_BUCKET_NAME;
const S3 = new S3Client();

export const handler = async (event,context) => {
  try {
    //GET object name from event
    const { eventTime, s3 } = event.Records[0];
    const srcKey = decodeURIComponent(s3.object.key.replace(/\+/g, " "));
    const srcBucket = s3.bucket.name;
    
    // ログ出す
    console.log(`${eventTime} --------- ${srcBucket}/${srcKey}`);
    
    // 画像をダウンロード
    const commandGET = new GetObjectCommand({
      Bucket: srcBucket,
      Key: srcKey,
    });
    const {Body, ContentType} = await S3.send(commandGET);
 

    //画像処理
    const image = await Body.transformToByteArray();
    const imageBuffer = await Sharp(image)
      .resize({
        height: IMAGE_HEIGH,
        width: IMAGE_WIDTH,
      })
      .toBuffer();
    
    // 処理済み画像を主なバケツにアップロード
    const commandPUT = new PutObjectCommand({
      Bucket: DEST_BUCKET_NAME,
      Key: srcKey,
      Body: imageBuffer,
      ContentType
    });
    
    await S3.send(commandPUT);
    const message = `Successfully resized ${srcBucket}/${srcKey} and uploaded to ${DEST_BUCKET_NAME}/${srcKey}`;
    console.log(message)
    return {
      statusCode: 200,
      body: message,
    };

  } catch (error) {
    console.error('error-------->',error);
  }
};
```

```const handler = async (event,context)```

どのようにeventの詳細を見ますか？


    1. [Test]
    2. [Configure test event]
    3. TemplateにてS３put 選んで
    → eventはeven JSON に書いていあります。

![checkevent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/30bea80b-0cff-07e6-21d7-eb1c26c5d3f1.png)



![check event 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/929d240c-46b1-2f85-f1c8-8ff1ad1f8598.png)

## ４テスト行い

### 4.1  S3アップロード

**手順**

    1.  S3サービスに入る
    2. 一時的なバケツ(temp-images-bucket）を選ぶ　

![add file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/b86b609d-0758-4957-dbbd-735d020c6896.png)

![chon file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/4a704017-697b-74b0-eda2-f53ee9919cfb.png)

### 4.2 結果をチェック

**手順**

    1. 主なバケツに入る（final-images-bucket)
    2. 画像を選んで
    3. Open押下

![xem file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/5b8e0813-f428-0f48-ee41-81b347cf4d52.png)

### 4.3 ログをチェック

**手順**

    1.CloudWatch サービスに入る
    2.Log groups
    3.AWSは既にグループを作成してくれます。
    
グループのパターン：```/aws/lambda/<ラムダファンクション名＞```

![log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/abf075ad-d366-7621-a3f4-da1a40a196ae.png)

時間合わせてログを開けてログをチェックします


### 最後

最後までご覧いただき、誠にありがとうございました。
もし記事にご興味や意味を感じていただけましたら、ハートやコメントを残して共有していただけると嬉しいです。
:bow::bow::bow: