今更ながらLocalStackを導入してみた

# はじめに
初めまして、[K.S.ロジャース](https://www.wantedly.com/companies/ks-rogers)の妹尾と申します🙋‍♂️

今回は私自身、存在は知っていて気になってはいたけど使ったことがなかった [LocalStack](https://www.localstack.cloud/) を実務で使う機会がありましたので自身の備忘も兼ねて導入手順や使ってみた感想などをまとめてみました。

今後導入を検討している方や LocalStack に興味のある方の参考になれば幸いです🙇‍♂️

誤った内容・助言等あればお気軽にコメントください！

# LocalStackとは
まず *LocalStackってなに？* という方もいらっしゃると思いますので簡単に説明します。

LocalStack とはAWSサービスをローカル環境にエミュレートするためのプラットフォームです。

LocalStack を導入することで、リモートのAWSサービスに接続せずに、AWS環境へデプロイしたアプリケーション相当の動作をローカル環境にて実現することができます。
AWSアカウントの作成すらも不要です。

しかも、機能制限等はありますが、無料での利用が可能です。
有料でしか利用できないサービスなどもありますので使いたいサービスに合わせて有料プランへのアップグレードが必要な場合もあります。

https://www.localstack.cloud/

https://docs.localstack.cloud/user-guide/aws/feature-coverage/

https://www.localstack.cloud/pricing

# 導入するに至った経緯
今回私が関わっているプロジェクトで LocalStack を導入することになった理由は以下です。

* ローカル環境でAWS環境相当の動作を確認したいけど複雑なアーキテクチャを採用している関係でローカル環境でシステム全体の動作確認ができない😢

LocalStack を導入するまでは擬似的に `debug` モード（ローカル環境モード）の時だけ動作するロジックをアプリケーションコードに埋め込むことで、本来とは異なる挙動ではありつつもシステムの機能としては動作を確認できる状態にしていました。

ただ、機能が増えていくにつれて、上記の対応では対応しきれなくなってきたため別の方法を探していました。

そこで LocalStack を採用すればローカル環境でAWS環境相当の動作を実現することができるのではないかということで導入が決定しました。

[Serverless Framework](https://github.com/serverless/serverless) などでも実現できるかもしれないですが、私の知見不足でできるとは判断できなかったため、今回は LocalStack を採用することになりました。

他にもこんなのがあるよっていう知見のある方は是非教えてください🙇‍♂️

# アーキテクチャ
今回のプロジェクトで採用しているアーキテクチャは以下の通りです。
* 管理画面Webサーバー
    * ECS Fargate(Golang)
* REST API
    * API Gateway -> Lambda(Golang)
* 非同期処理
    * DynamoDB Stream -> Lambda(Golang) -> SNS -> SQS -> Lambda(Golang)
* データベース
    * DynamoDB

この内、REST APIと非同期処理がローカル環境では完全に再現できない部分でしたのでその部分に LocalStack を活用しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318277/d562d67e-f7a7-a67b-c22f-3862d3731f48.png)

# 具体的な導入手順
私たちのプロジェクトではインフラ管理を [Terraform](https://www.terraform.io/) で行なっていた関係で、 LocalStack に対して Terraform を実行してAWS環境相当のリソースを作成することにしました。
[AWS SAM](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html)や[AWS CDK](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html)を使うことも可能なのでプロジェクトに合わせて導入の手順は異なると思いますが今回は Terraform を使った導入手順について記載します。

https://docs.localstack.cloud/user-guide/integrations/terraform/

https://docs.localstack.cloud/user-guide/integrations/aws-sam/

https://docs.localstack.cloud/user-guide/integrations/aws-cdk/

## LocalStackを起動する
今回は docker-compose を使用して LocalStack を起動します。
公式で docker-compose のスクリプトが公開されているのでこちらを参考にして作成してください。

https://docs.localstack.cloud/getting-started/installation/#docker-compose

私は以下のような `docker-compose.yml` を作成しました。

```docker: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-}
      - AWS_DEFAULT_REGION=ap-northeast-1 # awslocalコマンドのデフォルトリージョン指定
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
```

あとは起動するだけです😙
```bash
docker-compose up
```

## 既存の Terraform を LocalStack に適用するための修正
既存のテンプレートファイルは基本的にそのままは適用できないと思いますので、LocalStackに適用するために複製もしくは修正する必要があります。

私たちのプロジェクトでは Terraform を使っていましたが、環境毎に異なるテンプレートファイルを使っているというあまりよろしくない状態でした。（本来であれば共通化して環境毎に異なる値を変数化すべき）

かつ無料で LocalStack を使っており、 Terraform で管理しているリソースに有料でないと利用できないものが含まれていたため、仕方なく既存のファイルを複製して LocalStack 専用のテンプレートファイルを作成しました。

その他、 Terraform では非対応のリソースも存在するため、そちらも作成しないように修正する必要があります。

具体的には以下のようなリソースを作成しないようにしました。

* ELB
* ECS
* ECR
* CodeCommit
* CodeBuild
* CodePipeline

もしテンプレートファイルが共通化されている場合は `count` を使用してリソースの作成有無を判定するようにすることが可能ですのでそちらがおすすめです。

```terraform:main.tf
resource "hoge_project" "service" {
  count = var.is_cloud ? 1 : 0

  name       = "Hoge Service Production"
  project_id = "hoge-service-prod"
}
```

`count` については[こちら](https://tellme.tokyo/post/2022/06/12/terraform-count-for-each/)の記事が非常に参考になりました。

https://tellme.tokyo/post/2022/06/12/terraform-count-for-each/

## tflocal をインストール
[tflocal](https://docs.localstack.cloud/user-guide/integrations/terraform/#install-the-tflocal-wrapper-script) とは LocalStack に対して Terraform を実行するための小さなラッパースクリプトです。
tflocal をインストールしなくても[手動構成](https://docs.localstack.cloud/user-guide/integrations/terraform/#manual-configuration)することで LocalStack に対して Terraform を実行することが可能です。
今回は tflocal を利用するため公式ドキュメントの内容に沿ってインストールします。
```bash
pip install terraform-local
```

## LocalStack に対して Terraform を実行してリソースを作成
以下のコマンドを実行してリソースを作成します。

```bash
# ワークスペースの初期化
tflocal init

# 実行計画の確認
tflocal plan

# リソースの作成（オプションはお好みでどうぞ）
tflocal apply -auto-approve
```

基本的にはこちらの作業でリソース作成は完了になりますが、私のプロジェクトでは Terraform で Lambda にデプロイするソースコードは dummy（デプロイのためだけのコードをパッケージングしたもの） としてデプロイ後に最新のソースコードをデプロイするというフローを採用しているため、Lambdaのコードをデプロイする必要があります。
参考程度にそちらも記載しておきます。

## （おまけ） Lambdaのコードを更新
私たちはローカル環境でパッケージングしたコードを AWS CLI にてデプロイする方法を採用しています。
他にも [awslocal](https://docs.localstack.cloud/user-guide/integrations/aws-cli/#localstack-aws-cli-awslocal) コマンドを使って LocalStack のリソースに対してコマンドを実行することが可能です。

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

awslocal とは tflocal と同様小さなラッパーで、標準の `aws` コマンドの代替として利用できます。
awslocal を使うことで LocalStack に適用するための設定やパラメータ追加しなくても LocalStack 環境内で AWS CLI コマンドを実行できます。

`awslocal` コマンドを使っても良かったのですが、私たちのプロジェクトではローカル環境から LocalStack のリソースに対して `aws` コマンドを実行する機会は限定的だったため、今回は AWS CLI を採用しました。

ちなみに LocalStack コンテナ内には awslocal がデフォルトでインストールされているため、コンテナ内部で `aws` コマンドを実行する場合には `awslocal` コマンドを使っています。

前置きが長くなりましたが、Lambdaのコード更新は以下のような操作にて実施しています。

1. アプリケーションコード側でLambda用のファイルをビルドして zip ファイルを作成する。
1. aws cli にて zip ファイルを元に Lambda のコードを更新する。
   ```bash
   aws lambda update-function-code \
     --function-name "hoge-lambda" \
     --zip-file fileb:/./lambda_function/zip/hoge-lambda.zip \
     --publish \
     --endpoint-url=http://localhost:4566 \
     --region ap-northeast-1
   ```
   
私たちのプロジェクトではTerraformコードとアプリケーションコードは別の Git Repository にて管理しているため、ローカルで上記のような操作をしてなんとか Lambda のコードを更新しています。
Lambda のデプロイ周りについては何か良い方法があればご教授いただければ幸いです🙇‍♂️

# 使ってみた感想
## 総合的には？
LocalStack の導入によりできないことができるようになり、本来の目的を達成することができたので導入は成功だったと言えるかなと思います。
リモートの AWS 環境にデプロイしなくてもローカルで再現できて確認できるという点はやっぱり良いですね〜😁

## 使いやすさは？
導入した当初、私自身やメンバーの LocalStack を導入してみた感想は

> 毎回 LocalStack を使っての動作確認をするのは辛いな...

というのが本音でした🤔

* ログを確認するには？
    * CLI・・・😢
* リソースの情報を確認するには？
    * CLI・・・😢
* 何か動かないな〜？
    * CLIを使っての原因調査・・・😭

という感じで CLI でしか確認できない状態だったのが結構ツラミがありました。

ただ、 [LocalStack 3.0](https://blog.localstack.cloud/2023-11-16-announcing-localstack-30-general-availability/) がリリースされて [LocalStack Desktop](https://docs.localstack.cloud/user-guide/tools/localstack-desktop/) が登場したおかげでそのツラミが一気に解消されました。

まだまだリリースされたばかりなので、使いにくい箇所もあるとは思いますが、現状の使用感は CLI を使っていた頃に比べると **非常に良い** です 🙆‍♂️

LocalStack Desktop を使ったことない！という方は是非使ってみてください！

ということで使用感も現状はかなり改善されているため 7点/10点ぐらいはあるかなと感じています！

まだまだ LocalStack に対する知見も深めていく必要がありそうです。

https://blog.localstack.cloud/2023-11-16-announcing-localstack-30-general-availability/

https://docs.localstack.cloud/user-guide/tools/localstack-desktop/

# おわりに
今回は実務で LocalStack を導入した経験をもとに記事を書いてみました！

ローカルで再現しにくいアーキテクチャを採用している場合に LocalStack が最良の選択になるプロジェクトもあるかと思いますので、この記事の内容を参考に導入を検討してみてはいかがでしょうか？

この記事がどなたかの参考になれば幸いです！

[K.S.ロジャース](https://www.wantedly.com/companies/ks-rogers) では Wantedly でもブログ投稿をしています。
Techブログだけでなく会社ブログなども投稿しているので、気になった方はぜひ覗いてみてください😁

https://www.wantedly.com/companies/ks-rogers

では良いエンジニアライフを👋