lambda を go で動かし、X-Rayでtraceするまで

## このページについて

lambda を go で書き、実行途中のtraceをX-Rayに預けるまでの作業が書いてあります(2019/05時点)

### 開発環境

```
$ go version
go version go1.12.5 darwin/amd64
```

## 手順

### 関数作成

コンソールからlambdaを作成しましょう

後にX-Rayでlambdaのtrace結果を見たいので下記を有効にしておきます
lambda関数作成後の設定画面にあります
自動的にX-Rayと連携するための権限作成してくれるなんて親切ですね
**(必ず画面上部に出る保存ボタン押しとくこと)**
<img width="645" alt="スクリーンショット 2019-05-15 13.32.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/1cd6fe75-e84e-f77d-fb61-beaf750a1196.png">


### lambda を書く


```go:hello.go
package main

import (
	"context"
	"fmt"
	"io/ioutil"

	"github.com/aws/aws-lambda-go/lambda"
)

var (
	appName  = ""
	version  = ""
	revision = ""
)

type MyEvent struct {
	Name string `json:"name"`
}

func HandleRequest(ctx context.Context, evt MyEvent) (string, error) {
	return fmt.Sprintf("%s-%s-%s, Hello %s!", appName, version, revision, evt.Name), nil
}

func main() {
	lambda.Start(HandleRequest)
}
```

一応プロダクションを意識しておきたいので、gitのrevisionとか入れてます(loggingは後々ちゃんとやる想定)

### build -> deploy -> 実行

Makefileからbuild行います

```makefile:Makefile
# if tag doesnt exists, show revision
VERSION=$(shell git describe --tags --abbrev=7 --always)
REVISION=$(shell git rev-parse --short HEAD)

ifeq ($(VERSION),$(REVISION))
VERSION=v0.0.0
endif

NAME=hello

.PHONY: \
	init \
	build 

init:
	tfenv install

build:
	GO111MODULE=on GOOS=linux go build -v -ldflags "-X main.version=$(VERSION) -X main.revision=$(REVISION) -X main.appName=$(NAME)" -o build/${NAME} *.go
	cd build && zip $(NAME).zip $(NAME)
```

```
$ make build
```

できあがった、hello.zipをコンソールからアップロードします
受け取るjsonを修正しておきましょう
下記nameEventのとこから編集できます
![スクリーンショット 2019-05-15 19.35.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/a6e59ad8-dbe3-1789-436f-4827fce91c0c.png)
↓
![スクリーンショット 2019-05-15 17.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/9b9e857f-bcd7-9af1-6053-86809187cc09.png)


そして、実行。。

![スクリーンショット 2019-05-15 17.42.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/27c94e63-e078-3a2c-f62f-c6d07cf33090.png)

うん、動いてますねよかった。



### X-Ray, CloudWatchで様子を見る

lambda管理画面のモニタリングタブでCloudWatchログ見てみましょう

![スクリーンショット 2019-05-15 19.52.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/f19b751a-67e3-f434-0b61-2dd4d2d6f189.png)

ざっくりパフォーマンスはわかりますが、lambdaの中で他のサービスにアクセスするとなると
各サービスアクセスの時間が知りたくなったりしますよね？
例えば、s3にアクセスしたあと、あるapiにpost行い、また別のapiにpostするとします
このとき、全体の実行時間はCloudWatchみればわかりますが、どこがボトルネックかはapi側のログと照らし合わせたり
lambdaの中で計測ログを落とす必要があると思います

しかし、そんな面倒なことをせずとも、先ほど設定したX-Rayと連携するようにしてあげれば前述の問題は解決されます

X-Rayのサンプルにあったようにlambdaの中でawsにgetリクエストを送り、その内容をトレースしてみたいと思います。
これを行うとこのようにtrace結果が見れます

![スクリーンショット 2019-05-15 20.00.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/e76c9020-3862-81da-b93a-4bf943ee601a.png)

![スクリーンショット 2019-05-15 20.01.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/e91d9586-ed85-940c-356b-ea900c5fbfb5.png)

さっそくlambdaを書き換えてみます

```go:hello.go
package main

import (
	"context"
	"fmt"
	"io/ioutil"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-xray-sdk-go/xray"
	"golang.org/x/net/context/ctxhttp"
)

var (
	appName  = ""
	version  = ""
	revision = ""
)

type MyEvent struct {
	Name string `json:"name"`
}

func init() {
	xray.Configure(xray.Config{
		LogLevel:       "info",
		ServiceVersion: "1.2.3",
	})
}

func HandleRequest(ctx context.Context, evt MyEvent) (string, error) {
	// Start a segment
	ctx, seg := xray.BeginSegment(context.Background(), "api-request")
	_, err := getExample(ctx)
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	// Close the segment
	seg.Close(nil)
	return fmt.Sprintf("%s-%s-%s, Hello %s!", appName, version, revision, evt.Name), nil
}

func getExample(ctx context.Context) ([]byte, error) {
	resp, err := ctxhttp.Get(ctx, xray.Client(nil), "https://aws.amazon.com/")
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(resp.Body)
}

func main() {
	lambda.Start(HandleRequest)
}
```

```
$ make build
```

zipをアップロードして、実行し、成功するはずなのでX-Rayのコンソールで確認してみてください

<img width="1019" alt="スクリーンショット 2019-05-15 20.06.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47896/905e1c56-8c4a-ba5f-f84a-aba5a5014d46.png">

### この次

毎回手動アップロードはしんどいので、terraformなりで構成管理してみようと思います


## 参考

- [Go の AWS Lambda 関数ハンドラ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html)
- [aws-xray-sdk-go](https://github.com/aws/aws-xray-sdk-go)