ActiveStorageにLocalStackのS3を利用するまでの手順

結構苦戦したので忘備録として書いとく。

### LocalStackとは
この辺よんでください。
https://github.com/localstack/localstack
https://qiita.com/rio_matsui/items/e0c2c772d4579d00a312

### LocalStackの設定
とりあえずdocker-compose.ymlには下記のように書く

```Yaml
  localstack:
    image: localstack/localstack:0.12.1
    environment:
      SERVICES: s3
      AWS_ACCESS_KEY: 1234
      AWS_SECRET_ACCESS_KEY: 12345678
      DEFAULT_REGION: ap-northeast-1
      DATA_DIR: /data
      START_WEB: 0
      HOSTNAME_EXTERNAL: localstack
      INIT_SCRIPTS_PATH: /root/init
    volumes:
      - localstack-data:/data
      - ./docker/localstack:/root/init
    ports:
      - 4566:4566

volumes:
  localstack-data:
    driver: local
```

以下説明

#### 環境変数

```
SERVICES: s3
```
ここには使いたいサービスを記述する、複数使いたい場合はカンマ区切りで書く。

```
AWS_ACCESS_KEY: 1234
AWS_SECRET_ACCESS_KEY: 12345678
```
ここは適当でいい、SECRETは8文字以上じゃないと怒られる

```
DEFAULT_REGION: ap-northeast-1
```
ここもとりあえずお好みで。

```
DATA_DIR: /data
```
永続化させるデータの保存場所、Volumeを作成してこのディレクトリにマウントさせている。

```
START_WEB: 0
```
LocalStackには管理画面みたいなものが一応あるが、
使いたい場合はlocalstack/localstack-fullというイメージを使う必要がある。
localstack/localstackだと管理画面は使えないのだがログをみると起動しようとしてエラーを吐いているので無効にする。

```
HOSTNAME_EXTERNAL: localstack
```
理由はたぶんここに書かれているのと一緒。
これを書かないとRails側のコンテナからアクセスができない。
https://qiita.com/imunew/items/b74cecb7e12a9b9c4441

```
INIT_SCRIPTS_PATH: /root/init
```
初期化用のスクリプトの配置場所
ここに下記のようなのスクリプトを書いて置いておくと起動時に実行してくれる。

```Bash
cd /root/init
# コンテナにはawslocalが入ってるのでそちらを使ってBucketを作成する
awslocal s3 mb s3://test-bucket
```

今回は作成したスクリプトをコンテナ側にマウントして実行するようにしている。

```Yaml
      - ./docker/localstack:/root/init
```


ここに書いてない環境変数はここを参照
https://github.com/localstack/localstack#configurations

#### Port
```Yaml
    ports:
      - 4566:4566
```
LocalStackでは4566でアクセスを受け付けているのでポートフォワーディングを設定いれておく
ちなみにawscliで叩く場合は下記のようにEndpointを指定する。

```Bash
aws --endpoint=http://localhost:4566 s3 ls s3://test-bucket --recursive
```
コンテナ内からの場合はawslocalコマンドが入ってるのでそちらを使うほうが楽。

これで立ち上げるとS3にBucketが作成されているはず。
```
aws --endpoint=http://localhost:4566 s3 ls                                                                            
2020-11-02 10:12:10 test-bucket
```

### Rails側の設定
ActiveStorageでS3を使うための設定はこのあたりのページを参照。
https://qiita.com/tsubasan1122/items/0171fe04754a760f7e4a

ここではLocalStackを使うのに必要な設定だけを記載

#### storage.yml

```Yaml
localstack:
  service: S3
  access_key_id: 1234
  secret_access_key: 12345678
  region: ap-northeast-1
  endpoint: http://localstack:4566
  force_path_style: true
  bucket: test-bucket
```
以下説明

```Yaml
  service: S3
  access_key_id: 1234
  secret_access_key: 12345678
  region: ap-northeast-1
```
ここはLocalStack側の設定に合わせておく

```Yaml
  endpoint: http://localstack:4566
  force_path_style: true
```
awsのapiの叩き先をlocalstack側に変更するために設定する。
ただし、このままだとS3にアクセスする際にhttp://#{endpoint}.#{Bucket名}でアクセスしにいこうとするため
force_path_styleというオプションをtrueにして回避する。

これで、LocalStack側に保存されるようになったはず。
以下確認

``` Ruby
# 適当に画像を保存
file = "1.jpg"
content_type = 'image/jpg'
image = Image.first
image.image.attach(io: File.open(file), filename: File.basename(file), content_type: content_type)

# S3の画像のキーを確認
image.image.attachment.blob.key
"p1fp3sd0mqxscrvcx960nqa7caal"

# localstack側に画像が保存されているか確認
aws --endpoint=http://localhost:4566 s3 ls s3://test-bucket                                                              
2020-11-02 11:59:17      14845 p1fp3sd0mqxscrvcx960nqa7caal # 同一のキーが存在する
```

### その他
ファイルの保存はできるが、画像をブラウザに表示させようとしたときに取得されるURLのドメインが http://localstack:4566 になってしまう。
この辺は下記のページのようにURLを返すメソッドを作成して対応した。
https://qiita.com/shouta-dev/items/0b9569bf5522cca4cf3c