Go
GAE
golang
gcp

早速GAE/Go go1.11の次世代ランタイム試してみた

Betaが公開されたので早速触ってみたのでサラッと書いてみた

go1.9ランタイムからgo1.11ランタイム(2nd gen)に移行するためには最低限やることと気をつけること

中身的には このへん の内容です。

app.yamlで go111 ランタイムを指定する

今までは多分 api_version で指定していたと思いますが、 api_versionDeprecated になります。

そのため、 runtime へ指定します。

また、以下のタグも同様に Deprecated になります

  • application_readable
  • builtins
  • login
  • libraries
  • threadsafe
  • skip_files

Deprecatedのフィールドを利用している場合、deploy時にエラーになるようです。

サービス単位でmainパッケージが必要

旧世代のランタイムだとinitで初期化できていましたが、今後はmainパッケージが必要になります。

appengine ビルドタグが廃止されます

なんだっけ?これ。

追記 2018/10/16

// +build !appengine みたいなやつですね。

GoのBuild Constraintsという仕組みを利用しており、こちら に説明があります。

appengineの場合はtenntennさんからコメントで頂いているように、unsafeパッケージなどをライブラリで利用していた場合にappengineでは利用できないため指定されている場合などがあったようです。

実際、unsafeが利用できるようになったので以下のようなコードをappengineにデプロイすることができました。

byteをStringにする際に最速の変換方法です。

b := []byte("ハローワールド")
w := *(*string)(unsafe.Pointer(&b)) // []byte -> string
fmt.Println(w)  // ハローワールド

依存関係をプロジェクトに取り込む方法が変わって以下の2パターン

  • アプリケーションのコードと依存関係をGOPATHに配置する
  • go.modファイルを利用して定義する

Appengine固有のAPIではなく、Google Cloudクライアントライブラリを使って!

GAE/FEやGKEと同様にGoogleCloudクライアントライブラリを利用する必要があります。

このへん にまとまっています。

TQ、Datastore、AppengineMemcacheなどを利用していた方はもれなく移行が必要です。

appengineパッケージが必要な場合には、google.golang.org/appengineパッケージが利用できます。

追記 2018/10/16

Appengine SDKからCloud SDKに絶対移行する必要がある。という感じで書いてしまっていましたが、訂正です。

  • CloudSDKへの移行は strongly recommend であって必須ではありません。
  • google.golang.org/appengineパッケージ(Appengine SDK)を引き続き利用できますが、その際にはmainパッケージでappengine.Main() を実行する必要があります。
    • appengine.Main内で internal.Main() を呼んでおり、内部で http.ListenAndServe を行っているので、main内に自分で実装する必要はありません。(下にMainの実装など書いていますが。)

apstndbさんご指摘ありがとうございます。

go1.11ランタイムを利用する場合にはgcloud app deployコマンドを利用する必要があります

goapp コマンドはもう卒業できます。

gcloudコマンドでのデプロイも依存をきれいに解決してくれているように見えるので(vendorなど)もうgcloudで特にトリッキーなgopath設定などしなくても大丈夫なはず。(ちゃんと確認してない)

mainってどうすればいい?

mainのサンプルは ここ にあります。

http.HandleFunc("/", indexHandler)

// [START setting_port]
port := os.Getenv("PORT")
if port == "" {
    port = "8080"
    log.Printf("Defaulting to port %s", port)
}

log.Printf("Listening on port %s", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
// [END setting_port]

こんな感じにすると良いようです。

START-ENDのところを appengine.Main() を呼んでも問題なく動いているように見えます(推奨されるのか不明だけど)

追記 2018/10/16

上で書いたように、Appengine SDKを利用する場合には appengine.Main() を実行しておく必要があります。

httpハンドラを登録する

ハンドラを登録するには以下のいずれかを利用する必要があります

  • http.HandleFunc()の呼び出しをすべてmainに持ってくる
  • http.HandleFunc()を含むinit関数が起動時に呼び出されるようにパッケージをmainパッケージにインポートする

後者の場合、以下のコマンドが便利

gp=$(go env GOPATH) && p=$(pwd) && pkg=${p#"$gp/src/"} && find . -name "*.go" | xargs grep "http.HandleFunc" --files-with-matches | grep -v vendor/ | grep -v '/main.go' | sed "s#\./\(.*\)/[^/]\+\.go#\t_ \"$pkg/\1\"#" | sort | uniq

気づいた点

  • ログが旧ランタイムのときのようにリクエストコンテキストでまとまって出力がされなくなっている
    • 現状のFE、GKE、GAE 2nd-gen ランタイム は共通してそのようになっているようなのでしょうがない。
  • uploadしたくないファイルは .gcloudignore ファイルで指定できるっぽい(.gitignoreと同じ記述で指定できる)

サラッと触ってみた結果はこんな感じ。
改めてもう少し触ってみて記事を書こうと思う