🐷

PythonでlocalStack(Docker)のS3オブジェクトを取得する

2023/02/27に公開

AWSリソースをローカル環境で使うことができるlocalStackを試しに使ってみました。

https://localstack.cloud/

環境

機種 : MacBook Pro 2021(M1 Max)
OS : Monterey(12.2.1)
Python version(Docker): 3.11.2
pip version(Docker): 22.3.1
awscli-local version: 0.20

各種インストール

localStackのインストール

localStackを記載ある方法通りインストールします。が、Dockerコンテナを立てるので、なくてもまぁいい。

https://docs.localstack.cloud/getting-started/installation/

$ brew install localstack

awscli-localのインストール

awscli-localをインストールします。awsコマンドを使ってもlocalStackのリソースへアクセスできますが、毎度endpoint-urlを指定せねばなりません。awscli-localコマンドだと長ったらしいコマンドにならずに済みます。

https://docs.localstack.cloud/user-guide/integrations/aws-cli/#localstack-aws-cli-awslocal

https://github.com/localstack/awscli-local

$ pip install awscli-local
$ pip list | grep aws
awscli-local      0.20

DockerでlocalStackを構築する

DockerコンテナでlocalStack環境を構築します。良さげなdocker-compose.ymlのサンプルがあったのでありがたく、参考に使わせてもらいました。

https://github.com/localstack/localstack/blob/master/docker-compose.yml

以下は自分で追記しました。後ほど、Python環境のDockerコンテナからアクセスできるようにネットワークの設定をしています。

docker-compose.yml
+   networks:
+     - sample-app-network
+
+networks:
+ sample-app-network:

次のようなdocker-compose.ymlになります。

docker-compose.yml
version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"            # LocalStack Gateway
      - "127.0.0.1:4510-4559:4510-4559"  # external services port range
    environment:
      - DEBUG=${DEBUG-}
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR-}
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
    networks:
      - sample-app-network

networks:
  sample-app-network:    

起動します。

docker compose up -d

localStackでS3バケットとオブジェクトを作成する

ドキュメントを見つつ、localStackにS3バケットとオブジェクトを作成していきます。

https://docs.localstack.cloud/user-guide/aws/s3/

余談ですが、普段はs3コマンド(aws s3 ...)を利用しているのですが、ドキュメントではs3apiコマンドを利用していて、「何が違うんだってばよ!」と思い調べてみました。
以下の通り、s3apiコマンドよりs3コマンドのほうが高レベルな操作が可能なようでした。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-s3-apicommands.html

S3バケットを作成します。

$ awslocal s3 mb s3://first-sample-test
make_bucket: first-sample-test

$ awslocal s3 ls
2023-02-23 21:53:41 first-sample-test

ファイルのアップロードしてみます。次のようなhtmlファイルを作成しました。

hello.html
<p>hello world, localstack!</p>

CLIで先程作成したS3バケットにアップロードします。

$ awslocal s3 mv hello.html s3://first-sample-test/
move: ./hello.html to s3://first-sample-test/hello.html

$ awslocal s3 ls s3://first-sample-test/
2023-02-23 21:58:51         31 hello.html

試しにcurlしてみたら、取得できました。仕組みはいまいちよく分かっていない。。。

$ curl http://localhost:4566/first-sample-test/hello.html
<p>hello world, localstack!</p>

dockerでpython環境を構築する

先程作成したdocker-compose.ymlにPython環境を追記します。

docker-compose.yml
...
    networks:
      - sample-app-network
      
+  python3:
+    restart: always
+    image: python:3.11.2
+    container_name: "my-python"
+    tty: true
+    volumes:
+      - "./src/:/root/src/"
+    networks:
+      - sample-app-network

networks:
  sample-app-network:

また、S3オブジェクトにアクセスするためのpythonファイルを作成します。ディレクトリ構成は次の通り。

./
├── docker-compose.yml
└── src
   └── s3_sample.py

今回はシンプルにファイルを取得して、printするだけです。region_nameは文字列であればどんな値を入れておいても取得できるぽいです(localStackがそういうものなのかなぁ。。。)

s3_sample.py
import boto3

s3 = boto3.client(
    's3',
    region_name='dummy',
    # endpoint_url='http://localhost:4566'
    # dockerコンテナ内で実行する場合はこちらを利用する
    endpoint_url='http://localstack:4566'
)

response = s3.get_object(Bucket='first-sample-test', Key='hello.html')
body = response['Body'].read().decode('utf-8')
print(body)

起動します。

$ docker compose up -d

コンテナ内に入ってS3へ接続するときに使うboto3をインストールしておきます。

$ docker compose exec python3 bash

$ pip install boto3

pythonでS3ファイルを取得する

コンテナ内に入ったまま、s3_sample.pyのある場所へ移動します。AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYが必要ですが、文字列を設定しておけばなんでも良いみたいでした。

$ cd root/src/

$ export AWS_ACCESS_KEY_ID=ACCESSKEY123

$ export AWS_SECRET_ACCESS_KEY=SECRETKEY123

$ python s3_sample.py
<p>hello world, localstack!</p>

感想

AWSのS3に接続してファイルを取得するという処理のコードを書いていたのですが、開発中だと他の人も同じファイルを参照していたら、ファイル名をいちいち変更して開発したりしていたので、そういうバッティングすることがあればlocalStackを用いて開発してもよいかもしれません。
設定がガバガバでも疎通できるので、慣れると便利ですが逆にガバガバなため、設定の不備などを見落としてしまうんじゃないかなと思いました(今回だとIAMやリージョン名など)。
まだまだ使いこなせていないので、時折触ってみようと思います

株式会社ゆめみ

Discussion