@k_nishijima
AWS Lambda in Golang
JAWS-UG沖縄 真夏の熱すぎるサーバレス祭り! 2016年08月
@k_nishijima
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
The design is licensed under the Creative Commons 3.0 Attributions license.
@k_nishijima
皆さんこんにちは!
Go言語でコード書いてますか?
2
@k_nishijima
仕事で書いてる人?
趣味で書いてる人?
書いてない人?
3
@k_nishijima
今日は実践
「Go言語でLambda関数を書く」
をお届けします
4
@k_nishijima
あんた誰?
5
西島 幸一郎 / にしじま こういちろう

アールスリーインスティテュート ソリューションアーキテクト
https://www.r3it.com

大阪の会社に所属、宜野湾の自宅から100%リモートワーク
okinawa.goのコアメンバー
JAWS−UG沖縄のコアメンバー
ハッカーズチャンプルー実行委員長
ご質問などあればFacebook/Twitterなどでお気軽に〜♪
@k_nishijima
nishijima.koichiro
@k_nishijima
アジェンダ
6
๏ AWSとGolangの関係をおさらい
๏ 利用ツールの紹介
๏ 実際のLambda関数の書き方
๏ 簡単なハンズオン
@k_nishijima
AWSとGo言語
7
@k_nishijima
AWSとGo言語
8
๏ AWS SDK for Go あります

https://aws.amazon.com/jp/sdk-for-go/

๏ 新サービスにもかなりの勢いで追随してます

https://github.com/aws/aws-sdk-go

๏ ただし今のところ公式にはLambdaの対応言語ではありません

【Node.js (JavaScript)、Python、および Java (Java 8 互換)】

https://aws.amazon.com/jp/lambda/faqs/
@k_nishijima
え、じゃあどうやって
LambdaをGoで書くの?
9
@k_nishijima
利用ツールの紹介
10
@k_nishijima
三種の神器:利用ツールの紹介
11
๏ APEX : Lambda関数のデプロイ管理

http://apex.run/

https://github.com/apex/apex
๏ Simple API Gateway : API Gatewayの設定

https://github.com/horike37/simple-api-gateway

๏ Terraform : インフラの管理 https://www.terraform.io/
@k_nishijima 12
๏ Lambda関数の管理に特化したツール

๏ 環境構築はTerraformを組み合わせて使うことを想定
๏ Node.jsのshimの魔法によりGo言語の実行をサポート

→このおかげでGoでLambdaが書ける!
APEX
@k_nishijima 13
๏ 個人的にも支援中
๏ コードで貢献したかったがGoスキルが足りず…

ひとまずお金で支援することにした(^_^;)
APEX
@k_nishijima
Simple API Gateway
14
๏ API Gatewayの設定をCLIで行うツール

๏ マネコンの設定画面は罠が多すぎるので…

API作る際にはこれを利用して作るととても楽です
@k_nishijima
Simple API Gateway
15
๏ こっちはコードで貢献できた!
@k_nishijima 16
๏ みんな大好きHashiCorp社謹製のインフラ管理ツール

๏ JSONライクな設定ファイルからインフラを自動生成/破棄
๏ 組み合わせとしては、IAMの権限設定やLambda以外のリソース
管理に利用すると吉
Terraform
@k_nishijima
この辺のツールを使って
実際に何かLambdaを使って

Web APIを作ってみましょう
17
@k_nishijima
お題
問い合わせフォームを

DynamoDBに保存する
API
18
@k_nishijima
実際には
管理者にメールしたり
送信元にメールしたり
Slackに通知したり
色々やりたいけど

その辺は簡単に実装できるので各自!
19
@k_nishijima 20
๏ https://github.com/k-nishijima/lambda-handson-
jawsug-okinawa-201608

๏ infra以下にTerraformのtfファイル
๏ lambda以下にLambda関数のファイル
ソースリポジトリ
@k_nishijima 21
๏ Terraformで作られたAWSの環境(IAMロール、DynamoDB)
๏ API GatewayでホストされるWeb API
๏ Goで書かれたLambda関数(APEXでデプロイ)
๏ Goで書かれたコアライブラリ(AWSリソースを利用する実体)
全体構成
Goで書かれたLambdaSimple API Gatewayで設定
HTTPリクエスト Lambdaで動いてるところに
API GatewayでHTTP経由で
呼び出すインターフェイス
をつけるイメージ
@k_nishijima
実際の開発の流れ
22
1. Terraform でDB環境・IAM実行権限などを設定
2. Goでコーディング、テスト
3. APEXでLambda関数としてデプロイ
4. Simple API GatewayでAPI作成、デプロイ(はじめの1回だけ)
5. APIを叩いてテスト。OK?



問題があれば、2と3と5をぐるぐる繰り返す…
@k_nishijima 23
๏ インストールは

https://www.terraform.io/intro/getting-started/install.html
๏ リポジトリのinfraディレクトリのREADME.mdを確認の上、

“terraform apply"

๏ DynamoDBのテーブルとIAM Roleが作られる
Terraformで環境構築
@k_nishijima
さあGoでコーディング
24
๏ その前に!

プロジェクト構成をどうすべきか考える必要がある。

๏ 自分の場合は、GOPATH配下にコアライブラリを置き、

Lambda関数側からはそれを呼ぶ、という形にした。

ベストかどうかは分からんです・・・教えて偉い人!
@k_nishijima
プロジェクト構成(例)
25
コアライブラリ:
~/gopath/src/github.com/k-
nishijima/lambda-handson
APEX管理配下の

Lambda関数:
~/lambda-handson/
functions/funcname/


importして

使う
๏ コアライブラリはそれだけで単体テスト
๏ APEX管理配下の関数からimportして利用
@k_nishijima
この構成のメリット
26
๏ コアライブラリ側の、通常のGo言語のコーディング・デバッグ
でほとんど作業は完結する

(Lambda関数としてのデバッグはほぼ不要)
๏ AWSのサービスを呼び出すものもローカルで完結
๏ DynamoDB local、GoAws(未使用だけどSNS/SQSエミュレータ)
@k_nishijima
コアライブラリのコーディング、テスト
27
๏ https://github.com/k-nishijima/lambda-handson-golang-201608/
blob/master/dao.go

๏ 普通にGoでAWSを利用するコーディング

(制約はさておき、Lambdaから呼ばれるからといって特殊なコーディ
ングはない)
๏ HTTPリクエストを構造体経由で受け取る想定で書くと良い
@k_nishijima
コアライブラリのコーディング、テスト
28
๏ 構造体はJSONとvalidateのアノテーションに注目

๏ AWSの権限を取る部分をProfile指定とロール指定
の両対応にしておくと後で便利

(svcメソッドの部分)
@k_nishijima
コアライブラリのコーディング、テスト
29
๏ テストが終わったら、”go install” しておく

※これでAPEX管理下のLambdaから呼び出す準備が整う

๏ コアライブラリを改修したら

再度go installをお忘れなく
@k_nishijima 30
やっと本題(?)
GoでLambda関数を書く
@k_nishijima
APEXでプロジェクトひな形作成
31
๏ $ apex -p lambda-handson-201608 init

でプロジェクト雛形作成

(-pはプロジェクトを作るときに使うAWS profile名)。

๏ Lambda関数の実行用ロールなんかも作ってくれる

(が、今回はTerraformで作ったロールを使う)
@k_nishijima
APEXでプロジェクトひな形作成
32
├── functions
│   └── hello
│   └── index.js
└── project.json
── 関数ディレクトリ
── ディレクトリ名が関数名suffix
── 実装
── プロジェクト全体の設定ファイル
๏ Node.jsならそのままindex.jsをいじっていけば
hello関数が出来上がる(が、今回は削除する)
@k_nishijima
project.json重要
33
๏ project.jsonはプロジェクト全体の設定ファイル

๏ name: 各関数のprefixになる
๏ メモリ量とかタイムアウトとかここで書ける
๏ Lambda関数実行時のroleもここで指定
๏ 同じような書式で関数ごとの設定もfunction.jsonで可
@k_nishijima
GoでLambda関数作成時の注意点
34
๏ APEXではSTDINとSTDOUTをNodeとGo言語のやり取
りに使うので、例えばロギングなどは必ずSTDERR
に出力しないといけない。

os.Stderr.WriteString(“hoge”) みたいな
@k_nishijima
実際のソース:
イベントから構造体への変換
35
func main() {
apex.HandleFunc(func(event json.RawMessage, ctx
*apex.Context) (interface{}, error) {
// リクエストを格納する構造体
var request lambdaHandson.AddValueRequest
if err := json.Unmarshal(event, &request); err != nil {
return nil, err
}
@k_nishijima
実際のソース:
json.Unmarshalするときに、JSONアノテーションが効く
36
type AddValueRequest struct {
Stage string `json:"stage" valid:"required"`
Email string `json:"email" valid:"email,length(1|512),required"`
Message string `json:"message" valid:"length(1|1024),required"`
}
{ "stage": "dev",
"email": "foo@bar.com",
"message": "hello golang"}
まあunmarshalだけなら
アノテーション書かなくても

大丈夫だけど・・・



JSONをレスポンスするとき

アノテーション重要
リクエスト

のJSON
@k_nishijima
実際のソース:
validateはgovalidatorを使ってみました
37
type AddValueRequest struct {
Stage string `json:"stage" valid:"required"`
Email string `json:"email" valid:"email,length(1|512),required"`
Message string `json:"message" valid:"length(1|1024),required"`
}
๏ https://github.com/asaskevich/govalidator

๏ 他にも同種の便利なパッケージはあると思うので探してみて!
@k_nishijima
実際のソース:
実行したいメソッドを呼ぶ
38
err = dao.Put(request)
if err != nil {
return nil, err
}
๏ Lambda関数側はほぼ定形的なコーディングとするように仕向け、

可能な限りシンプルにしておく
๏ Lambda上にロジックやら条件分岐やらを書き出すと・・・(^_^;)
@k_nishijima
Lambda環境で動かす時のTIPS:

必要な権限の取り方
39
๏ LambdaはIAM Roleを使って動くので、権限はAccessKeyなどを

指定などする必要はない

= 「Profile指定とロール指定の両対応にしておくと便利」の件

๏ このコードの実装では、Lambda関数が読む設定ファイルから、

Profileの指定を削除しておけばOK
@k_nishijima
ダメ・ゼッタイ(^^;
40
@k_nishijima 41
$ apex -p lambda-handson-201608 deploy contact
๏ これで contact関数をデプロイ。

コードを修正してテストを通したらデプロイ、

そして確認。これを何度も繰り返す。
๏ これをマネコンのGUIでやってたら死んじゃいます(^_^;)
APEXでデプロイ
@k_nishijima
APEXでデプロイ
42
関数名がproject.jsonのname + functionディレクトリ
名になっているのが分かる
@k_nishijima
APEXでLambda関数実行
43
$ apex -p lambda-handson-201608 invoke contact < request_contact.json
๏ JSONファイルを引数としてcontact関数をinvoke。

上手く動けばレスポンスが返ってくる。
@k_nishijima
今回はDynamoDBの中身を見る

関数を作ってないので
44
๏ 手動でマネージメントコンソールからDynamoDBの中身を
見てください(^_^;)

๏ コアライブラリにGetItemsというメソッドを付けておき
ましたので、データ取得APIもすぐ出来ると思います!
@k_nishijima
APEXで実行時ログを見る
45
$ apex -p lambda-handson-201608 logs contact
๏ 動かなかったりした時はログをチェック
@k_nishijima 46
以上です!
@k_nishijima
ここまでで
47
๏ Goで書いた自前のライブラリを利用する、

Goで書かれたLambda関数が動くようになりました。
๏ このような関数を例えばスケジューリング実行やS3のイベント
に反応して実行するようにすれば、実務にもそのまま使えます。
@k_nishijima
だがしかし
まだWeb APIになってない><
Lambda関数はそのままでは

HTTPS経由で呼べない…
48
@k_nishijima
ここで
API Gateway
の登場です
49
@k_nishijima
Web APIになれば
ブラウザから普通に叩ける。

HTMLだけホストしておけば

大丈夫になる!
50
@k_nishijima
Simple API GatewayでAPI作成
51
๏ インストールは

“npm install -g simple-api-gateway”

๏ ES6のコードなのでNodeは4以上、

勿論AWS CLIも必要です。詳しくは

https://github.com/horike37/simple-api-gateway
@k_nishijima
Simple API GatewayでAPI作成
52
@k_nishijima
実際作ってみる
53
๏ POST /contactにリクエスト投げると
๏ Lambdaが呼ばれてよろしく処理される
๏ そんなAPIを作りましょう
@k_nishijima
利用するAWSの権限の設定は?
54
๏ https://github.com/horike37/simple-api-gateway/
issues/5
๏ コントリビュートチャンス!!

お待ち申し上げております(^_^;)
@k_nishijima
実際作ってみる
55
west-mbp:lambda-handson-jawsug-okinawa-201608 nishijima$ apigw create
? Please select Region for API Gatway ap-northeast-1
? Please input API Name Handson API
API Create Success!! Please action `apigw edit` and set up API
@k_nishijima
実際作ってみる
56
west-mbp:lambda-handson-jawsug-okinawa-201608 nishijima$ apigw edit
? Please select Region for API Gatway ap-northeast-1
? Please select API Handson API
? Do you want to use an existing Resources on Handson APIor create a new one? Create A
New Resouce
? Please select parent Resource /
? Please input Resouce Path contact
? Please select method POST
? Please select backend lambda function lambda-handson-jawsug-okinawa-201608_contact
? May I set enable CORS? Yes
? Set up mapping template? (y/N) y で、エディタが開くのでマッピングテンプレートを入れて

? Please edit mapping template Received
? May I create the API? Yes
Create Success!! Resource and Method on Handson API API.
@k_nishijima
マッピングテンプレートって何?
57
๏ Integration Requestに指定できる、URLパラメータ/パス
パラメータ/HTTPヘッダなどをLambda関数のコードに渡す
ためのテンプレート
๏ 想定するURLパラメータとともに、APIGWのステージやHTTP
ヘッダなどをLambda関数に渡すことが出来る
@k_nishijima
APIを作ったら、ステージ名をつけてデプロイ
58
west-mbp:lambda-handson-jawsug-okinawa-201608 nishijima$ apigw deploy
? Please select Region for API Gatway ap-northeast-1
? Please select API Handson API
? Please input Stage Name dev
Deploy success! Endpoint:https://あなたの.execute-api.ap-northeast-1.amazonaws.com/dev
๏ これでステージ「dev」のAPI完成。ステージ = 環境と言い換えてもOK。
๏ ステージごとURLが発行されるので、まったく別の環境として利用可能。
๏ Lambdaのエイリアスと連動したり、このコードの例のように環境変数と
して扱ってプログラムの動作を切り替えたり、いろいろ利用できます。
@k_nishijima
呼び出してみる
59
$ curl -H "Content-Type: application/json"

-X POST

-d "email=curl@foo.com&message=hello world via curl"

https://あなたのURL.execute-api.ap-northeast-1.amazonaws.com/dev/contact
"ok"
@k_nishijima 60
出来た!?
@k_nishijima
はじめに戻って:

サーバレスアーキテクチャとは?
61
๏ Q: 開発は楽になった?
๏ A: 1関数の責任範囲がとても狭くなるので、相対的に楽にな
る。デバッグも楽。

また、とにかく繰り返しになるデプロイが楽なのがいい。
@k_nishijima
はじめに戻って:

サーバレスアーキテクチャとは?
62
๏ Q: スケールする感じする?
๏ A: 極力ステートレスな実装にして、バックエンドのデータ
ソースも速度で詰まらないDynamoDBなどを活用すれば、ス
ケールしないほうがおかしい
@k_nishijima
セッションとか
クライアント側の状態管理は!?
63
๏ 何を言っとるんですか、時代は21世紀ですよ。

そんなものはありません(建前
๏ JavaScriptでAWS SigV4を実装した話を聞きたいですか、
そうですか(イラネw
@k_nishijima
おや、こんなのも…
64
๏ A Go framework for AWS Lambda microservices

http://gosparta.io/
๏ 誰か試してLTしてくださいな(^^)/
@k_nishijima
まとめ
65
๏ AWS SDK for Go があるので

AWSリソースを使ったLambdaを書くのはとても簡単
@k_nishijima
まとめ
66
๏ Goは非常にパワフルな言語なので、

生産性高くサクサクLambda関数が書ける
@k_nishijima
まとめ
67
๏ きっとAWS CLIがGoでリライトされる未来が来る!(来ないかもw
๏ それはどうでもいいとして(^_^;)

現状でまったく不満はないですが、公式にLambdaでサポートされ
ると更に嬉しいですね!
@k_nishijima 68
Thank you so much!
Any questions?

AWS Lambda in Golang