Nature Bath vol.6【勉強会】みんなのGo言語 執筆陣による「Go開発・運用の現場」の発表資料です https://nature.connpass.com/event/194611/
シングルバイナリにこだわる - AWS Lambda 編2020.11.18 Nature Bath vol.6⾯⽩法⼈カヤック 藤原俊⼀郎 @fujiwara
View Slide
@fujiwaragithub.com/kayac/ecspressoAmazon ECS デプロイツールgithub.com/fujiwara/lambrollAWS Lambda デプロイツール
AWS Lambda を何の⾔語で書いていますか?Node.jsPythonRubyJavaGo.NETCustom Runtime Bash Perl COBOL...
Go で Lambda を書くと嬉しいランタイムの寿命に影響されにくいデプロイするものはビルド済のx86_64バイナリ = ⾔語のEoLに影響されない起動も実⾏も速いGoが好き
Go で Lambda を書くhttps://github.com/aws/aws-lambda-go#getting-started// main.gopackage mainimport ("github.com/aws/aws-lambda-go/lambda")func hello() (string, error) {return "Hello ƛ!", nil}func main() {// Make the handler available for Remote Procedure Call by AWS Lambdalambda.Start(hello)}この main.go をビルドして Zip に含めて Handler として呼ぶ
kayac/s3-object-routerhttps://github.com/kayac/s3-object-routerS3 に置かれたObjectを指定したルールで切り分ける君{"tag": "app.info", "source": "stdout", "message": "xxx"}{"tag": "app.warn", "source": "stderr", "message": "yyy"}{"tag": "app.info", "source": "stdout", "message": "zzz"}ルール path/to/{{ .tag }}/{{ .source }}で処理するとpath/to/app.info/stdout/...に 13⾏⽬がpath/to/app.warn/stderr/...に 2⾏⽬が保存されるS3 Event Notification でこの Lambda を呼んで実⾏する
Lambda の典型的なユースケース = S3 イベントトリガCLI から S3 URL s3://bucket/objectを指定して実⾏できたら便利ですよね!開発中とかリカバリとか世の中にはいろいろあるいちいち Lambda にデプロイして aws lambda invoke-function ...より CLI で叩きたい$ aws lambda invoke-function \--function-name s3-object-router \--payload '{"Records":[{"s3":{"bucket":{"name":"bucket-name"},"object":{"key":"object-key"}}}]}'ではなく$ s3-object-router s3://bucket-name/object-keyってしたい
「シングルバイナリにこだわる」とはLambda⽤とCLI⽤のバイナリを個別にビルドするのは無駄では???main.go がちょっと違うだけで本体はほぼ同じビルド時間2倍、サイズも2倍ファイルの管理も⾯倒Lambda でも CLI でも動く1個のバイナリを GitHub releases においておきたい
Lambda でも CLI でも⾃動判別してよしなに動くコード環境変数 AWS_EXECUTION_ENVを⾒て判別!if strings.HasPrefix(os.Getenv("AWS_EXECUTION_ENV"), "AWS_Lambda") ||os.Getenv("AWS_LAMBDA_RUNTIME_API") != "" {// Lambdaとして動いているlambda.Start(handler)} else {// Lambdaではない// ...}2020-11-18現在カスタムランタイム provided.al2 で AWS_EXECUTION_ENVが設定されない不具合AWS_LAMBDA_RUNTIME_APIがあるかも⾒たほうがよいサポートケースで報告済、近⽇修正予定とのこと
コマンドラインオプションは環境変数でも設定できるようにLambdaでは起動時にコマンドライン引数を指定できないのでflag は環境変数でも指定できるようにしておく$ myapp -port 8080$ MYAPP_PORT=8080 myappLambda に限らずコンテナでも環境変数のほうが個別に上書きできて便利
コマンドラインオプションは環境変数でも設定できるようにflag.VisitAll()を使うと…func main() {var port intflag.IntVar(&port, "port", 8080, "port number")flag.VisitAll(func(f *flag.Flag) {name := "MYAPP_" + strings.ToUpper(f.Name)if v, exists := os.LookupEnv(name); exists {f.Value.Set(v)}})flag.Parse()fmt.Printf("%d\n", port)}
fujiwara/ridgehttps://github.com/fujiwara/ridgeAPI Gateway REST/HTTP API ALB Lambda の HTTP ハンドラを net/http で書ける君package mainimport ("fmt""net/http""github.com/fujiwara/ridge")func main() {var mux = http.NewServeMux()mux.HandleFunc("/", handleRoot)ridge.Run(":8080", "/", mux)}func handleRoot(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "text/plain")fmt.Fprintln(w, "Hello World")}
ridge を使ったバイナリでも⾃動判別Lambda として動作する場合Lambda payload を net/http.Requestに変換handler アプリケーション に *Request ResponseWriterを渡すnet/http.ResponseWriterに書かれたものを Lambda response に変換Lambda 以外で動作する場合net/http.Serverでネイティブな HTTP server として動作EC2 ECS 等でもそのまま動く!
【PR】 Lambda のデプロイには fujiwara/lambroll をhttps://github.com/fujiwara/lambrollLambda Function のみ をデプロイすることに特化したミニマルなデプロイツール他のものは⾃分で⽤意できるんだ!という⼈向けfunction.jsonとGoのビルド済バイナリだけで lambroll deployできます{"FunctionName": "my-go-lambda","Handler": "handler","MemorySize": 256,"Role": "arn:aws:iam::123456789012:role/lambda","Runtime": "go1.x"}