AWS SAMのDynamoDB Local接続のお試し(golang)

#はじめに
最近AWSのサービスを利用してシステム構築することに・・・
lambdaをメインにしたwebapiの開発をローカルで試した結果をまとめていきます。
本記事はローカルでAWS SAMを利用して、dynamodbとの連携までやります。
次の記事ではデプロイ周りをやります。

#利用していきます！

##今回使用するツールなどのバージョン
- Windows 10 Pro：21H1
- WSL2：Ubuntu 20.04 LTS
- vscode：1.61.2
- Docker：20.10.9
- Go: 1.17.3


##AWS SAM CLI のインストール
Homebrewを利用してAWS SAM CLIをインストールします。
AWSの公式サイトに[AWS SAM CLI のインストール](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)の手順がありますので、そちらを参考に。
※git、dockerはインストールしておいてください
※公式で提供されているpipパッケージを利用する方法もあります。この方法のほうがよい？（pip install aws-sam-cli）

###Homebrew をインストールする

* Homebrew をインストール（ちょっと時間かかるね）

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```

* 以下のコマンドを実行して PATH に Homebrew を追加します。

```
test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv)
test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile
echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile
```

* Homebrew がインストールされていることを確認します。

```
brew --version
```

```:結果
Homebrew 3.3.3
Homebrew/homebrew-core (git revision 6be0048502f; last commit 2021-11-14)
```

###Homebrew CLI を使用して AWS SAM をインストールする

* Homebrew を使用して AWS SAM CLI をインストールするには、以下のコマンドを実行します。

```
brew tap aws/tap
brew install aws-sam-cli

```

* インストールを確認します。

```
sam --version
```

```:結果
SAM CLI, version 1.35.0
```

* Homebrew を使用した AWS SAM CLI のアップグレード（インストールした時には既に最新なので実施は不要です）

```
brew upgrade aws-sam-cli
```

* AWS CLIもインストール

```
brew install awscli
```

* インストールを確認します。

```
aws --version
```

```:結果
SAM CLI, version 1.35.0
```

* AWS CLIの初期設定

```
aws configure
```

```
AWS Access Key ID [None]: アクセスキー ID
AWS Secret Access Key [None]: シークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: 
```

###goをインストール
公式サイトを見て、いい感じに実施してください笑
https://golang.org/doc/install


##hello-worldを実施してみる
AWS SAM を使用してサンプルの Hello World アプリケーションをダウンロード、構築、およびローカル実行を試します。
AWSの公式サイトに[チュートリアル: Hello World アプリケーションのデプロイ](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html)の手順がありますので、そちらを参考に。


###ステップ 1: サンプル AWS SAM アプリケーションをダウンロードする
* サンプル AWS SAM アプリケーションをダウンロードする

```
sam init
```

```:出力
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

Which runtime would you like to use?
        1 - nodejs14.x
        2 - python3.9
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs12.x
        8 - nodejs10.x
        9 - python3.8
        10 - python3.7
        11 - python3.6
        12 - python2.7
        13 - ruby2.5
        14 - java8.al2
        15 - java8
        16 - dotnetcore2.1
Runtime: 4

Project name [sam-app]: sam-app

Cloning from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
        1 - Hello World Example
        2 - Step Functions Sample App (Stock Trader)
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: sam-app
    Runtime: go1.x
    Architectures: x86_64
    Dependency Manager: mod
    Application Template: hello-world
    Output Directory: .

    Next application steps can be found in the README file at ./sam-app/README.md
    

    Commands you can use next
    =========================
    [*] Create pipeline: cd sam-app && sam pipeline init --bootstrap
```


###ステップ 2: アプリケーションを構築する
* アプリケーションを構築する

```
cd sam-app
sam build
↓Docker環境内でビルドする場合（AWSが提供するビルド用イメージを利用して）
sam build --use-container
```

```:出力
Building codeuri: ~/docker/sam-app/hello-world runtime: go1.x metadata: {} architecture: x86_64 functions: ['HelloWorldFunction']
Running GoModulesBuilder:Build

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
```


###ステップ 3: API をローカルでホストする
* API をローカルでホストする

```
sam local start-api
```

```:出力
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-11-14 20:36:28  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
```

* curlコマンドで確認

```
curl http://127.0.0.1:3000/hello
```
```:出力
Hello, 193.xxx.xxx.xxx
```

* ブラウザで確認　http://127.0.0.1:3000/hello

```:出力
Hello, 193.xxx.xxx.xxx
```

* ここまで実施したときのコンソール情報（参考までに）

```
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-11-14 20:36:28  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Invoking hello-world (go1.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-go1.x:rapid-1.35.0-x86_64.

Mounting ~/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 369cb9ac-0e64-4ed0-aa19-e413c3c4d983 Version: $LATEST
END RequestId: 369cb9ac-0e64-4ed0-aa19-e413c3c4d983
REPORT RequestId: 369cb9ac-0e64-4ed0-aa19-e413c3c4d983  Init Duration: 0.34 ms  Duration: 3119.01 ms   Billed Duration: 3120 ms Memory Size: 128 MB     Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2021-11-14 20:37:23 127.0.0.1 - - [14/Nov/2021 20:37:23] "GET /hello HTTP/1.1" 200 -
Invoking hello-world (go1.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-go1.x:rapid-1.35.0-x86_64.

Mounting ~/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: 476e014e-98ce-4adf-b127-b764fd15b7ea Version: $LATEST
END RequestId: 476e014e-98ce-4adf-b127-b764fd15b7ea
REPORT RequestId: 476e014e-98ce-4adf-b127-b764fd15b7ea  Init Duration: 0.29 ms  Duration: 4096.03 ms   Billed Duration: 4097 ms Memory Size: 128 MB     Max Memory Used: 128 MB
No Content-Type given. Defaulting to 'application/json'.
2021-11-14 20:38:28 127.0.0.1 - - [14/Nov/2021 20:38:28] "GET /hello HTTP/1.1" 200 -
2021-11-14 20:38:28 127.0.0.1 - - [14/Nov/2021 20:38:28] "GET /favicon.ico HTTP/1.1" 403 -
```

###ステップ 4: ローカルでLambda 関数を直接呼び出す
* ローカルでLambda 関数を直接呼び出す

```
sam local invoke "HelloWorldFunction"
```

```:出力
Invoking hello-world (go1.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-go1.x:rapid-1.35.0-x86_64.

Mounting ~/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
END RequestId: 71fa4e7b-ecee-41be-b763-604defb5e87c
REPORT RequestId: 71fa4e7b-ecee-41be-b763-604defb5e87c  Init Duration: 0.39 ms  Duration: 2668.64 ms    Billed Duration: 2669 ms        Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"Hello, 193.xxx.xxx.xxx\n"}
```


#DynamoDBとの接続を試してみます

##DynamoDB localの準備
[docker-compose](https://github.com/hiropppp/aws-sam-dynamodb/blob/main/docker-compose.yaml)でdynamodbとGUIで操作でできるツールであるdynamodb-adminを準備します。
詳細は[Github](https://github.com/hiropppp/aws-sam-dynamodb)を参考にしてみてください。

* DynamoDB local起動用

```yaml:docker-compose.yml
services:
  dynamodb-local:
    container_name: test_dynamodb-local
    image: amazon/dynamodb-local:latest
    user: root
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath /data
    volumes:
      - dynamodb-local-data:/data
    ports:
      - 8000:8000
    networks:
      - dynamodb-local-network

  dynamodb-admin:
    container_name: test_dynamodb-admin
    image: aaronshaf/dynamodb-admin:latest
    environment:
      - DYNAMO_ENDPOINT=dynamodb-local:8000
    ports:
      - 8001:8001
    depends_on:
      - dynamodb-local
    networks:
      - dynamodb-local-network

volumes:
  dynamodb-local-data:

networks:
  dynamodb-local-network:
    name: docker.internal
```

* 起動

```
docker-compose up -d
```

* テーブル作成

```
aws dynamodb --profile local --endpoint-url http://localhost:8000 create-table --cli-input-json file://./testdata/db_local.json
```

##アプリの準備

* ビルド

```
sam build
OR
sam build --use-container
```

* アプリ実行

```
sam local start-api --docker-network docker.internal
```

```:出力
Mounting ApigatewayFunction at http://127.0.0.1:3000/apigw [GET]
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2021-11-22 00:21:56  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
```

* テーブルに値が入っていないことの確認

```
aws dynamodb scan --table-name local_company_table --profile local --endpoint-url http://localhost:8000
```

```:出力
{
    "Items": [],
    "Count": 0,
    "ScannedCount": 0,
    "ConsumedCapacity": null
}
```

* APIを実行してデータ投入

```
curl localhost:3000/apigw
```

```:出力
{"company":"Future","year":"1989"}
```

* テーブルに値が入っていることの確認

```
aws dynamodb scan --table-name local_company_table --profile local --endpoint-url http://localhost:8000
```

```:出力
{
    "Items": [
        {
            "year": {
                "S": "1989"
            },
            "company": {
                "S": "Future"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}
```

#一旦接続まで実施してみました
DynamoへCRUDするソースを作成して、記事を綺麗に修正してみたいと思います。
[Github](https://github.com/hiropppp/aws-sam-dynamodb)にソースはあるので試してみたいと思う人は触ってみてください。下記コマンドだけでいけるはず？

```
git clone https://github.com/hiropppp/aws-sam-dynamodb
docker-compose up -d
aws dynamodb --profile local --endpoint-url http://localhost:8000 create-table --cli-input-json file://./testdata/db_local.json
sam build
OR
sam build --use-container
sam local start-api --docker-network docker.internal
aws dynamodb scan --table-name local_company_table --profile local --endpoint-url http://localhost:8000
curl localhost:3000/apigw
aws dynamodb scan --table-name local_company_table --profile local --endpoint-url http://localhost:8000
```

#実行後の環境削除
dockerの環境を一括削除


```
docker-compose down --rmi all --volumes --remove-orphans
```

#参考資料
参考とさせていただきました。ありがとうございます。
[https://future-architect.github.io/articles/20200323/](Serverless連載1: SAMを使ったローカルテスト（Go編）)
[https://xp-cloud.jp/blog/2020/10/15/8936/](AWS SAMでLambda + API Gateway環境のローカルテスト)
[https://qiita.com/KWS_0901/items/02d7e1d1a2240abadfd8](Lambda (SAM CLI) + MySQL(docker-compose)を利用した REST API作成方法メモ)