Apexを使ってgoでAWS Lambda functionを作ってみる

## はじめに

AWS Lambdaの管理ツールであるApexを使ってgoを実行する
LambdaFunctionの作り方と使い方のメモ。
割と[apex.run](http://apex.run/#) に載ってる内容ではありますがが。

## AWS Lambdaとは

いわゆるサーバーレスアーキテクチャの一種でFaas(Function as a Service)
[公式](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html)

- コードをアップするとAWSInfra上でサーバー用意したり、プロビジョニングしなくても実行する事ができるコンピューティングサービス。
  - 現状はjs,java,c#,pythonなどをサポートしてる（近々goもやるらしい)
  - apexだとNodeでgoを実行してくれるのでgoもいける

-  AWS内の他サービスで定義されたトリガーに紐付けてpubsubみたいな事もできる 
  - s3,dynamoの変更通知を受け取ってゴニョゴニョしてあげたり、code deployのデプロイ完了のトリガーを使ってなにがし、みたいに。

簡単に言うとLambdaでサポートしてる言語なら、デプロイとか実行環境整備したりとか不要でEC2インスタンスわざわざ立てなくても
実行できるし、他のAWSサービスと連携しやすいよって感じ。

ウェブサーバーやバッチサーバーみたいなIaasを必要とするようなものではなく、イベントに引っ掛けて
ちょっとした処理をするくらいのものに使うのがいい感じ

## インストール

### apexのインストール

```
curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh
```

### awscliのインストール

aws使うなら入れてる人が大半だと思いますが
apexの動作にaws credentialが必要なので...

[公式](http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html)の引用

```
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python get-pip.py
sudo pip install awscli
```

sixの依存関係errorが出る場合は

```
 sudo pip install --ignore-installed awscli
```

あたりで無視しちゃうと早いです。

### credentialの設定

[公式](http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html)の引用。
regionは自分の使ってるもので。

```
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE #
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
```



## プロジェクト作成

apex cliで雛形作成ができるのでサクッと作ってしまいましょう。

```
$ mkdir your_lambda_function_dir
$ cd your_lambda_function_dir
$ apex init

             _    ____  _______  __
            / \  |  _ \| ____\ \/ /
           / _ \ | |_) |  _|  \  /
          / ___ \|  __/| |___ /  \
         /_/   \_\_|   |_____/_/\_\

  Enter the name of your project. It should be machine-friendly, as this
  is used to prefix your functions in Lambda.

    Project name: xxx_notify

  Enter an optional description of your project.

    Project description: notification for xxx

  [+] creating IAM xxxx_function role
  [+] creating IAM xxxxx policy
  [+] attaching policy to lambda_function role.
  [+] creating ./project.json
  [+] creating ./functions

  Setup complete, deploy those functions!

    $ apex deploy
```

テンプレートな構成を作ってくれます。
terraform使ってない場合はIAMを求められるので、作るor指定が必要。

```
 tree
.
├── functions
│   └── hello
│       └── index.js
└── project.json
```

## goでコードを書く

特にgoを使うための細かな設定などは不要です。apexできる子。
今回は受け取ったsnsのbodyをsqsに投稿するサンプルを。

* .apexignoreでdeployしないファイルを指定する(バイナリをあげれば良いので*.goは無視)

```
cat .apexignore
*.go
```

* snsで受け取ったbodyをsqsに送るコードを書く

```
import (
	"bytes"
	"encoding/json"
	"strings"

	apex "github.com/apex/go-apex"
	apexSns "github.com/apex/go-apex/sns"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/sqs"
)

func main() {
	apexSns.HandleFunc(func(event *apexSns.Event, ctx *apex.Context) error {
		body := event.Records[0].SNS.Message
		//bodyをsqsに送るよ
		awscli := sqs.New(sess, &aws.Config{Region: aws.String("ap-northeast-1")})
		queueUrl := "your_sqs_queue_url"
		params := &sqs.SendMessageInput{
			QueueUrl:    &queueUrl,
			MessageBody: &body,
		}
		_, err = awscli.SendMessage(params)
		return err
	})
}
```

こんな感じ。
apexでのgo実行は、プロセス立ち上げてnodeでgoのバイナリを実行してるだけなので
importとかはローカルで解決してれば大丈夫です。

aws-sdk-goがいい感じにIAMの権限を使ってくれるので
403とか出る場合はlambdaを実行するIAMのロールにsqsのアクセス権を与えてあげてください。

イメージ湧きづらい場合は
[公式](https://github.com/apex/apex/tree/master/_examples/go)をみると良いです。


## deployと実行

* deploy

```
apex deploy function_name
```

* 実行

```
apex invoke function_name
```

* logを見る

```
apex logs function_name -f
/aws/lambda/*** 2017-11-15T06:05:30.238Z	fb0b2dbb-c9ca-11e7-afc4-6762e3c5815e	{"errorMessage":"hoge"} // handlerFuncで返したerrがprintされる
/aws/lambda/*** END RequestId: fb0b2dbb-c9ca-11e7-afc4-6762e3c5815e
/aws/lambda/*** REPORT RequestId: fb0b2dbb-c9ca-11e7-afc4-6762e3c5815e	Duration: 936.31 ms	Billed Duration: 1000 ms 	Memory Size: 128 MB	M
```

## [応用] vpc内のリソースにアクセスする

[公式](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.html)のやり方でもいいし、
`package json` に以下を追加する感じでもおkです

```
{
  "vpc": {
    "securityGroups": ["xxx"],
    "subnets": ["aaaa", "bbbb"]
  }
}
```