Golang + gorilla/mux + LambdaProxyIntegration + ServerlessFrameworkを動かすまで

# 概要
タイトル通り、GolangアプリケーションをLambdaプロキシ統合で動かします

## Lambdaプロキシ統合ってなに
ここに書いてある

https://docs.aws.amazon.com/ja_jp/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html

これを使うとLambda関数を小分けにしなくても、アプリケーション側でルーティングを振り分ける従来の開発方法でサーバーレスなアプリケーションが作れる

## 必要なもの
・AWSアカウント
・ServerlessCLI
インストール、認証情報の設定が必要です

公式ドキュメント

https://www.serverless.com/framework/docs/getting-started

## 実装
`sls create --template aws-go-mod --path go-test`
テンプレートから雛形を作ってもらいます
`go-test`の部分はお好きなプロジェクト名を入れてください

この時点でのファイル構成はこんな感じ
```
├── Makefile
├── gomod.sh
├── hello
│   └── main.go
├── serverless.yml
└── world
    └── main.go
```

ここからプロキシ統合で動かすためにMakefileやらserverless.ymlやら色々手を加えていきます

プロキシ統合では単一のlambda関数にAPIGatewayからリクエストを渡し関数側でルーティング等の処理をします。
現在はhello関数とworld関数が存在するのでこれを削除して一つにしましょう。

```
├── Makefile
├── gomod.sh
├── serverless.yml
└── src
    └── main.go
```

```serverless.yml
~~~
provider:
  name: aws
  runtime: go1.x
  #リージョンの設定をしないとデフォルトのus-east-1に飛ばされます
  region: ap-northeast-1
~~~

functions:
  #lambda関数名
  main:
    #ビルド後のバイナリファイルパス
    handler: bin/main
    #lambdaプロキシ統合の設定
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

~~~
```


次にmain.goにAPIGatewayから送られてきたリクエストをどうにかする処理を書いてくのですが、AWS様がライブラリを用意してくれているので簡単に実装できます。
net/httpは勿論、gorilla/mux, Gin, Echo fiber等がサポートされているみたいですが今回はgorilla/muxを使用します。
Go歴2日なのでクソコードなのは許してください。。。

https://github.com/awslabs/aws-lambda-go-api-proxy

```main.go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/awslabs/aws-lambda-go-api-proxy/gorillamux"
	"github.com/gorilla/mux"
)

var gorillaMuxLambda *gorillamux.GorillaMuxAdapter

func handleHello(w http.ResponseWriter, r *http.Request) {
		type response struct {
		Message string `json:"message"`
	}
		result, err := json.Marshal(response{Message: "hello"})
		if err != nil {
			log.Println(err.Error())
			fmt.Fprint(w, "sorry server error...")
			return
		}
		fmt.Fprintf(w, "%s", result)
}

//コールドスタート時にのみ実行される
func init() {
	log.Printf("cold start")
	router := mux.NewRouter()
	router.HandleFunc("/", handleHello)

	gorillaMuxLambda = gorillamux.New(router)
}

func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	return gorillaMuxLambda.ProxyWithContext(ctx, req)
}

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

```




関数の構成を変更したのでMakefileにも変更を加えます
またデフォルトのbuildコマンドだと何故かファイルのパスが解決できないとかなんとかでlambdaが動いてくれないためこちらにも変更を加えます
何故動かないのかはよくわからないので誰か教えて下さい。。。
とりあえずおまじないとして`-ldflags='-d -s -w' -a -tags netgo -installsuffix netgo`を追加します


```
#Makefile

.PHONY: build clean deploy gomodgen

build: gomodgen
	export GO111MODULE=on
    #ここを変更
	env GOARCH=amd64 GOOS=linux go build -ldflags='-d -s -w' -a -tags netgo -installsuffix netgo -o bin/main src/main.go

clean:
	rm -rf ./bin ./vendor go.sum

deploy: clean build
	sls deploy --verbose

gomodgen:
	chmod u+x gomod.sh
	./gomod.sh

```

これで設定が終わったのでファイルをビルドしてデプロイしていきます

go.modの作成
`make gomodgen`

モジュールのダウンロード
`go mod tidy`

ビルド
`make build`

デプロイ
`sls deploy`

この時点でのファイル構成
```
├── Makefile
├── bin
│   └── main
├── go.mod
├── go.sum
├── gomod.sh
├── serverless.yml
└── src
    └── main.go
```

無事にデプロイ出来たら`sls invoke -f main`でAPIを叩いてみましょう

```
{
    "statusCode": 200,
    "headers": null,
    "multiValueHeaders": {
        "Content-Type": [
            "text/plain; charset=utf-8"
        ]
    },
    "body": "{\"message\":\"hello\"}"
}
```
ここまで出来たらあとは普通のGolangアプリケーションと同じ感覚で開発していけます

# 最後に
Lambdaは安いし楽だし個人開発において最強