ローカル環境でgoで書いた aws lambda用のコードを実行しちゃう
この馬の写真は、宮崎県の都井岬にいる野生の馬です。車で真横を通らせていただきました。
サーバーレスアプリケーション
このブログ、kaeruspoonの開発ですが、脱Railsして golang で再実装しようと思います。
EC2上でgoを動かしてもいいのですが、せっかくなのでサーバーレスアプリケーションとして構築しようかと考えました。
とりあえず今回は、ローカル環境でaws lambda用のgoコードが動かせるようにするところまでやってみます。
AWS SAM(Serverless Application Model)
ローカルマシン(Mac)でサーバーレスアプリケーションを開発するために AWS SAMを利用します。SAMには aws-sam-cli というツールがあって、ローカルでの開発には便利そうなのでした。
golangを使うのでgoenvで環境を整え、aws-sam-cliを使うためにpyenvでpython環境を整えました。
ひな形作成
aws-sam-cliを使ってアプリケーションのひな形を用意します。
sam init --runtime go1.x --name kaeru_go
golangを使うのでgoを指定しています。--name
で指定したプロジェクト名がそのままディレクトリ名になります。
最初に作られるファイル構成は以下のような感じ。
├── Makefile <-- 自動ビルド用Makefile
├── README.md
├── hello-world <-- サンプル
│ ├── main.go <-- goで書かれたlambdaのコード
│ └── main_test.go <-- テストコード
└── template.yaml
Makefile
goをコンパイルするためにMakefileを以下のように修正しました。適宜、使いやすいように、また必要なものを追加してください。
.PHONY: deps clean build
all: deps clean build
deps:
go get -u github.com/aws/aws-lambda-go/events
go get -u github.com/aws/aws-lambda-go/lambda
clean:
rm -rf ./hello-world/hello-world
build:
GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world
サンプルを動かしてみる
現時点ですでに動かすことができます。
コンパイル
まず、make
を実行してサンプルコードhello-world
をコンパイルします。
$ make
go get -u github.com/aws/aws-lambda-go/events
go get -u github.com/aws/aws-lambda-go/lambda
rm -rf ./hello-world/hello-world
GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world
hello-world
ディレクトリの下に実行ファイルが生成されます。
$ ls -l hello-world
total 16976
-rwxr-xr-x 1 tsukasa staff 8683300 9 16 05:23 hello-world
-rw-r--r-- 1 tsukasa staff 1096 9 16 05:15 main.go
-rw-r--r-- 1 tsukasa staff 1557 9 16 05:15 main_test.go
Api Gatewayのイベント作成
Api Gatewayがリクエストを受けると、イベントを発行してaws lambdaに渡されます。
このイベントを表しているjsonファイルを作成します。
sam local generate-event api --method GET > get_event.json
lambdaをローカルで動かす
sam local invoke
を実行すると、lambdaが動きます。
$ sam local invoke HelloWorldFunction --event get_event.json
2018-09-16 05:28:51 Invoking hello-world (go1.x)
2018-09-16 05:28:51 Starting new HTTP connection (1): 169.254.169.254
Fetching lambci/lambda:go1.x Docker container image......
2018-09-16 05:28:55 Mounting /Users/tsukasa/dev/test/kaeru_go/hello-world as /var/task:ro inside runtime container
START RequestId: 578e697c-138f-1161-d9f7-ac84372c2b60 Version: $LATEST
END RequestId: 578e697c-138f-1161-d9f7-ac84372c2b60
REPORT RequestId: 578e697c-138f-1161-d9f7-ac84372c2b60 Duration: 893.11 ms Billed Duration: 900 ms Memory Size: 128 MB Max Memory Used: 10 MB
{"statusCode":200,"headers":null,"body":"Hello, 10.0.0.1\n"}
lambda HelloWorldFunction
に対して get_event.json
のイベントを渡してあげた結果、{"statusCode":200,"headers":null,"body":"Hello, 10.0.0.1\n"}
という結果が返ってきました。
サンプルコードは https://checkip.amazonaws.com
にアクセスした結果、得られたIPアドレスを返すプログラムです。
Api Gatewayをローカルで動かす
以下を実行すると、ローカルでApi Gatewayが起動します。
$ sam local start-api
2018-09-16 05:33:41 Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
2018-09-16 05:33:41 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
2018-09-16 05:33:41 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
ブラウザで http://127.0.0.1:3000/hello
にアクセスしてみましょう。lambdaの結果が表示されるはずです。
記事詳細API の作成
kaeruspoonの 記事詳細ページ(/articles/:id
) を表示するためのAPIを作ってみます。
まず一番大切な template.yml
を修正します。Resources
の下に、aws lambdaの定義をします。
HelloWorldFunction
となっているので、Resouces
の下を以下のように書き換えます。
Resources:
ArticleShowFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: article-show/
Handler: article-show
Runtime: go1.x
Tracing: Active
Events:
CatchAll:
Type: Api
Properties:
Path: /articles/{Id}
Method: GET
Outputs:
ArticleShowAPI:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/articles/id"
ArticleShowFunction:
Description: "article show function"
Value: !GetAtt ArticleShowFunction.Arn
ArticleShowFunctionIamRole:
Description: "Implicit IAM Role created for Article Show function"
Value: !GetAtt ArticleShowFunctionRole.Arn
HelloWorldxxx
となっていた箇所をArticleShowxxx
に変更しています。
Resouces
-> ArticleShowFunction
-> Properties
の下に lambdaで動かすものの情報を記しています。
ここでは、article-show
ディレクトリの下にある article-show
という実行ファイルを使用すると定義しています。
また、Events
-> Properties
の下で対応するApi GatewayのURI を定義しています。
イベントの修正
/articles/:id
というように、URIのパスの中にパラメータid
が含まれているので、これをイベントでも表現します。
パスの中のパラメータは pathParameters
で表現されます。get_event.json
の以下の部分に Id
を追加します。
"pathParameters": {
"proxy": "/examplepath",
"Id": "3456"
},
goコードの修正
id
を受け取ったことがわかるように、レスポンスに含めてみましょう。
hello-world
ディレクトリをarticle-show
にリネームした上で、main.go
の中の最後のレスポンス生成の部分を以下のように書き換えます。
return events.APIGatewayProxyResponse{
Body: fmt.Sprintf("Article Hello, ID:%s, IP:%s", string(request.PathParameters["Id"]), string(ip)),
StatusCode: 200,
}, nil
URIのパスのパラメータは request.PathParameters
で取得できます。
Makefileの修正とコンパイル
hello-world
からarticle-show
に変更したので、Makefileも以下の部分を書き換えておきます。
clean:
rm -rf ./article-show/article-show
build:
GOOS=linux GOARCH=amd64 go build -o article-show/article-show ./article-show
できたらmake
を実行して article-show/main.go
をコンパイルします。
lambdaを動かす
以下を実行してみてください。
$ sam local invoke ArticleShowFunction --event get_event.json
2018-09-16 05:55:00 Invoking article-show (go1.x)
2018-09-16 05:55:00 Starting new HTTP connection (1): 169.254.169.254
Fetching lambci/lambda:go1.x Docker container image......
2018-09-16 05:55:04 Mounting /Users/tsukasa/dev/kaeru_go/article-show as /var/task:ro inside runtime container
START RequestId: d59e6ed3-e729-126e-1139-5feb553f7f1d Version: $LATEST
END RequestId: d59e6ed3-e729-126e-1139-5feb553f7f1d
REPORT RequestId: d59e6ed3-e729-126e-1139-5feb553f7f1d Duration: 918.57 ms Billed Duration: 1000 ms Memory Size: 128 MB Max Memory Used: 10 MB
{"statusCode":200,"headers":null,"body":"Article Hello, ID:3456, IP:10.0.0.1\n"}
最後のレスポンスのところで ID: 3456
と出力されているのがわかります。get_event.json
で定義した id
が渡ってきていますね。
ローカルでApi Gatewayを起動すれば、id
を自由に変更して確かめることができます。
sam local start-api
http://127.0.0.1:3000/articles/1234
にブラウザでアクセスすると、ID:1234
と表示されるはずです。URLの1234
の部分を自由に変更してみてください。
まとめ
とりあえずローカルでaws lambda を(Api Gatewayも)動かすことができました。sam
を使って、完成したコードをaws lambdaへデプロイすることもできるようです。
ローカルでDynamoDBを使ったり、このブログを開発していく上で気づいたことがあったらまた記事にしようと思います。