AWS SAM CLI + Golang + DynamoDB Local + Reactでの環境構築

こんにちわ、株式会社Relicの野口と申します。
表題の通り、DynamoDBを使った開発の環境構築についてまとめていきたいと思います。

ゴール

Reactから、DynamoDB Localから値を取得し、画面に表示するまで

環境

MacBookPro13inch M2 Mac OS Venture 13.2.1
Docker version 20.10.23, build 7155243
Docker Compose version v2.15.1
Homebrew 4.0.23

前提

Mac
Dockerをインストールできている
HomeBrewがインストールできている

フォルダ構成

最終的には、このような構成になります。

|-- .aws-sam
|-- hello-world
|   |-- Dockerfile
|   |-- go.mod
|   |-- go.sum
|   |-- main_test.go
|   |-- main.go
|-- template.yaml
|-- README.md
|-- samconfig.toml
|-- Dockerfile-awscli
|-- docker-compose.yml
|-- bin
|   |-- main.sh
|   |-- create.sh
|   |-- seed.sh
|   |-- user.json

AWS SAM CLIのインストール

brewコマンドを使ってSAM CLIをインストールしましょう
※homebrewがない方は別途インストールしてください。

brew install aws/tap/aws-sam-cli

バージョンが確認出たらインストール完了です。

sam --version
SAM CLI, version 1.86.1

確認できましたら、任意のフォルダに移動してsamを実行します。指定したフォルダの中に作成されますので、今回はmyAppという名前でフォルダを作成し、その中に展開します。

cd myApp
sam init

対話形式で構築内容を聞かれますので選択していきましょう。

Which template source would you like to use?
1 - AWS Quick Start Templatesを選択

Choose an AWS Quick Start application template
1 - Hello World Example14を選択

Use the most popular runtime and package type? (Python and zip)
こちらは「N」で

Which runtime would you like to use?
「3」のgo1.xで

What package type would you like to use?
Dockerを使用するので「2」のImageにしましょう。

Would you like to enable X-Ray tracing on the function(s) in your application?
X-Rayは今回不要ですので、「N」で

Would you like to enable monitoring using CloudWatch Application Insights?
CloudWatchも今回不要なため「N」で

project name [sam-app]:
sam-app
プロジェクトネームはsam-appにしておきます。

これにて指定したフォルダ内にsamの実行フォルダが構築されたかと思います!作成したフォルダに移動してみましょう。

cd sam-app

以下のように展開されているかと思います。

|-- hello-world
|   |-- Dockerfile
|   |-- go.mod
|   |-- go.sum
|   |-- main_test.go
|   |-- main.go
|-- template.yaml
|-- README.md
|-- samconfig.toml

では、一度この状態でビルドし、ブラウザで表示してみましょう。buildする前にDockerを起動させておいてください。

sam build
sam local start-api

これによりDocker Imageが作成され、http://127.0.0.1:3000/hello
がターミナルに表示されるかと思います。
このURLをブラウザで開いてみてください。
Hello, ○○ ←ipアドレスがはいります。
が表示されればOKです!
cmd+cで抜けましょう。

DynamoDB Localの環境構築

それでは、DynamoDB Localの環境を構築していきましょう。sam-app内に以下のファイルを追加していきます。

touch Dockerfile_awscli
touch docker-compose.yml

Dockerfile_awscli

FROM amazon/aws-cli

ENV AWS_ACCESS_KEY_ID=fake_access_key\
    AWS_SECRET_ACCESS_KEY=fake_secret_access_key\
    DYNAMODB_REGION=ap-northeast-1
WORKDIR /usr/app

docker-compose.yml

version: '3'
services:
  dynamodb:
    image: amazon/dynamodb-local
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath . -optimizeDbBeforeStartup
    volumes:
      - dynamodb:/home/dynamodblocal
    ports:
      - 8000:8000
  awscli:
    build:
      context: .
      dockerfile: Dockerfile_awscli
    entrypoint: [""]
    tty: true
    command:
      - /bin/sh
    volumes:
      - ./bin:/usr/app
      
volumes:
  dynamodb:
    driver: local
  bin:
    driver: local

コンテナを起動する

docker-compose up -d // Dockerのコンテナを立ち上げる
docker ps -a // AWS CLI用のコンテナ名を確認する。今回はsam-app-awscli-1

AWS CLIコンテナに入る

// docker exec -it [AWS CLIコンテナ名] /bin/bash
docker exec -it sam-app-awscli-1 /bin/bash ←今回はこのようになります。

bash-4.2#と表示されればOKです。

AWS CLIコマンドを実行し、テーブルの確認

aws dynamodb list-tables --region ap-northeast-1 --endpoint-url http://dynamodb:8000/

--regionと--endpoint-urlは必ず入れておきましょう。

{
  "TableNames": []
}

が返ってきましたね!
無事入れました!。これにてDynamoDBLocal環境がDocker上で作られました。
抜ける際は、exitと入力しましょう。

テーブルの作成

ターミナル上でコマンドを打ってするのもいいんですが面倒です。
なのでbinフォルダの中に、shファイルとjsonファイルを格納して、コマンド一発でDBを構築しましょう。
おそらく、今の状態でsam-app配下にbinフォルダが作成されているかと思います。以下ファイルをbinフォルダ内に作成します。

cd bin
touch main.sh
touch seed.sh
touch create.sh
touch put.sh
touch delete.sh
touch user.json

main.shファイル

#!/usr/bin/env bash

docker-compose exec -T awscli ./seed.sh

seed.shファイル

#!/usr/bin/env bash

sh ./delete.sh
sh ./create.sh
sh ./put.sh

create.shファイル

aws dynamodb create-table \
  --region ap-northeast-1 \
  --endpoint-url http://dynamodb:8000 \
  --table-name user \
  --attribute-definitions \
    AttributeName=email,AttributeType=S \
    AttributeName=password,AttributeType=S \
  --key-schema \
    AttributeName=email,KeyType=HASH \
    AttributeName=password,KeyType=RANGE \
  --provisioned-throughput \
    ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --table-class STANDARD

put.shファイル

aws dynamodb batch-write-item \
  --region ap-northeast-1 \
  --endpoint-url http://dynamodb:8000 \
  --request-items file://user.json \

delete.shファイル

#!/usr/bin/env bash

aws dynamodb delete-table --table-name user --region ap-northeast-1 --endpoint-url http://dynamodb:8000

user.jsonファイル

{
  "user": [
	{
	  "PutRequest": {
		"Item": {
		  "email": {
			"S": "hoge@hoge.com"
		  },
		  "password": {
			"S": "hogehoge"
		  }
		}
	  }
	},
	{
	  "PutRequest": {
		"Item": {
		  "email": {
			"S": "huga@huga.com"
		  },
		  "password": {
			"S": "hugahuga"
		  }
		}
	  }
	},
	{
	  "PutRequest": {
		"Item": {
		  "email": {
			"S": "hoga@hoga.com"
		  },
		  "password": {
			"S": "hogahoga"
		  }
		}
	  }
	}
  ]
}

ルート直下に戻り、下記コマンドをターミナルで入力してください

cd ../
./bin/main.sh

もう一度Docker環境に入って、テーブルがどうなっているかを確認しましょう

docker exec -it sam-app-awscli-1 /bin/bash
aws dynamodb list-tables --region ap-northeast-1 --endpoint-url http://dynamodb:8000/

{
    "TableNames": [
        "user"
    ]
}

ユーザーテーブルが作成されていますね!

ターミナルで確認

ユーザーテーブルの中身をターミナルで確認したい場合は、以下を入力してください。

aws dynamodb scan --table-name user --region ap-northeast-1 --endpoint-url http://dynamodb:8000/

AWS-SDK-GO

今回は、AWS-SDK-GOで実装します。
hellow-worldフォルダ配下の、go.modに読み込みたいものをversion指定して追加してください

require github.com/aws/aws-lambda-go v1.36.1
require github.com/aws/aws-sdk-go v1.44.284 // ここに追加
replace gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.2.8

module hello-world

go 1.16

同じくDockerfileにRUN go mod tidyを追加しましょう

FROM public.ecr.aws/bitnami/golang:1.19 as build-image

WORKDIR /go/src
COPY go.mod go.sum main.go ./

RUN go mod tidy // ここに追加

RUN go build -o ../bin

FROM public.ecr.aws/lambda/go:1

COPY --from=build-image /go/bin/ /var/task/

# Command can be overwritten by providing a different command in the template directly.
CMD ["hello-world"]

main.go

では今度はGoでDynamoDBの値を取得していきましょう
hello-worldフォルダ内のmain.goを開いてください。今回は、テーブルの中身を全て取得するScanItemを使用します。

package main

import (
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/aws/credentials"
  "github.com/aws/aws-sdk-go/service/dynamodb"
  "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
  "fmt"
  "encoding/json"
)

type User struct {
  Email string
  Password string
}

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  sess := session.Must(session.NewSession(&aws.Config{
    Region: aws.String("ap-northeast-1"),
    Endpoint: aws.String("http://host.docker.internal:8000"),
    Credentials: credentials.NewStaticCredentials("dummy_access_key_id", "dummy_secret_access_key_id", ""),
  }))
  
  svc := dynamodb.New(sess)

  // userテーブルを全件取得
  var users []User = []User{}
  params, err := svc.Scan(&dynamodb.ScanInput{
    TableName: aws.String("user"),
  })
  if err != nil {
    fmt.Println(err.Error())
  }
  
  for _, scanedUser := range params.Items {
    var userTmp User
    _ = dynamodbattribute.UnmarshalMap(scanedUser, &userTmp)
    users = append(users, userTmp)
  }
  
  j, _ := json.Marshal(users)
 
  return events.APIGatewayProxyResponse{
    Headers: map[string]string{
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
    },
     
    Body: string(j),
    StatusCode: 200,
  }, nil
}

func main() {
  lambda.Start(handler)
}

書き終えたらビルドし、apiを立ち上げましょう。

sam build
sam local start-api

Reactでの取得

構築は今回は説明を省きます。 src/App.jsをそのまま更新します。

import React, { useEffect, useState } from "react"
import './App.css';

function App() {
  const [users, setUsers] = useState([])

 const fetchUserData = () => {
    fetch("http://127.0.0.1:3000/hello")
      .then(response => {
        return response.json()
      })
      .then(data => {
        setUsers(data)
      })
  }

 useEffect(() => {
    fetchUserData()
  }, [])

 return (
   <div className="App">
       {
           users.map((item, idx) => {
         return <div key={idx}>
           <p>Email: {item.Email}</p>
           <p>Password: {item.Password}</p>
         </div>
       })
     }
   </div>
 );
}

export default App;

PORTが同じく3000で被ってしまってますので、package.jsonに以下を追加してください。今回は、react側のPORTを3001に変更します。

package.json

"scripts": {
  "start": "PORT=3001 react-scripts start", ← ここに追加
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}

npm startでReactアプリを起動してみましょう。トップページに、ユーザーの一覧が表示されていればOKです!

DynamoDB Adminの導入

DynamoDB LocalにはGUIツールがあります。折角なので導入していきましょう。
docker-compose.ymlに以下を追記してください

docker-compose.yml

dynamodb-admin:
  image: aaronshaf/dynamodb-admin:latest
  environment:
    - DYNAMO_ENDPOINT=dynamodb:8000
  ports:
    - 8001:8001
  depends_on:
    - dynamodb

dockerを再起動します。

docker-compose up -d

起動後、http://localhost:8001 にアクセスすると、追加したテーブルが確認できます。お疲れさまでした!

dynamodb-admin

最後に

Relicでは積極的に採用を行なっております!
地方拠点もありますので、U・Iターン大歓迎です!🙌
ご興味のある方は、Relic採用サイトからエントリーをお願いいたします

この記事が気に入ったらサポートをしてみませんか?