SNS + SQS + Lambdaのファンアウト構成でサムネイルを作成する

# やりたいこと
画像がS3にアップロードされたときに、SNS + SQSのファンアウト構成でサイズ毎のサムネイル画像を並列処理で作成する。

![スクリーンショット 2023-12-04 3.08.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/7b139a57-c4a2-afd3-cbea-bfbf0c556d72.png)


# SNSトピックを作成
`fanout-test` というトピックを作成します。
![スクリーンショット 2023-11-19 17.01.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/81fe443f-4dac-6f44-aa84-5576302bfe52.png)

# SQSを作成
`fanout-test-small` というSQSキューを作成し、`fanout-test`トピックにサブスクライブします。
![スクリーンショット 2023-11-19 17.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/b4c11edd-9774-6b7b-dab2-e921f7ab2671.png)

同様に、`fanout-test-large` というSQSキューを作成し、`fanout-test`トピックにサブスクライブします。

# S3バケットを作成
S3バケット`fanout-test-20231119`を作成し、`original` `small` `large`フォルダをあらかじめ作成しておきます。
![スクリーンショット 2023-11-19 17.21.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/feb3f75c-d8c7-990b-0932-f7dc98fc8c06.png)

# SNSのアクセスポリシーを編集
イベント通知の送信先にSNSトピックを選択する場合、送信先のSNSのアクセスポリシーの編集が必要なので
SNSトピック `fanout-test` に対して、下記のようなポリシーを追加します。
```json
    {
      "Sid": "S3-policy",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "SNS:Publish",
      "Resource": "arn:aws:sns:ap-northeast-1:<account id>:fanout-test",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:s3:::fanout-test-20231119"
        }
      }
    }
```

# イベント通知を作成
`original` フォルダにファイルがアップロードされたときにSNSトピック`fanout-test` に通知を送るような
イベント通知を作成します。
![スクリーンショット 2023-11-19 17.23.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/b89f6f6a-25e4-e3e2-2842-f24da3fd9a1e.png)
![スクリーンショット 2023-11-19 17.24.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/9f3ff22a-6fa7-949d-a394-62a069226725.png)

# ECRにイメージをpush

詳しくは下記を参照してください。

https://qiita.com/chain792/items/913c6d6e9ba91addb582

```:Dockerfile
FROM amazon/aws-lambda-ruby:latest

# EPELリポジトリを追加
RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

# remiレポジトリを追加
RUN yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm

# libvipsとlibvipsを使うために必要なパッケージをインストール
RUN yum install -y install vips make gcc

COPY Gemfile Gemfile.lock ${LAMBDA_TASK_ROOT}

COPY lambda_function.rb ${LAMBDA_TASK_ROOT}

# /usr/local/bundleにGemをインストールする
ENV GEM_HOME=/usr/local/bundle

RUN bundle install

CMD [ "lambda_function.lambda_handler" ]
``` 

```:Gemfile
source 'https://rubygems.org'
gem 'image_processing'
```

```ruby:lambda_function.rb
require 'image_processing/vips'
require 'aws-sdk-s3'
require 'json'

def lambda_handler(event:, context:)
  sizes = {
    "small" => 200,
    "large" => 600
  }

  # 画像データ取得
  s3_client = Aws::S3::Client.new(:region => 'ap-northeast-1')
  body = JSON.parse(event['Records'][0]['body'])
  message = JSON.parse(body["Message"])
  key = message["Records"][0]["s3"]["object"]["key"]
  uploaded_data = s3_client.get_object(:bucket => ENV['BUCKET_NAME'], :key => key).body.read

  # 画像リサイズ
  uploaded_file = Vips::Image.new_from_buffer(uploaded_data, '')
  size = sizes[ENV["SIZE"]]
  resized_file_path = ImageProcessing::Vips.source(uploaded_file).resize_to_limit!(size, size).path

  # アップロード実行
  s3_resource = Aws::S3::Resource.new()
  s3_resource.bucket(ENV['BUCKET_NAME']).object("#{ENV["SIZE"]}/#{File.basename(key)}").upload_file(resized_file_path)
end
```


# Lambdaを作成
ECRのイメージから `createSmallThumbnail` 関数を作成します。

Lambdaの実行ロールにSQSFullAccessとS3FullAccessを追加します。
![スクリーンショット 2023-12-04 3.02.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/a2c4e0d8-b515-49a9-8d24-32157f06ba48.png)

トリガーをSQS`fanout-test-small`に設定します。
![スクリーンショット 2023-12-04 3.02.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/65b16f38-e684-a2e6-55ce-bbdc3a12e7ee.png)

同様に`createLargeThumbnail`関数も作成します。

# S3に画像をアップロード

originalディレクトリに画像をアップロードすると、smallディレクトリ、largeディレクトリにサムネイル画像がアップロードされます。

![スクリーンショット 2023-12-04 3.05.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/0400b660-af43-b4b7-379f-11146b93270c.png)


![スクリーンショット 2023-12-04 3.05.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/6cd07cd7-74d6-e66a-a00e-e64e74db9a6c.png)

![スクリーンショット 2023-12-04 3.07.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736970/ddcda90b-4639-9ece-12ae-3eed4500998c.png)