$30 off During Our Annual Pro Sale. View Details »

シングルバイナリにこだわる - AWS Lambda編 / Nature Bath vol.6

シングルバイナリにこだわる - AWS Lambda編 / Nature Bath vol.6

Nature Bath vol.6【勉強会】みんなのGo言語 執筆陣による「Go開発・運用の現場」の発表資料です
https://nature.connpass.com/event/194611/

FUJIWARA Shunichiro

November 18, 2020
Tweet

More Decks by FUJIWARA Shunichiro

Other Decks in Technology

Transcript

  1. シングルバイナリにこだわる - AWS Lambda 編
    2020.11.18 Nature Bath vol.6
    ⾯⽩法⼈カヤック 藤原俊⼀郎 @fujiwara

    View Slide

  2. @fujiwara
    github.com/kayac/ecspresso
    Amazon ECS デプロイツール
    github.com/fujiwara/lambroll
    AWS Lambda デプロイツール

    View Slide

  3. AWS Lambda を何の⾔語で書いていますか?
    Node.js
    Python
    Ruby
    Java
    Go
    .NET
    Custom Runtime Bash Perl COBOL...

    View Slide

  4. Go で Lambda を書くと嬉しい
    ランタイムの寿命に影響されにくい
    デプロイするものはビルド済のx86_64バイナリ = ⾔語のEoLに影響されない
    起動も実⾏も速い
    Goが好き

    View Slide

  5. Go で Lambda を書く
    https://github.com/aws/aws-lambda-go#getting-started
    // main.go
    package main
    import (
    "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 Lambda
    lambda.Start(hello)
    }
    この main.go をビルドして Zip に含めて Handler として呼ぶ

    View Slide

  6. kayac/s3-object-router
    https://github.com/kayac/s3-object-router
    S3 に置かれた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 を呼んで実⾏する

    View Slide

  7. 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
    ってしたい

    View Slide

  8. 「シングルバイナリにこだわる」とは
    Lambda⽤とCLI⽤のバイナリを個別にビルドするのは無駄では???
    main.go がちょっと違うだけで本体はほぼ同じ
    ビルド時間2倍、サイズも2倍
    ファイルの管理も⾯倒
    Lambda でも CLI でも動く1個のバイナリを GitHub releases においておきたい

    View Slide

  9. 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
    があるかも⾒たほうがよい
    サポートケースで報告済、近⽇修正予定とのこと

    View Slide

  10. コマンドラインオプションは環境変数でも設定できるように
    Lambdaでは起動時にコマンドライン引数を指定できないので
    flag は環境変数でも指定できるようにしておく
    $ myapp -port 8080
    $ MYAPP_PORT=8080 myapp
    Lambda に限らずコンテナでも環境変数のほうが個別に上書きできて便利

    View Slide

  11. コマンドラインオプションは環境変数でも設定できるように
    flag.VisitAll()
    を使うと…
    func main() {
    var port int
    flag.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)
    }

    View Slide

  12. fujiwara/ridge
    https://github.com/fujiwara/ridge
    API Gateway REST/HTTP API ALB Lambda の HTTP ハンドラを net/http で書ける君
    package main
    import (
    "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")
    }

    View Slide

  13. ridge を使ったバイナリでも⾃動判別
    Lambda として動作する場合
    Lambda payload を net/http.Request
    に変換
    handler アプリケーション に *Request ResponseWriter
    を渡す
    net/http.ResponseWriter
    に書かれたものを Lambda response に変換
    Lambda 以外で動作する場合
    net/http.Server
    でネイティブな HTTP server として動作
    EC2 ECS 等でもそのまま動く!

    View Slide

  14. 【PR】 Lambda のデプロイには fujiwara/lambroll を
    https://github.com/fujiwara/lambroll
    Lambda Function のみ をデプロイすることに特化したミニマルなデプロイツール
    他のものは⾃分で⽤意できるんだ!という⼈向け
    function.json
    とGoのビルド済バイナリだけで lambroll deploy
    できます
    {
    "FunctionName": "my-go-lambda",
    "Handler": "handler",
    "MemorySize": 256,
    "Role": "arn:aws:iam::123456789012:role/lambda",
    "Runtime": "go1.x"
    }

    View Slide