はじめに
AWS上でのシステムを構築する際に簡単な挙動をローカル環境で確認したいという要望はちょくちょく聞きます。そのときによく使われるのがLocalStackです。
有償版・無償版それぞれあるのですが、無償版でも多くの機能が使えます。詳細は以下を参照。
今回はこのLocalStackの無償版を使ってRuby SDKでS3の操作を試してみます。
前提
以下のバージョンで試しました。
LocalStackの準備
LocalStackのセットアップはDockerを使うと簡単です。以下のようなcompose.yml
を作成しました。
version: "3.8" services: localstack: container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}" image: localstack/localstack:3.3.0 ports: - "127.0.0.1:4566:4566" environment: - DEBUG=${DEBUG:-0} volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock" app: build: . tty: true volumes: - .:/myapp/
アプリの準備
アプリ検証用としてDockerfile
も用意しておきます。
FROM ruby:3.3.0-slim WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY lib /myapp/lib
gemの管理用としてGemfile
を以下の内容で作成します。
source 'https://rubygems.org' gem 'aws-sdk-s3', '~> 1.146.1'
Gemfile.lock
は空ファイルとして作成します。
アプリはlib/app.rb
として準備しておきます。今回はこんな感じで。
require 'aws-sdk-s3' region = "us-east-1" bucket_name = "bucket01" key_name = "key01" Aws.config.update( endpoint: "http://localstack:4566", access_key_id: "test", secret_access_key: "test", region: region, force_path_style: true ) client = Aws::S3::Client.new # バケットを作成 client.create_bucket(bucket: bucket_name) p client.list_buckets # オブジェクトを作成 client.put_object(bucket: bucket_name, key: key_name, body: "Hello World") # オブジェクトを読み込み p client.get_object(bucket: bucket_name, key: key_name).body.read # オブジェクト情報一覧 p client.list_objects_v2(bucket: bucket_name)
実行
準備が整えば、docker compose up
を実行するとアプリが起動します。起動しているコンテナのうち、appコンテナに接続してruby lib/app.rb
を実行するとAWS SDK S3 gemを利用したS3の動作が確認できます。
## ↓ client.list_bucketsの実行結果 #<struct Aws::S3::Types::ListBucketsOutput buckets=[#<struct Aws::S3::Types::Bucket name="bucket01", creation_date=2024-04-07 04:47:48 UTC>], owner=#<struct Aws::S3::Types::Owner display_name="webfile", id="xxxxx">> ## ↓ client.get_objectの実行結果 "Hello World" ## ↓ client.list_objects_v2の実行結果 #<struct Aws::S3::Types::ListObjectsV2Output is_truncated=false, contents=[#<struct Aws::S3::Types::Object key="key01", last_modified=2024-04-07 05:18:59 UTC, etag="\"yyyyy\"", checksum_algorithm=[], size=11, storage_class="STANDARD", owner=nil, restore_status=nil>], name="bucket01", prefix="", delimiter=nil, max_keys=1000, common_prefixes=[], encoding_type=nil, key_count=1, continuation_token=nil, next_continuation_token=nil, start_after=nil, request_charged=nil>
考察・注意点
LocalStackはあくまで他ベンダーによるエミュレータですので、挙動がAWSのものと一致しているとは限らないのは注意が必要です。
また、開発用としてAws.config.update
でendpoint
やaccess_key_id
などは個別に設定する必要があり、環境変数化などのなんらかの対処は必要になるかなと思います。
最後に、Aws.config.update
で指定しているforce_path_style: true
についてです。S3はPath styleとVirtual hosted styleの2種類のアドレスモデルがあり、Path styleは廃止予定となっています。
force_path_style: true
の記述は、Path styleでアクセスすることを指定するものです。この記述がない場合、以下のエラーメッセージが出力されます。
/usr/local/lib/ruby/3.3.0/net/http.rb:1603:in `initialize': Failed to open TCP connection to bucket01.localstack:4566 (getaddrinfo: Name or service not known) (Seahorse::Client::NetworkingError)
なんらかの回避方法を探しているのですが、この記事を書いているときには見つけることができませんでした。