golangのお勉強4 + apex + lambda

  • 1
    いいね
  • 0
    コメント

概要

swaggerで作ったコードを使うのか、別の書き方をするのか、lambdaにするのか、golangの使い方をちょっとずつ調べる。

golang on lambda

» Apex で Go はどう実行されているのか TECHSCORE BLOG
http://www.techscore.com/blog/2016/12/08/how-does-golang-run-on-apex/

nodeで標準出力経由なので2回プロセスが立ち上がる

eawsyのaws-lambda-goを使ってみる — そこはかとなく書くよん。
http://tdoc.info/blog/2016/11/02/eawsy_lambda.html

nodeの標準出力経由じゃなく実行する手法(pythonからバイナリの拡張として実行)
よさそうだけど保留

AWS Lambdaで効率的にgoバイナリを実行する — そこはかとなく書くよん。
http://tdoc.info/blog/2016/01/07/lambda.html

そのため、一回一回のリクエストのたびにプロセスを立ち上げるコストが掛かり、毎回500msecほどかかってしまう、という事になってしまいます。

この問題点を解消するライブラリがlambda_procです。

lambda_procでプロセスの立ち上げを最初だけにする方法。
そのぶん、お金はかかる認識であってるかしら。

packageをどう読み込むのか

lambdaに上がるのはバイナリで、コンパイルはローカルで実行するからどこにパッケージがあっても大丈夫。。という認識。

関数の戻り値の捨て方

_,err := hoge()

構造体について

Golang 構造体と仲良くなろう - Qiita
http://qiita.com/kkam0907/items/92d3d31c84c596eacaee

構造体とポインタとがある。
この辺まだ不安。

Api記述のライブラリ


せっかくswagger使うんだしswaggerに合わせる(任せる)かなあ。。

  • swaggerのコード

    Go言語でServer作る時に必要な知識メモ - Qiita
    http://qiita.com/awakia/items/ef8978fc33ffb1d27460

    mux(ムック)というライブラリでルーティングして、各ファイルの関数を実行
    muxはデフォルトのものよりリッチなルーティングがしたいときの選択肢なのかな

    ListenAndServe()からserve()まで辿ると、serveHTTP()を実行している。
    ここでwriter,responseを受け取ってhandlerを呼び出し始める。多分。

    API Gateway + Lambdaでcatch allした処理をApex + Goでnet/httpで扱える ridge を書いた - 酒日記 はてな支店
    http://sfujiwara.hatenablog.com/entry/2016/10/03/153022

    環境変数でローカル実行とlambdaの両方を記述
    ルーティング処理はlambdaのridgeで行う
    こちらのコードも、writerとrequestを渡して関数を実行してるな。。
    これならswaggerのコードを流用しやすそうな気がする。

  • echo

    自分で導入できるか試して動いてはいたはず。。
    ルーティング以外にも機能面でメリットがあるんだっけ。。

  • Go言語でServer作る時に必要な知識メモ - Qiita
    http://qiita.com/awakia/items/ef8978fc33ffb1d27460

    いろいろある。。気になるけど保留。

デバッグ

IntelliJ CE + Delve で Go 言語のデバッグ環境構築(Mac) - Qiita
http://qiita.com/mxxxxkxxxx/items/db952ddf5b13a594b55e

Go言語のデバッガをMacのIntelliJ IDEA CEで使う - くじらにっき++
http://kujira16.hateblo.jp/entry/2017/01/20/161012

lambdaにあげてつかってみてもうまく動いてくれない

API Gateway からCloudfrontのエラーが返ってくる

どうやら内部的に使っているらしい。
認証情報がうまく受け渡せないが理由がわからない。。

Amazon CloudFrontでAPI Gatewayの痒いところに手を届ける | Developers.IO
http://dev.classmethod.jp/cloud/cache-api-gateway-by-cloudfront/

ご存じの方もいると思いますが、そもそもAPI Gatewayは内部でCloudFrontの仕組みを利用しており、スケーラビリティやアベイラビリティの面でCloudFrontの特徴を享受しています。しかしながら、CloudFrontのフル機能をAPI Gatewayで利用できるわけではないため、場合によっては以下の図のようにAPI Gatewayの手前にCloudFrontを置く構成を取ることがあります。

試しに認証をはずしてみたがタイムアウトしてしまう

golangで相対パスでライブラリを読み込んでるせいだと思う。実行ファイルが大きくなってる。
hello worldだと問題なかったのに。。
たぶん、このへんはライブラリの呼び出しについて工夫すればいいのだろう。。
結果的には、lambda(Node.js)と実行ファイル(Golang)の間の値の受け渡し方法を
文字列に変更(構造体を渡してたわ)したら問題なかった。
標準出力でやりとりしてるからかと。。
でもまあ、Nodeが間にあるかは関係なく、webapiなのだから文字列で出力するのはあたりまえか。

この辺も、デバッグをしやすくしておかないと厳しいな。。
golangがわで、出力の手前でチェックをかければ、、それでも不測の事態がおきたらエラー箇所を探すのが大変そう。。
cloudwatchとかaws-xrayとかを理解したらいけるのかしら。。

この例は多分関係ないけどメモ。

Lambda + API Gateway で処理の実行がむちゃくちゃ遅くなるケースを調べた - Qiita
http://qiita.com/pm11op/items/eb148b23b062fe778bd1

カスタム認証がうまくつかえない

cloudfrontを手前に配置する場合が多いらしいからそうすればいいのかもしれない。
なんかくやしい。
認証情報を渡すことはできているけど、deny判定されるのがなぜかわからない。。
デバッグの方法を理解しないと進めない。わからない。。

Amazon API Gateway で Custom Authorization を使ってクライアントの認可を行う | Developers.IO
http://dev.classmethod.jp/cloud/aws/api-gateway-custom-authorization/

ヘッダーに Authorization: deny を付けると、アクセス拒否されます。

ユーザー認証の事例

Amazon API Gateway の Custom Authorizerを使い、User PoolsのユーザでAPI認証を行う - Qiita
http://qiita.com/horike37/items/7e0984e7729099032930

Amazon Cognito User Poolsを使って、webサイトにユーザ認証基盤を作る - Qiita
http://qiita.com/horike37/items/1d522f66452d3abe1203

ユーザの管理機能がサーバ側のコードを1行も書くこと無く実現できちゃうのはまじでスゴイです。
しかもセキュア。

gopathについてわからないので調べる

direnvで設定するのは悪くなさそう。。

これからGoを始める人のためのTips集 | The Wacul Blog
http://blog.wacul.co.jp/blog/2014/08/22/go/

GOPATH には2つ役割があります。
ビルド時のインポートパスとして、GOPATH に指定したすべてのディレクトリを参照する(コロン区切り)
go get コマンドで外部パッケージを読み込んだ時、 GOPATH の先頭のディレクトリにダウンロードする

direnvで解決するGOPATHの3つの問題点 - None is None is None
http://doloopwhile.hatenablog.com/entry/2014/06/18/010449

direnvを使うと、プロジェクトごと(ディレクトリごと)にGOPATHを分ける事ができます。
ところで、.envrcにlayout goを書いただけでは、 ~/gocode-foo/src以下には、プロジェクト本体と依存パッケージが区別なく置かれてしまいます。
そこで direnv edit . で .envrcに設定を追加します。

改めて、direnvを使いましょう! - HDE BLOG
http://blog.hde.co.jp/entry/2015/02/27/182117

direnvは他にも追加のコマンドを用意しています。
layout program_name :その言語用の開発環境をセットアップする
PATH_add path: 環境変数PATHにpathを追加
path_add envname path: 環境変数envnameにpathを追加
詳しくはdirenv-stdlib.1.mdをご覧ください。

また、~/.direnvrcに自作コマンドを書くこともできます。 後述のuse pythonなどは~/.direnvrcへの追記が必須です。

direnv/direnv-stdlib.1.md at master · direnv/direnv
https://github.com/direnv/direnv/blob/master/man/direnv-stdlib.1.md

layout go: Sets the GOPATH environment variable to the current directory.

他言語から来た人がGoを使い始めてすぐハマったこととその答え - Qiita
http://qiita.com/mumoshu/items/0d2f2a13c6e9fc8da2a4

自分がいま書いてるコードもGOPATH以下に置く、っていうのが他言語から来た人がハマるポイントだと思う。

Goのディレクトリレイアウト、IntelliJ IDEAの設定、バージョン管理を考える - Qiita
http://qiita.com/kai-zoa/items/2ba5be2ddb370e9bf962

この記事は当時どうにかプロジェクト単位でGOPATHを設定できないか試行錯誤していたときの備忘録ですが、もし同じことを考えてる人がいたらはっきり言ってそれは諦めることをオススメします。

なぜならプロジェクト単位でGOPATHを変えたり後述の方法でやっているような無茶をするとGoのエコシステムによる様々な恩恵が得られません。


まさにやろうとしてた。。
具体的な恩恵ってなんのことかしら。
プロジェクトを超えてgcする的なツールが用意されてるとかそういうことがあるのかな。

vendorがわからない。。

どうも読みに行ってくれない。
vendorじゃなくgopath直下で共通パッケージを読みに行くという記事もあれば、
実行してみるとhoge/srcをよみにいったりよくわからない。。

  • 試して読み込んでくれた
    • パターン1
      • コードをfunctions以下にapex的に配置
      • パッケージをsrc直下に配置
    • パターン2
      • ひとまずfunctionsを無視してsrcディレクトリにいれて、srcもvendorも直下ではなくディレクトリを設けた

[golang]Go1.6で正式採用されたvendorの参照の挙動をみてみた – しゃまとんのたね
http://shamaton.orz.hm/blog/archives/223

Lambda x Apexで、Goで書いたラムダ関数を楽に動かす - きょこみのーと
http://kyokomi.hatenablog.com/entry/2016/11/03/145147

GOPATH下にプロジェクトを置けば共通の実装とかをパッケージ化して使いまわせるので便利
下記の例だと resize パッケージと aws パッケージ

apex+lambdaでのgoの構成

基本的にはこんぱいるしてからアップロードするから、
リモート側は気にしなくていいという理解。

» Apex で Go はどう実行されているのか TECHSCORE BLOG
http://www.techscore.com/blog/2016/12/08/how-does-golang-run-on-apex/

Node.jsとGoが標準入出力パイプでやり取りしていることがわかりました。

glideについて

調べるうちに出てきた、、vendoringはglideがスタンダードになっているらしい。
あくまでここ数年。

Go言語をMacにインストールしてみる(goenv + direnv + glide) - Qiita
http://qiita.com/keitaMatsuo/items/466c6eabac4538d9de71

vendorフォルダ内に外部ライブラリをインストールでき、それをYAMLで管理する。
package.jsonのようなもの。

Glide で Go 言語のパッケージ管理と vendoring - Librabuch
https://librabuch.jp/blog/2016/04/go-lang-vendoring-glide/

コマンドは glide up -u -s と省略できます。--strip-vcs オプションは、 go get の後に各レポジトリの .git ディレクトリを削除するという単純な動作をします。s
vendor ディレクトリ配下にある vendor ディレクトリを除外する --strip-vendor オプションもあります。

srcディレクトリとかvendorディレクトリをapex配下でどう配置すればいいのか

go以外の事例も参考。。
functionsをsrc扱いにできないかしら。

Apexを使ってAWS Lambdaを楽に管理しよう - 病みつきエンジニアブログ
http://yamitzky.hatenablog.com/entry/2016/06/09/235548

functionsディレクトリはビルド用ディレクトリ(コミットしないディレクトリ)として割りきりましょう


apexだとvendorを使う想定の作りになってないのかも。わすれよう。

go言語について

無難にかけて質もいいけど、めんどくさいこともあるよってことかな。

なぜGo言語 (golang) はよい言語なのか・Goでプログラムを書くべき理由
http://www.yunabe.jp/docs/why_golang_is_good.html

Goは言語デザインとして正しく使うのが難しい、乱用するとプログラムを無意味に複雑にしてしまう機能が排除されています(高度な型システム・継承・Generics・例外・イベントモデルによる並行処理など)。 そのため、ある意味ではGoは「知的な」プログラマにとっては面白みに欠く言語だと思います。実際、Goを勉強しても概念的には目新しいものはなくて勉強することそのもので何かが学べるとは僕は全く思いません。 一方でプログラミング言語を習得することのゴールは、それを使って何か大規模なソフトウェアをチームで作ることであるのだと思います。 チームで大規模なソフトウェアを開発することはそれ自体が非常に難しいことです。そのゴールのためにプログラミング言語自体はシンプルに保ち、それ以外の部分により多くの労力をさけるようにするというのは僕は正しい判断だと思います。

Apexでgolangを使うかどうか

ここまで調べてまだちゃんと実装はしていない。
まだ準備が必要そう。
NodeからGoを呼び出す仕組みがネックになるポイントか。

  • 今回はgoをやめる。nodeとか。
  • 標準出力での受け渡しという点を考慮する(多分大丈夫かな。。)
  • lambdaをやめてgolangで実装つつ、公式にgolangが対応(すると信じる)したら移行する
  • ベンチマークを思い出す、性能的には多分意味あるのだろう

もうちょっとやってみるか。

AWS LambdaでJavaとNode.jsとGoの簡易ベンチマークをしてみた。 - 谷本 心 in せろ部屋
http://d.hatena.ne.jp/cero-t/20160101/1451665326

Goを使った場合は性能が安定しており、ウォーミングアップしても性能が変わりません。メモリ使用量が少ないのも良いですね。Node.jsから呼び出す際のオーバーヘッドがあるせいか、性能的にはウォーミングアップ後のJavaやNode.jsに一歩劣る感じでした。

プロキシ統合

なんとか動かせるところまで行ったけど、値がわたっておらず、ルーティングの結果、301になるみたい。。
ヘッダーの値を入力に使ってるからか。。
プロキシ統合とかマッピングテンプレートとかでいけるような。。いけない。
あれ、NodeからGoに渡すところってヘッダーの値とか渡してくれるのかしら。Apexは。
表週出力を経由してやりとりしてるから気を使ってくれてなければgo側で値が取れないかのかな。。


このスライドは参考になるな。

AWS Lambda in Golang
https://www.slideshare.net/KoichiroNishijima/aws-lambda-in-golang

マッピングテンプレートって何? 57 ๏ Integration Requestに指定できる、URLパラメータ/パス パラメータ/HTTPヘッダなどをLambda関数のコードに渡す ためのテンプレート ๏ 想定するURLパラメータとともに、APIGWのステージやHTTP ヘッダなどをLambda関数に渡すことが出来る

API Gatewayへの入力値にLambdaからアクセスする - Qiita
http://qiita.com/r7kamura/items/6420538789da95cd2f47

ridgeの意味がやっとわかった気がする。。
swagger.yamlがかなりシンプルになるな。。やってみよう。

API Gateway + Lambdaでcatch allした処理をApex + Goでnet/httpで扱える ridge を書いた - 酒日記 はてな支店
http://sfujiwara.hatenablog.com/entry/2016/10/03/153022

Configure Proxy Integration for a Proxy Resource - Amazon API Gateway
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html

pythonの事例だけどswagger.yamlの例もあるのでよい

AWS Lambda と Python で作る動的な HTML ページ - Qiita
http://qiita.com/kimihiro_n/items/15cce90ec93625c4445a

リソースを /{proxy+} としてあげることで、任意のパスのリクエストを同じ Lambda でさばくことが可能です。

検証の具体的な結果がみれる

Amazon API Gatewayが非常に便利なHTTPプロキシとして進化したらしいので使ってみた - Qiita
http://qiita.com/seiya_orz/items/4e9d8c62739399b076b7

APIGateway+lambda でのデバッグ方法

  • lambda(Node)
    • console.log
  • lambda(golang)
    • エラー出力らしい。 > os.Stderr.WriteString(“hoge”)
    • apex logs appname

apexのエイリアス

apex invokeだとcurrentを参照していて$LATESTじゃない
実際のapiをたたくなら$LATEST

ridgeを使う

とてもよさげだけど、イシューにあるようにルーターを強化したいな。。(しなくても大丈夫だった。)

router := sw.NewRouter()
router.HandleFunc("/", handleRoot)
http.Handle("/", router)

まずmuxを理解したい

https://github.com/gorilla/mux

Subrouter()とかr.Walkとかつかえそうな気がする。