localstackでS3をモックした

AWSDockerLaravelPHP開発環境

環境

localstack

  • 公式

    • AWSのサービスをモックしてくれる有能

      • S3とか
  • GitHub

    • ソースコード
    • 使い方
  • DockerHub

    • すぐ使えるやつ

docker-compose.yml

  • laradockのdocker-compose.ymlを直接触らず拡張する

laradock/.env

# Select which docker-compose files to include. If using docker-sync append `:docker-compose.sync.yml` at the end
COMPOSE_FILE=docker-compose.yml:../etc/docker-compose-additional.yml

etc/docker-compose-additional.yml

version: '3'

services:
  # LocalStack
  localstack:
    image: localstack/localstack:latest
    environment:
      - SERVICES=s3
      - DEFAULT_REGION=ap-northeast-1
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - ${DATA_PATH_HOST}/localstack:/tmp/localstack
    ports:
      - "9000:8080"
      - "4567-4578:4567-4578"
    networks:
      - backend

  # awscli
  storage_init:
    image: xueshanf/awscli
    environment:
      - AWS_DEFAULT_REGION=ap-northeast-1
      - AWS_DEFAULT_OUTPUT=json
      - AWS_ACCESS_KEY_ID=tekitou
      - AWS_SECRET_ACCESS_KEY=tekitou
    depends_on:
      - localstack
    volumes:
      - ../etc:/root
    entrypoint: /root/wait-for-it.sh localstack:4572 -- aws --endpoint-url=http://localstack:4572 s3 mb s3://sample.bucket/
    networks:
      - backend

localstack

  # LocalStack
  localstack:
    image: localstack/localstack:latest
    environment:
      - SERVICES=s3
      - DEFAULT_REGION=ap-northeast-1
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - ${DATA_PATH_HOST}/localstack:/tmp/localstack
    ports:
      - "9000:8080"
      - "4567-4578:4567-4578"
    networks:
      - backend
  • s3とかをモックする人
  • http:localhost:9000でダッシュボード閲覧できる
  • localstackが提供するAWSサービスごとにポートが割り振られる

    • 今回使用したS3は4572
  • workspaceと疎通するために、同じネットワークbackendに属させる
  • データを永続化させる場合はDATA_DIR環境変数の設定が必要

storage_init

  # awscli
  storage_init:
    image: xueshanf/awscli
    environment:
      - AWS_DEFAULT_REGION=ap-northeast-1
      - AWS_DEFAULT_OUTPUT=json
      - AWS_ACCESS_KEY_ID=tekitou
      - AWS_SECRET_ACCESS_KEY=tekitou
    depends_on:
      - localstack
    volumes:
      - ../etc:/root
    entrypoint: /root/wait-for-it.sh localstack:4572 -- aws --endpoint-url=http://localstack:4572 s3 mb s3://sample.bucket/
    networks:
      - backend
  • localstackの提供するs3に対してバケット作成命令を出すワンショットなコンテナ
  • ホンモノではないのでAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEYはどんな値でもよい
  • wait-for-itでlocalstackの準備完了を待っている

  • localstackと疎通するために同じネットワークbackendに属させる

localstack利用側のPHPコード

Storageファサードでs3使う

  • プラグイン入れる

    composer require league/flysystem-aws-s3-v3
  • src/config/filesystems.php
    ...
    'disks' => [
        ...
        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => true,
        ],
    ],
    ...
  • src/.env
...
# AWS s3 access
AWS_DEFAULT_REGION=ap-northeast-1
AWS_DEFAULT_OUTPUT=json
AWS_ACCESS_KEY_ID=tekitou2
AWS_SECRET_ACCESS_KEY=tekitou2
AWS_BUCKET=sample.bucket
AWS_ENDPOINT=http://localstack:4572

src/config/filesystems.php

  • 重要なのはここ
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => true,
  • とくにこれ
'use_path_style_endpoint' => true,
  • AWS/バケットの仮想ホスティング
  • S3のアクセス方法にはVirtual Hosted StyleとPath Styleとがある

    • Virtual Hosted Style http://sample.bucket.localstack:4572/
    • Path Style http://localstack:4572/sample.bucket/
  • Path Styleに切り替える設定

ハマりどころ

  • use_path_style_endpointはデフォルトfalse = Virtual Hoted Style
  • falseの場合、PHPのS3Clientがバケットにアクセスする際、
    下記ホストにアクセスされる:
http://sample.bucket.localstack:4572/
  • sample.bucket.localstackなんて名前知らねーよ」と当然エラーになる

    • curl通らない
  • docker-compose.ymlでエイリアスを定義すれば?
services:
  workspace:
    links:
      - localstack
      - localstack:sample.bucket.localstack
http://sample.bucket.localstack:4572/
↓
http://localstack:4572/
  • ↑これも駄目
  • 名前解決はできるようになるが、バケット名の情報が落ちるので、
    PHPコード側で毎回バケット名を指定しなければならなくなる
        Storage::disk('s3')->put(
            'sample.bucket/hoge.jpg',
            $imageData,
            'public'
        );