2022.10.11
LocalStack でローカル環境に EC2 と RDS を用意する
D.M. です。
こんな悩みはありませんか。
・AWS を勉強するためガチャガチャやりたいが、いきなり課金されるのが怖い。(無料が賄っている範囲を超えてしまうことがある)
こうした開発者向けに登場したプロダクト LocalStack が2022年7月にバージョン 1.0 になりました。
Announcing LocalStack 1.0 General Availability!
LocalStack には無料版と有料版(LocalStack Pro)があり、今回は有料版のトライアルを使ってみたので、そこから得た技術的知見と感想を書いていきます。
TL;DR
・LocalStack 無料版は Lambda、S3がローカルで動かせる。Terraform の練習ができる。
・LocalStack Pro の EC2 は Docker コンテナである。ネットワーク構成のコマンドは通るが、制約効果はない。
・RDS は LocalStack コンテナ内のプロセスとして立ち上がる。
LocalStack とは
LocalStack は2016年に有志が GitHub で始めたプロジェクトのようで、2021年に法人化されています。
公式ページ 会社紹介
https://LocalStack.cloud/company/
オープンソースなので GitHub で中身をのぞくことも可能です。見た感じPythonで書かれている模様。
https://github.com/localstack/localstack
無料版 LocalStack の特徴とメリット・デメリット
LocalStack は AWS をエミュレートしてローカルにテスト用の環境を構築できます。
無料版と有料版の機能一覧はこちらにあります。
https://docs.LocalStack.cloud/aws/feature-coverage/
無料版で特筆すべき点は Lambda と S3 が使えるというところです。
プログラミングは1から開発するときはどうしてもとりあえず書いてみて動かすといったことを繰りかえす必要がありますが、有料のAWS環境で雑に繰り返し実行するにはコスト面で不安があります。 LocalStack はサーバレス環境のローカル開発の手法として一石投じていると思います。
S3 と Lambda を取り扱ったブログ記事はいろいろあり、参考にさせていただきました。(たとえばこちら)
無料版でイタいのは、 EC2 と RDS は使えないところです。ただここは自前の Docker コンテナで EC2 と RDS の機能を代替させ、無理やり LocalStack Lambda と接続させてしまえばなんとかなったりするという意見も Web 上には散見されるので、無料版の利用用途は工夫の余地があると思っています。
LocalStack の内部構造
LocalStack は Docker コンテナとして動きます。
コンテナ内の構成要素
・LocalStack メインプロセス:AWS CLIを受け付けるAPIサーバみたいなもの
・他のプロセス:S3、Dynamo、Kinesis など
公式の図がわかりやすいのでそのまま引用。
https://docs.LocalStack.cloud/developer-guide/basics/
Lambda や EC2 は別の Docker コンテナとして立ち上がります。
https://docs.LocalStack.cloud/LocalStack/
まとめると。。
LocalStack 本体 = 独立した Docker コンテナ
RDS, S3, DynamoDB, Kinesis … = LocalStack コンテナ内の独立したプロセス
EC2, lambda = 独立した Docker コンテナ(本体とは別コンテナで動く)
なのでうまく動かないなとなった場合、最悪 Docker のコンテナを直接調査して操作すればなんとなく解決できるような構造になっています。
LocalStack のイケてないところ
AWS マネジメントコンソールの画面をブラウザでカチカチいじって構築ができません。
全部 aws cli でやる必要があります。逆に言うと API は全部通るので Terraform は実行できますし、その辺の開発環境としては充分に役に立ちます。
AWS マネジメントコンソールがないかわりに、独自の Cockpit という UI があります。
https://docs.LocalStack.cloud/get-started/cockpit/
メリット
・LocalStack main の起動など管理ができる。
・LocalStack main のログが読める(aws cli の実行結果ログ)
ただ Windows と Mac がまだベータ版なのと、私は今回 Windows WSL 上で動いている LocalStack を使っておりそこにつなぐことができなかったので使えませんでした。
awslocal
LocalStack に向けて aws cli を実行するうえで、ENDPOINT URL は LocalStack のサーバのIPを指定する必要があります。
そのときコマンドで –endpoint-url を毎回指定するのが面倒なので、以下のツールで簡易的に実行できます。
https://github.com/localstack/awscli-local
例えばこんな感じ。
aws –endpoint-url=http://localhost:4566 kinesis list-streams
↓
awslocal kinesis list-streams
本番との差別化で事故も減らせるので、是非こいつを使っていきましょう。
LocalStack Pro (有料版)
本題です。
やりたいことは以下の検証です。
- 1 EC2
- 1-1 立てられるか?
- 1-2 ネットワーク設定できるか?
- 2 RDS
- 2-1 立てられるか?
- 2-2 EC2 と通信できるか?
で、さっそく結論です。
- 1 EC2
- 1-1 〇 立てられた。
- 1-2 △✕ 設定値は登録されるが、期待したような通信制約はなさげ。
- 2 RDS
- 2-1 〇 立てられた。 Aurora Postgres, MySQL
- 2-2 〇 EC2 と通信できた。
私が実行した環境は以下の通りです。
環境
・Windows 10
・WSL 2
・Ubuntu 20.04.4 LTS
・docker 20.10
・docker-compose 2.11
・LocalStack 1.1
準備 LocakStacl を Pro 版で立ち上げる
前提としてProの申し込みが必要です。
Proを使うためにはアカウントを作ってログインし、 Account → Subscriptions でProのトライアルを選んで進むとAPIキーが発行されます。特にクレジットカード等の入力は不要でした。
次に LocalStack を Pro で立ち上げる必要があります。
これは公式に解説があります。
https://docs.LocalStack.cloud/get-started/pro/
これは動作時にキーを環境変数で渡してあればなんでもよく、
OS の環境変数
export LocalStack_API_KEY=your-api-key
docker run の引数
-e LocalStack_API_KEY=${LocalStack_API_KEY:- }
docker-compose ファイルのパラメータ
environment:
– LocalStack_API_KEY=${LocalStack_API_KEY- }
どのパターンでも大丈夫です。
今回は公式GitHub の docker-compose をベースに構築しました。
https://github.com/LocalStack/LocalStack
Pro 初回実行時の確認方法
LocalStack の health API で使える機能がわかります。主に以下が有料版で使える機能なので、ここが available やら running となっていたらOKです。
persistence
ec2
rds
私の実行結果です。
$ curl -s "$ENDPOINT_URL/health" | jq . { "features": { "persistence": "initialized", "initScripts": "initialized" }, "services": { "acm": "available", "apigateway": "available", "cloudformation": "available", "cloudwatch": "available", "config": "available", "dynamodb": "available", "dynamodbstreams": "available", "ec2": "running", ...
1 EC2
1-1 EC2 インスタンス(Pro版)を立ち上げる
結論としてはすぐにうまくいきました。
ミニマムでとりあえず立ち上げるには以下を行います。
1 image の取得とタグ付け
2 aws cli 実行
$ docker pull public.ecr.aws/amazonlinux/amazonlinux:2.0.20220912.1 docker tag public.ecr.aws/amazonlinux/amazonlinux:2.0.20220912.1 LocalStack-ec2/amazonlinux2:ami-amazonlinux2 awslocal ec2 run-instances --image-id ami-amazonlinux2
これで立ち上がります。
1点注意が必要なのが image_id です。
これはホスト OS の方で LocalStack-ec2 の接頭辞をつけた Docker Image のバージョン番号に相当します。
今回の場合は ami-amazonlinux2 を自分でつけています。
Image は ubuntu focal と amazonlinux2 でやったところ両方とも普通に動作しました。
( amazonlinux2022 でやったところ、立ち上がりましたが ssh キー登録に失敗しました)
1-2 ネットワークをしっかり設定してみる
以下公式のガイドに従って基本的なネットワーク設定コマンドを実行してEC2を構築します。
これも結論としてはコマンド自体は正常に動きました。
AWS CLI を使用して IPv4 対応 VPC とサブネットを作成する
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-subnets-commands-example.html
サマリーとしては以下の項目を設定します。
・VPC
・Subnet
・Internet Gateway
・Route Table
・Security Group
・KeyPair
(コマンドは長いので折り畳んであります)
# VPC 作成
# CIDR BLOCKを作る
awslocal ec2 create-vpc –cidr-block 10.0.0.0/16 –query Vpc.VpcId –output text
# VpcId
export VPC_ID=vpc-5b98d9fd
# サブネット1を作る
awslocal ec2 create-subnet –vpc-id $VPC_ID –cidr-block 10.0.1.0/24
export SUBNET_ID_1=subnet-c92f5e90
# サブネット2をつくる
awslocal ec2 create-subnet –vpc-id $VPC_ID –cidr-block 10.0.0.0/24 –endpoint-url=$ENDPOINT_ URL
# インターネットゲートウェイを作る(パブリックサブネットになる)
awslocal ec2 create-internet-gateway –query InternetGateway.InternetGatewayId –output text
export IGW_ID=igw-790b3244
# VPC と IG をくっつける
awslocal ec2 attach-internet-gateway –vpc-id $VPC_ID –internet-gateway-id $IGW_ID
# ルートテーブル作成
awslocal ec2 create-route-table –vpc-id $VPC_ID –query RouteTable.RouteTableId –output text
export RTB_ID=rtb-411d208f
# 全トラフィック 0.0.0.0 がIGを指すようにルート作成する。
awslocal ec2 create-route –route-table-id $RTB_ID –destination-cidr-block 0.0.0.0/0 –gateway-id $IGW_ID
# ルートテーブル確認
awslocal ec2 describe-route-tables –route-table-id $RTB_ID
# サブネット確認
awslocal ec2 describe-subnets –filters “Name=vpc-id,Values=$VPC_ID” –query “Subnets[*].{ID:SubnetId,CIDR:CidrBlock}”
# パブリックサブネットをルートテーブルに紐づける
awslocal ec2 associate-route-table –subnet-id $SUBNET_ID_1 –route-table-id $RTB_ID
# サブネットに起動されたEC2が自動的にIPを紐づけられる設定
awslocal ec2 modify-subnet-attribute –subnet-id $SUBNET_ID_1 –map-public-ip-on-launch
# キーペア作成
awslocal ec2 create-key-pair –key-name MyKeyPair –query “KeyMaterial” –output text > MyKeyPair.pem
chmod 400 MyKeyPair.pem
# ssh アクセス用セキュリティグループ
awslocal ec2 create-security-group –group-name SSHAccess –description “Security group for SSH access” –vpc-id $VPC_ID
export SG_ID=sg-356e095957483464b
awslocal ec2 authorize-security-group-ingress –group-id $SG_ID –protocol tcp –port 22 –cidr 172.16.0.0/12
export AMI_ID=ami-a4827dc9
# ec2 作成 10.0.1.0 のほうのサブネットに入れる
awslocal ec2 run-instances –image-id $AMI_ID –count 1 –instance-type t2.micro –key-name MyKeyPair –security-group-ids $SG_ID –subnet-id $SUBNET_ID_1
結論としては以下のように設定が完了しました。
$ awslocal ec2 describe-instances --instance-id i-4d9cc4bda8a9ef129 "Instances": [ { "AmiLaunchIndex": 0, "ImageId": "ami-amazonlinux2", "InstanceId": "i-4d9cc4bda8a9ef129", "InstanceType": "t2.micro", "KernelId": "None", "KeyName": "MyKeyPair", "LaunchTime": "2022-10-09T09:57:17+00:00", "Monitoring": { "State": "disabled" }, "Placement": { "AvailabilityZone": "ap-northeast-1a", "GroupName": "", "Tenancy": "default" }, "PrivateDnsName": "ip-10-0-1-9.ap-northeast-1.compute.internal", "PrivateIpAddress": "10.0.1.9", "ProductCodes": [], "PublicDnsName": "ec2-54-214-116-86.ap-northeast-1.compute.amazonaws.com", "PublicIpAddress": "54.214.116.86", "State": { "Code": 16, "Name": "running" }, "StateTransitionReason": "", "SubnetId": "subnet-c92f5e90", "VpcId": "vpc-5b98d9fd", "Architecture": "x86_64", "BlockDeviceMappings": [ { "DeviceName": "/dev/sda1", "Ebs": { "AttachTime": "2022-10-09T09:57:17+00:00", "DeleteOnTermination": true, "Status": "in-use", "VolumeId": "vol-2e059d9d" } } ], "ClientToken": "ABCDE0000000000003", "EbsOptimized": false, "Hypervisor": "xen", "NetworkInterfaces": [ { "Association": { "IpOwnerId": "000000000000", "PublicIp": "54.214.116.86" }, "Attachment": { "AttachTime": "2015-01-01T00:00:00+00:00", "AttachmentId": "eni-attach-2a097216", "DeleteOnTermination": true, "DeviceIndex": 0, "Status": "attached" }, "Description": "Primary network interface", "Groups": [ { "GroupName": "SSHAccess", "GroupId": "sg-356e095957483464b" } ], "MacAddress": "1b:2b:3c:4d:5e:6f", "NetworkInterfaceId": "eni-5d7635ca", "OwnerId": "000000000000", "PrivateIpAddress": "10.0.1.9", "PrivateIpAddresses": [ ...
EC2を立ち上げた直後にホスト OS の docker ps を見ると、以下のように EC2 が別のコンテナとして立ち上がっていることがわかります。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cc7ddaf79d14 LocalStack-ec2/amazonlinux2:ami-amazonlinux2 "sleep 43200" 20 minutes ago Up 20 minutes 0.0.0.0:41011->22/tcp, :::41011->22/tcp LocalStack-ec2.i-4d9cc4bda8a9ef129 946a193a8361 LocalStack-ec2/ubuntu-focal:ami-ubuntu "sleep 43200" 6 hours ago Up 6 hours 0.0.0.0:47355->22/tcp, :::47355->22/tcp LocalStack-ec2.i-156483f5cc20a3683 649129d9f464 LocalStack/LocalStack:latest "docker-entrypoint.sh" 4 weeks ago Up 22 hours (healthy) 0.0.0.0:53->53/tcp, :::53->53/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:4566->4566/tcp, :::4566->4566/tcp, 4510-4559/tcp, 0.0.0.0:4571->4571/tcp, :::4571->4571/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:53->53/udp, :::8080->8080/tcp, :::53->53/udp, 5678/tcp LocalStack_main
当然ですが docker exec でコンテナ内にログインできます。
$ docker exec -it LocalStack-ec2.i-4d9cc4bda8a9ef129 /bin/bash
また SSH も可能です。 aws cli で設定したキーがしっかり使えました。
$ ssh [email protected] -i MyKeyPair1.pem -v
EC2 立ち上げ時の LocalStack の Docker log を読むと以下がわかります。
・IP が割り振られている。(172.17.0.2)
・ssh が初期設定されている。(dropbearで入っているっぽい)
$ docker logs LocalStack_main | less 2022-10-10T04:27:50.590 DEBUG --- [ asgi_gw_1] l.s.ec2.vmmanager.docker : Launching instance i-4d9cc4bda8a9ef129 2022-10-10T04:27:50.591 DEBUG --- [ asgi_gw_1] l.u.c.docker_sdk_client : Running container with image: LocalStack-ec2/amazonlinux2:ami-amazonlinux2 2022-10-10T04:27:50.591 DEBUG --- [ asgi_gw_1] l.u.c.docker_sdk_client : Creating container with attributes: {'mount_volumes': [('/var/run/docker.sock', '/var/run/docker.sock')], 'ports': <PortMappings: {'22/tcp': 54063}>, 'cap_add': None, 'cap_drop': None, 'security_opt': None, 'dns': None, 'additional_flags': None, 'workdir': None, 'command': ['sleep', '43200'], 'detach': True, 'entrypoint': None, 'env_vars': {}, 'image_name': 'LocalStack-ec2/amazonlinux2:ami-amazonlinux2', 'interactive': False, 'name': 'LocalStack-ec2.i-4d9cc4bda8a9ef129', 'network': None, 'remove': False, 'self': <LocalStack.utils.container_utils.docker_sdk_client.SdkDockerClient object at 0x7ff9429d9c90>, 'tty': False, 'user': None} 2022-10-10T04:27:50.615 DEBUG --- [ asgi_gw_1] l.u.c.docker_sdk_client : Starting container cc7ddaf79d14ab8088061ff44befbb54ed1f0cc00f17b0ae03c39c4ea4e7fae0 2022-10-10T04:27:51.110 INFO --- [ asgi_gw_1] l.s.ec2.vmmanager.docker : Instance i-f112790c673b3f1e2 will be accessible via SSH at: 127.0.0.1:54063, 172.17.0.2:22 2022-10-10T04:27:51.110 DEBUG --- [ asgi_gw_1] l.u.c.docker_sdk_client : Listing containers with filters: None 2022-10-10T04:27:51.130 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Executing command in container LocalStack-ec2.i-4d9cc4bda8a9ef129: mkdir -p /var/log 2022-10-10T04:27:51.189 INFO --- [ asgi_gw_1] LocalStack.request.aws : AWS ec2.RunInstances => 200 2022-10-10T04:27:51.315 DEBUG --- [Thread-41233] l.s.ec2.vmmanager.docker : Starting ssh setup in container: LocalStack-ec2.i-4d9cc4bda8a9ef129 2022-10-10T04:27:51.315 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Copying file /var/lib/LocalStack/tmp/scp into LocalStack-ec2.i-4d9cc4bda8a9ef129:/usr/bin/scp 2022-10-10T04:27:51.353 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Copying file /var/lib/LocalStack/tmp/dropbear-sshd into LocalStack-ec2.i-4d9cc4bda8a9ef129:/usr/bin/dropbear 2022-10-10T04:27:51.395 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Executing command in container LocalStack-ec2.i-4d9cc4bda8a9ef129: ['mkdir', '-p', '/etc/dropbear'] 2022-10-10T04:27:51.430 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Executing command in container LocalStack-ec2.i-4d9cc4bda8a9ef129: ['sh', '-c', 'mkdir -p $HOME/.ssh; echo -n "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxfo+NcFxLKqyy1W+Q5BPtge0Gee4mpz3sjOhuhn7QzzH77AkDJm8oh8ZaK3Rnkrc/LLlGH7pcq+kipO7nPZ9g/N/t+yg1uo6i+CC293coR/42dnjOA+TgdMSTXd9hmdRoZsxU5jTbFoJ6mRj26Sp4aHlYFh+gL8b5SuLKgs7piy4yfJtdVqEHEKaD+USEzNhf/MYD6661lTPnPYWdL/GQbIxXc4VouJD/tU0h1vFY5Kjnba4nYxJFd1o+QZ2LxsIc9FlmnCYmPX1/HV1OBt4BL67FKYxjVaylo2CeE3ACc+IiNvGA93A3uKAIZ6u381MFnPMqOrFeDCph/wD50M9F" >> $HOME/.ssh/authorized_keys'] 2022-10-10T04:27:51.464 DEBUG --- [Thread-41233] l.u.c.docker_sdk_client : Executing command in container LocalStack-ec2.i-4d9cc4bda8a9ef129: ['/usr/bin/dropbear', '-R', '-p', '22'] 2022-10-10T04:27:51.498 DEBUG --- [Thread-41233] l.s.ec2.vmmanager.docker : Finished ssh setup in container: LocalStack-ec2.i-4d9cc4bda8a9ef129
EC2 の VPC を分けると通信はどうなるのか
EC2コンテナをポコポコ複数個量産し、その際に以下の変化をつけてみます。
・VPCを分ける
・Subnetを分ける
・セキュリティグループで22以外のポートを開けてみる
さてどうなったでしょうか。
期待値としては通信ができないような設定が追加されて欲しいところです。
ですが結論は、設定値としては作られますが、裏側の Docker コンテナの通信制約は特に生成されませんでした。
以下のように docker network の bridge に全 EC2 インスタンスがぶっこまれています。
$ docker network inspect bridge [ { "Name": "bridge", "Id": "32025d62064d219c823300358ce313fb6edfed894a945aee2946f323ef023272", "Created": "2022-10-03T19:11:48.803259032+09:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "3091fa994cfa33df4056157da275115341997d7a7585557ed058493f14fca7ac": { "Name": "LocalStack-ec2.i-4d9cc4bda8a9ef129", "EndpointID": "3c95cc592322b4d5cbc8f6cda33e65160b132040582108a0f76e87a0a86d6e51", "MacAddress": "02:42:ac:11:00:04", "IPv4Address": "172.17.0.4/16", "IPv6Address": "" }, "7778e2f6a670b05978089e54944243350d6cc4941a5973af43461673f98635fe": { "Name": "LocalStack-ec2.i-f82277fe83ab34d65", "EndpointID": "1a908fa777e4b332d620f3b76e5eb944e7a830307e6646f8cc48154680dc0c6a", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" }, "9c6217e10c6fdd05030eea0332b70ee9fe408ef00eeed9eea64e4173dc5da571": { "Name": "LocalStack-ec2.i-dd5a8dd8635f14f6e", "EndpointID": "c61e73036fba5e5297bbba33d9637e5eaf76461fc538edaaba3207664bbef954", "MacAddress": "02:42:ac:11:00:05", "IPv4Address": "172.17.0.5/16", "IPv6Address": "" }, "d7cd4280426f0ac2f54b8b5bcb8c58971f055bb235c8d4683e41a5a5297fce6c": { "Name": "LocalStack-ec2.i-cb182d093c26d4440", "EndpointID": "bcc2c81f66d83280ad1ea0ba415d3a05a088e7bb50af07a69c5269947f08130b", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ]
何種類かやりましたが iptables 的には以下のようにコンテナIPである 172.17.0.X のsshポート 22 が空いてるのみとなっています。
ACCEPT tcp -- anywhere 172.17.0.2 tcp dpt:ssh ACCEPT tcp -- anywhere 172.17.0.3 tcp dpt:ssh ACCEPT tcp -- anywhere 172.17.0.4 tcp dpt:ssh ACCEPT tcp -- anywhere 172.17.0.5 tcp dpt:ssh ACCEPT tcp -- anywhere 172.17.0.6 tcp dpt:ssh
期待値としては、aws cli でネットワークと ACL を作ってから通信ができませんねをテストができるとありがたかったのですが、そこまでは現状はできないようです。
VPC や Subnet を分けた場合であっても、デフォルトと同じ Docker ネットワークに含まれる状態になります。(デフォルトの bridge ネットワーク)
Docker コンテナのネットワークは最終的には iptables で確認できますが、VPC を分けても特別なルールは生成されず、ssh のポートが空いただけでした。
もしかしたら別のところで制約があるのかもですが、今回はそこが見つけられませんでした。
この点は新しい情報が入ればアップデートしたいと思います。
Python を入れてHTTP SERVERを動かしてみる。
EC2なのでWebアプリケーションも動かしてみます。
雑に手動でPythonを入れて、簡単な HTTP SERVER を実装します。
$ amazon-linux-extras install python3.9 $ vi http_server.py $ cat http_server.py from http.server import SimpleHTTPRequestHandler, HTTPServer server = HTTPServer(('', 8000), SimpleHTTPRequestHandler) server.serve_forever() $ python3.9 http_server.py
ホストOS側から curl でアクセス。
するとしっかり動作しています。
$ curl 172.17.0.2:8000 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Directory listing for /</title> </head> <body> <h1>Directory listing for /</h1> <hr> <ul> <li><a href=".dockerenv">.dockerenv</a></li> <li><a href="bin/">bin@</a></li> <li><a href="boot/">boot/</a></li> <li><a href="dev/">dev/</a></li> <li><a href="etc/">etc/</a></li> <li><a href="home/">home/</a></li> <li><a href="http_server.py">http_server.py</a></li> <li><a href="lib/">lib@</a></li> <li><a href="lib64/">lib64@</a></li> <li><a href="local/">local/</a></li> <li><a href="media/">media/</a></li> <li><a href="mnt/">mnt/</a></li> <li><a href="nohup.out">nohup.out</a></li> <li><a href="opt/">opt/</a></li> <li><a href="proc/">proc/</a></li> <li><a href="root/">root/</a></li> <li><a href="run/">run/</a></li> <li><a href="sbin/">sbin@</a></li> <li><a href="srv/">srv/</a></li> <li><a href="sys/">sys/</a></li> <li><a href="tmp/">tmp/</a></li> <li><a href="usr/">usr/</a></li> <li><a href="var/">var/</a></li> </ul> <hr> </body> </html>
python側のログもしっかり出ています。
172.17.0.1 - - [09/Oct/2022 05:22:38] "GET / HTTP/1.1" 200 - 172.17.0.1 - - [09/Oct/2022 05:41:41] "GET / HTTP/1.1" 200 -
まあこれは言ってしまえばただのDockerコンテナなので動くのは当然でした。
WSL 2 内の docker コンテナのポートをWindowのブラウザから参照するにはもうひとつ技が必要だと思いますがまあなんとかなるでしょう。
2 RDS
公式の情報によると RDS は MySQL. Postgres, SQL Server が使えます。(2022年10月現在)
https://docs.LocalStack.cloud/aws/rds/
まずは定石通り公式に出ているコマンドをそのまま実行してみます。
公式ではいきなり強気にローカルに aurora-postgresql を作ります。
$ awslocal rds create-db-cluster --db-cluster-identifier db1 --engine aurora-postgresql --database-name test { "DBCluster": { "AllocatedStorage": 1, "DatabaseName": "test", "DBClusterIdentifier": "db1", "Status": "available", "Endpoint": "localhost:4510", "MultiAZ": false, "Engine": "aurora-postgresql", "Port": 4510, "MasterUsername": "test", "StorageEncrypted": false, "DBClusterArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster:db1", "IAMDatabaseAuthenticationEnabled": false, "TagList": [] } }
aurora-postgresql が難なく生成できました。
ホストOSから接続してみます。
$ psql -d test -U test -p 4510 -h 172.18.0.2 -W Password: psql (11.17 (Ubuntu 11.17-1.pgdg20.04+1)) Type "help" for help. test=# create table test1 (id int ); CREATE TABLE test=# insert into test values (123) ; INSERT 0 1 test=# select * from test; id ---- 123 (1 row)
問題なく動作しました。
デフォルトで作られるのは postgres 11 でした。(11,12,13が選べる模様)
mysql の方も構築してコマンド実行してみます。
$ awslocal rds create-db-instance --db-instance-identifier db-mysql --engine mysql --db-instance-class db.t3.large --master-username test --master-user-password test { "DBInstance": { "DBInstanceIdentifier": "db-mysql", "DBInstanceClass": "db.t3.large", "Engine": "mysql", "DBInstanceStatus": "creating", "MasterUsername": "test", "DBName": "test", "Endpoint": { "Address": "localhost", "Port": 4511 },
ほとんど同じなので割愛しますが、デフォルトで入るのは MariaDB 5.5 のようでした。
RDS は LocalStack が動いてるDocker コンテナ内のプロセスとして動作します。
ps で見てみるとこんな感じ
$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 5484 3248 ? Ss Oct06 0:00 /bin/bash /usr/local/bin/docker-entrypoi root 14 0.0 0.2 31672 27052 ? S Oct06 0:29 /usr/local/bin/python /usr/local/bin/sup root 17 0.0 0.0 4084 680 ? S Oct06 0:00 tail -qF /var/lib/LocalStack/logs/locals root 19 0.3 2.3 4535152 296552 ? Sl Oct06 13:30 .venv/bin/python -m LocalStack.cli.main root 22 0.0 0.0 0 0 ? Z Oct06 0:00 [python] <defunct> root 8581 0.0 0.0 7892 2908 ? S Oct08 0:00 /bin/su LocalStack -c /usr/lib/postgresq localst+ 8582 0.0 0.2 210164 26116 ? Ss Oct08 0:04 /usr/lib/postgresql/11/bin/postgres -p 3 localst+ 8584 0.0 0.0 210308 8600 ? Ss Oct08 0:00 postgres: checkpointer localst+ 8585 0.0 0.0 210164 5748 ? Ss Oct08 0:01 postgres: background writer localst+ 8586 0.0 0.0 210164 9668 ? Ss Oct08 0:01 postgres: walwriter localst+ 8587 0.0 0.0 210704 6612 ? Ss Oct08 0:03 postgres: autovacuum launcher localst+ 8588 0.0 0.0 65216 5088 ? Ss Oct08 0:07 postgres: stats collector localst+ 8589 0.0 0.0 210564 7996 ? Ss Oct08 0:00 postgres: TimescaleDB Background Worker localst+ 8590 0.0 0.0 210560 6724 ? Ss Oct08 0:00 postgres: logical replication launcher root 13680 0.0 0.0 2392 760 ? Ss 13:18 0:00 /bin/sh -c mysqld --no-defaults --user=m mysql 13681 0.0 0.6 1761980 84392 ? Sl 13:18 0:00 mysqld --no-defaults --user=mysql --data root 14066 0.0 0.0 5748 3536 pts/0 Ss 13:24 0:00 /bin/bash root 14444 0.0 0.0 9388 3052 pts/0 R+ 13:29 0:00 ps aux
EC2 から RDS へ接続してみる
再び EC2 側に入り Python で DB 接続するだけのコードを書いてみます。
$ python3.8 Python 3.8.5 (default, Aug 16 2022, 20:22:19) [GCC 7.3.1 20180712 (Red Hat 7.3.1-13)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import psycopg2 >>> connection = psycopg2.connect("host=172.18.0.2 port=4510 dbname=test user=test password=test") >>> connection.get_backend_pid() 10754 >>> cur = connection.cursor() >>> cur.execute("select * from test") >>> for row in cur: ... print(row) ... (123,)
問題なく動作しています。
実はここで1回ハマりました。
EC2 と RDS が通信できませんでした(苦笑)
デフォルトではつながるのですが、私のケースでは少し特殊なことをしておりつながりませんでしたのでTipsとしてメモを残しておきます。
RDS は LocalStack のいるコンテナの中に立ち上がるプロセスなので、EC2内でそのLocalStack の IP を指してやればつながるはずです。
私は LocalStack を docker-compose で独自の Docker ネットワークを構築して立ち上げてしまっていました。
こうすると EC2 のデフォルトのネットワークである bridge 内からアクセスできなくなります。
こうしたケースでは個別にEC2のネットワークを LocalStack と同じものに着け直す必要があります。
docker network connect LocalStack-tutorial LocalStack-ec2.i-4258e122f244813bb
同じ問題が lambda にも起こりえますが設定で解決できるようになっています。 lambda はコンテナとして立ち上がるので、どのDockerネットワークに属するかを指定しないとRDSやS3に接続不能になります。 デフォルトの bridge 以外の場合は LAMBDA_DOCKER_NETWORK で指定することができます。(なんでこれがEC2側にないのか。。)
今回検証できなかったこと
・EC2のIAM連携
・RDSのメモリなどパラメータを変更
・RDSバックアップ
また LocalStack は 1.0 に際し面白い機能をいくつか出しています。これらも継続して検証していこうと思います。
・チーム内でのローカル環境の共有
・CI 環境でのテスト
2022年9月の段階で Roadmap ページがあり GCP 、 Azure のサポートなどの要望が上がっていましたが、今見たところページがなくなっていました。
Azure 対応については GitHub がありますがしばらく更新されていないように見えるので当面は動きがないかもしれません。
https://github.com/LocalStack/azure-cli-local
Roadmap は現状のディスカッションページに吸収されているようなので、今後はここでの盛り上がりに期待していきます。
https://discuss.LocalStack.cloud/
宣伝
次世代システム研究室では、最新のテクノロジーを調査・検証しながらインターネットのアプリケーション開発を行うアーキテクトを募集しています。募集職種一覧 からご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD