これは、なにをしたくて書いたもの?
LocalStackをよく使っているのですが、起動時にLocalStack上のリソースを作る方法がないのかなと思って調べてみました。
初期化フック(Initialization Hooks)というものを使うと、LocalStackのライフサイクルに合わせて処理を追加できるようなので
こちらを試してみることにしました。
LocalStackのInitialization Hooks
LocalStackの初期化フックに関するドキュメントは、こちら。
LocalStackには4つのライフサイクルフェーズ(またはステージ)があるようです。
- BOOT … コンテナは実行されているが、LocalStackランタイムは開始されていない
- START … Pythonプロセスが実行中で、LocalStackランタイムが開始している
- READY … LocalStackはリクエストを処理する準備ができた
- SHUTDOWN … LocalStackがシャットダウンしている
このライフサイクルに合わせて、所定のディレクトリにシェルスクリプト(.sh
)またはPythonスクリプト(.py
)を配置しておくことで
各フェーズ(ステージ)でスクリプトを実行できるようです。
シェルスクリプトは実行権限が付与されている必要があります。
各フェーズ(ステージ)とディレクトリは、以下の対応になっています。
- BOOT …
/etc/localstack/init/boot.d
- START …
/etc/localstack/init/ready.d
- READY …
/etc/localstack/init/shutdown.d
- SHUTDOWN …
/etc/localstack/init/start.d
なお、LocalStackはDockerで動作するので、LocalStackが動作しているコンテナ内のディレクトリに配置する、ということになります。
ドキュメントにはDocker ComposeおよびLocalStack CLIでの設定方法が書かれています。
Initialization Hooks / Usage example
今回は起動時にLocalStack上にリソースを作成するということを試してみます。
環境
今回の環境は、こちら。
$ python3 -V Python 3.10.6 $ pip3 -V pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10) $ docker version Client: Docker Engine - Community Version: 24.0.2 API version: 1.43 Go version: go1.20.4 Git commit: cb74dfc Built: Thu May 25 21:51:00 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 24.0.2 API version: 1.43 (minimum version 1.12) Go version: go1.20.4 Git commit: 659604f Built: Thu May 25 21:51:00 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.21 GitCommit: 3dce8eb055cbb6872793272b4f20ed16117344f8 runc: Version: 1.1.7 GitCommit: v1.1.7-0-g860f061 docker-init: Version: 0.19.0 GitCommit: de40ad0 $ docker compose version Docker Compose version v2.18.1
$ awslocal --version aws-cli/2.12.6 Python/3.11.4 Linux/5.15.0-76-generic exe/x86_64.ubuntu.22 prompt/off
LocalStackコンテナ内を確認する
まずはLocalStackをインストールします。
$ pip3 install --upgrade localstack
バージョン。
$ localstack --version 2.1.0
起動。
$ localstack start
localstack_main
というコンテナ内でLocalStackは実行されているので、この中を少し確認してみましょう。
awsコマンドは入っているようです。
$ docker container exec -it localstack_main which aws /usr/local/bin/aws
/usr/local/bin
ディレクトリの中を見てみましょう。
$ docker container exec -it localstack_main ls -1 /usr/local/bin 2to3 2to3-3.10 __pycache__ aws aws.cmd aws_bash_completer aws_completer aws_zsh_completer.sh awslocal awslocal.bat docker-entrypoint.sh docker-java-home idle idle3 idle3.10 jp.py normalizer pip pip3 pip3.10 pydoc pydoc3 pydoc3.10 pyrsa-decrypt pyrsa-encrypt pyrsa-keygen pyrsa-priv2pub pyrsa-sign pyrsa-verify python python-config python3 python3-config python3.10 python3.10-config rst2html.py rst2html4.py rst2html5.py rst2latex.py rst2man.py rst2odt.py rst2odt_prepstyles.py rst2pseudoxml.py rst2s5.py rst2xetex.py rst2xml.py rstpep2html.py virtualenv wheel
awslocalも入っていますね。
バージョンを確認。
$ docker container exec -it localstack_main aws --version aws-cli/1.27.164 Python/3.10.12 Linux/5.15.0-76-generic botocore/1.29.164 $ docker container exec -it localstack_main awslocal --version aws-cli/1.27.164 Python/3.10.12 Linux/5.15.0-76-generic botocore/1.29.164
Pythonは3.10が入っていました。
$ docker container exec -it localstack_main python3 -V Python 3.10.12
初期状態では、/etc/localstack/init
ディレクトリにはなにも入っていないようです。
$ docker container exec -it localstack_main find /etc/localstack/init /etc/localstack/init
OSはDebianですね。
$ docker container exec -it localstack_main cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" NAME="Debian GNU/Linux" VERSION_ID="11" VERSION="11 (bullseye)" VERSION_CODENAME=bullseye ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/"
とりあえず、ざっくりと環境は確認できました。
LocalStack起動後にリソースを作成する
では、初期化フックを使ってみましょう。まずはLocalStackの起動後にリソースを作成してみたいと思います。
例に習って、init-aws.sh
というスクリプトを作成。
init-aws.sh
#!/bin/bash # S3バケットの作成 awslocal s3 mb s3://my-bucket # SQSキューの作成 awslocal sqs create-queue --queue-name my-queue
Initialization Hooks / Usage example
シェルスクリプトの場合、Shebangの記述(今回は#!/bin/bash
)が必須のようです。
実行権限を付与。
$ chmod a+x init-aws.sh
LocalStack CLIで指定する場合は、DOCKER_FLAGS
という環境変数を使うようです。
DOCKER_FLAGS
環境変数を使うとLocalStackをDocker内で動作させる時に、docker container run
にカスタムフラグを渡せます。
この時に、-v
オプションを使ってスクリプトをコンテナにマウントします。
起動。
$ DOCKER_FLAGS="-v $(pwd)/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh" localstack start
起動時のログを見ていると、リソースが作成されていることがわかります。
2023-07-01T13:07:59.822 INFO --- [ asgi_gw_0] localstack.request.aws : AWS s3.CreateBucket => 200 make_bucket: my-bucket 2023-07-01T13:08:00.558 INFO --- [ asgi_gw_0] localstack.request.aws : AWS sqs.CreateQueue => 200 { "QueueUrl": "http://localhost:4566/000000000000/my-queue" }
確認してみましょう。
$ awslocal s3 ls 2023-07-01 22:07:59 my-bucket $ awslocal sqs list-queues { "QueueUrls": [ "http://localhost:4566/000000000000/my-queue" ] }
確かに作成されているようです。
Docker Composeで行う場合は、こちら。
compose.yaml
services: localstack: image: localstack/localstack ports: - 4566:4566 volumes: - "./init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh"
AWS CLI v2をインストールする
別のサンプルとして、LocalStackに含まれているAWS CLIをv2にするようにしてみましょう。
軽い気持ちでやったら、だいぶ苦労しましたが…。
スクリプトを作成して
install-aws-cli-v2.sh
#!/bin/bash # AWS CLI v1をアンインストール # 相対パスのpip3だと、LocalStackのvenvのpip3を向いているため /usr/local/bin/pip3 uninstall -y awscli cd /tmp curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install rm -rf aws awscliv2.zip
実行権限を付与。
$ chmod a+x install-aws-cli-v2.sh
今回はBOOTフェーズ(ステージ)で実行するようにしました。
$ DOCKER_FLAGS="-v $(pwd)/install-aws-cli-v2.sh:/etc/localstack/init/boot.d/install-aws-cli-v2.sh" localstack start
$ docker container exec -it localstack_main aws --version aws-cli/2.12.6 Python/3.11.4 Linux/5.15.0-76-generic exe/x86_64.debian.11 prompt/off $ docker container exec -it localstack_main awslocal --version aws-cli/2.12.6 Python/3.11.4 Linux/5.15.0-76-generic exe/x86_64.debian.11 prompt/off
ちょっとこの部分を説明しておきます。
# AWS CLI v1をアンインストール # 相対パスのpip3だと、LocalStackのvenvのpip3を向いているため /usr/local/bin/pip3 uninstall -y awscli
LocalStackのAWS CLIは、pipでインストールされているAWS CLIを見てバージョン判定を行います。このため、AWS CLI v2を
インストールしてもAWS CLI v1をpip内で解決可能だとそのバージョンを見てしまいます。
このためAWS CLI v1をアンインストールする必要があるのですが、相対パスのpip3
を指定するとLocalStack管理のvenvのpip3を
見ているようなのでこのような形にしました。
まあ、ムリにv2に入れ替えようと思わない方がいい気がしてきましたね…。
起動時にリソースも一緒に作成する場合。
$ DOCKER_FLAGS="-v $(pwd)/install-aws-cli-v2.sh:/etc/localstack/init/boot.d/install-aws-cli-v2.sh -v $(pwd)/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh" localstack start
Docker Compose。
compose.yaml
services: localstack: image: localstack/localstack ports: - 4566:4566 volumes: - "./install-aws-cli-v2.sh:/etc/localstack/init/boot.d/install-aws-cli-v2.sh" - "./init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh"
ファイルの実行順を確認する
ディレクトリ内に複数のスクリプトを配置した際は、ソートされて実行されるようです。
https://github.com/localstack/localstack/blob/v2.1.0/localstack/runtime/init.py#L169
確認してみましょう。
ディレクトリを作成して
$ mkdir -p scripts/ready.d
こんなスクリプトを用意。
scripts/ready.d/01_echo.sh
#!/bin/bash echo 'first script'
scripts/ready.d/02_print_pip3_version.sh
#!/bin/bash pip3 -V
scripts/ready.d/03_hello.py
import boto3 print('print boto3 version:' + boto3.__version__)
数字の順で実行されるかどうかの確認ですね。
シェルスクリプトには、実行権限を付与。
$ chmod a+x scripts/ready.d/*.sh
確認。
$ DOCKER_FLAGS="-v $(pwd)/scripts/ready.d:/etc/localstack/init/ready.d" localstack start
結果。
first script pip 23.1.2 from /opt/code/localstack/.venv/lib/python3.10/site-packages/pip (python 3.10) print boto3 version:1.26.164
01、02、03の順で実行されたようです。
Docker Composeの設定も載せておきます。
compose.yaml
services: localstack: image: localstack/localstack ports: - 4566:4566 volumes: - "./scripts/ready.d:/etc/localstack/init/ready.d"
まとめ
LocalStackの初期化フックを使って、起動時に初期化処理を行えることを確認してみました。
この機能、今まで知らなかったので起動と同時にリソースを作成したい場合などに使ってみましょう。
Terraformで作ることもありますが、まあ使い分けですね。