Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App Engine Standard Go 1.11へのマイグレーションの方針が大幅に変更されたので追従した #101

Merged
merged 2 commits into from
Jul 2, 2019
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 21 additions & 121 deletions app-engine/note/gaego19-migration-gaego111/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,138 +2,45 @@

tag:["google-app-engine", "Go"]

App Engine Standard Go 1.9からGo 1.11へのマイグレーションは、単純なGoのアップデートだけでなく、ランタイム自体に大きな変更が入っています。
そのため、単純にGo 1.11を使うようにしただけではDeployすることができず、ある程度の作業が必要です。
この記事はその作業はどのぐらいのものなのかと、Google App Engine Standard 2nd Generationと呼ばれる新たなランタイムを使うと何が変わるのかについて、そして、いつアップデートすべきか?について記します。
App Engine Standard Go 1.9からGo 1.11への移行ではいくつか嬉しい機能が追加されています。
それは下回りがgVisorになることによって生まれた恩恵です。

Google App Engine Standard 2nd Generationがどういうものなのかは以下を確認するとよいです
gVisorがどういったものなのかは以下を確認するとよいです
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我々は知っていることを書きたくなりがちですが、 gVisor の話は脇道なのであまり書かなくても良いのでは。
「Go 1.11 以降は従来のサンドボックスから gVisor ベースのサンドボックスになることでいくつかの制限がなくなった。」として、その後は「Go 1.11 になって緩和されこと」として言及するくらいで良いかと。
嬉しいことについても、嬉しいことがあるから移行するんじゃなくて迫られているから移行するのであって今回は必ずしも必要ないですしね。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元々書いてあったから、とりあえず残したけど、ごっそり消しますかねー


* [Java 8 ランタイム以降のサンドボックスと gVisor by @apstndb](https://docs.google.com/presentation/d/1GKkv6GAelTwieGThqnX28f6dc7fXKwDPuypRzCYT_Zk/edit#slide=id.p)
* [GCP のサーバーレスを支える gVisor by @apstndb](https://docs.google.com/presentation/d/14AAOJFsf9bSkJPA0rSAwFcn5XIcoalntCsUN8pTGO00/edit#slide=id.p)
* [gVisorとGCP by @apstndb](https://docs.google.com/presentation/d/1F6k6bBS7BOUQWl9WGEpQJDyfvd04Et_-EeIHRoQzz-Y/edit#slide=id.p)

## 2nd Generationになって嬉しいこと
## gVisorになって嬉しいこと

2nd Generationは1st Generationを以下を解決するために生まれました
gVisorは元々のApp Engineのサンドボックス環境に存在した課題を解決するために生まれました

### 言語の機能をそのまま使えるようにする

1st GenerationではGoの場合、unsafeが使えないなどの問題がありました。
元々のRuntimeではGoの場合、unsafeが使えないなどの問題がありました。
ものすごく致命的というわけではないですが、一部のライブラリが使えなかったりするので、多少不便でした。

外と通信するにもApp Engine SDKの機能を利用する必要がありました。
httpを行いたければ、App Engine URLFetch APIを利用する。
Socket通信を行いたければ、App Engine Socket APIを利用する必要がありました。
2nd Generationでは、http clientなどがそのまま使えるようになっています。
gVisor版では、http clientなどがそのまま使えるようになっています。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Socket API なしで net.Dial や gRPC 接続する GCP クライアントライブラリが使えるようになったことで Cloud Spanner 以外も使えるようになったので、一般的にここに含めても良さそうな。


Local FileへのWRITEも行えなかったので、一時ファイルを書き込む必要があるライブラリを使うのも困難でした。
2nd Generationでは `/tmp` にWRITEできるので、一時ファイルを保存できるようになりました。

### App Engine APIからの脱却

App Engineは単純にHTTP Serverとして存在しているわけではなく、非常に多くの機能を持っています。
Log, Datastore, Memcache, Users, Mail, Search, TaskQueue, Image, Blobstoreなどです。
これらはGoogle Cloud Platformが登場する以前から存在し、App Engine SDKとしてClientが提供されていました。
便利な機能たちではありますが、App Engine専用の存在なので、強力なロックインを招きます。

2nd GenerationではなるべくApp Engine packageを使わない方向に進んでいます。
1.9まではログを出力するにも `google.golang.org/appengine/log` pakcageを利用する必要がありましたが、1.11では単純に標準出力に出力するだけでログが出力できます。

urlfetch, socketも使う必要がなくなりました。
これらはGoogle Cloud PubSubやGoogle Cloud StorageなどGCPのサービスを利用する時も必要で、更にQuotaがあるというものだったので、非常に不便でしたが、解決しました。
gVisor版では `/tmp` にWRITEできるので、一時ファイルを保存できるようになりました。

### Google Cloud Spannerへの接続

App Engine 1st GenerationはGoogle Cloud Spannerと接続することができませんでしたが、2nd Generationでできるようになりました。

## 2nd Generationになって悲しいこと

### `login:required` , `login:admin` の廃止

Users Serviceの機能として、 `app.yaml` に記述すれば、アクセス制限ができたこの機能が廃止されました。
Go 1.11ではすでに動かないので、なんらかの方法で実装し直す必要があります。
ただ、2018/11/25時点ではこの機能を完全に代替えするGCPの機能はありません。
似たような機能として [Cloud Identity-Aware Proxy](https://cloud.google.com/iap/) があるのですが、Users Serviceとまったく同じことはできません。
できないこととして、 `default` serviceは認証なしでOKで、 `/mypage` は `login:requreid` で、 `admin` serviceは `login:admin` みたいなことができません。
Serviceごとに認証可能なメンバーを変更するということはできるのですが、 `Reqeust Pathで変更する` のと、 `いずれかのServiceは認証なし` というのができません。
公式ドキュメントのMigration先はFirebase Authなどを使って、頑張れ!となっているのですが、結構頑張らないとできない気がします。

### Request Logがまとまらない
従来のApp Engine SandboxではGoogle Cloud Spannerと接続することができませんでしたが、gVisor版でできるようになりました。

1st GenerationはRequestごとにApplication LogがきれいにStackdriver Loggingの1Entityにまとまるようになっていました。
しかし、2nd GenerationではLog出力するとすべて別のEntityとして出力されるので、複数リクエストが来ている状態だと、見るのがなかなか大変です。
出力したログメッセージ以外の部分にリクエスト情報などたくさんの情報が付与されるので、ログの容量が大きくなるのも嬉しくない点です。
## Go1.11でいくつか廃止されたこと

### app.yaml includes の廃止

includesはapp.yamlに別のファイルをincludesする機能。
Deploy時に環境ごとに差異がある部分を取り込むのに地味に便利でしたが、廃止されました。
同じようなことはShellなどを駆使すればできれば可能ですが、数が多いと少々めんどうです。

### 脱却しきれないApp Engine API

Go 1.11ではApp Engine APIたちが基本的にサポートされたままなので、必ずしもApp Engine APIを完全脱却する必要はありません。
ただ、完全脱却できるとUnitTestがやりやすくなったりするので、挑戦したくなるのが人情です。
しかし、現状、完全脱却は難しい現状があります。
urlfetch, socketのように完全に脱却できたものもありますが、移行先のGCPの機能がないものが多いです。

#### App Engine APIの移行先

* Users -> Identity-Aware Proxy?
* Memcache -> Cloud Memorystore for Redis
* Datastore -> Cloud Datastore
* Search -> ...?
* Mail -> SendGrid?
* TaskQueue -> Cloud Tasks
* Cron -> Cloud Scheduler
* Image -> ...?

##### Users -> Identity-Aware Proxy?

上に書いたようにまったく同じことができるわけではない

##### Memcache -> Cloud Memorystore for Redis

ポジション的には正しいが、Cloud Memorystoreは現状、同じVPCからの接続しか許可していないので、App Engine Standardからは接続できない。
Cloud SQL Proxyのような機能が登場するのが待たれる。

##### Datastore -> Cloud Datastore

後ろ側は同じなので、機能的には同じですが、APIのInterfaceが異なるので、ソースコードは修正する必要があります。
[goon](https://github.com/mjibson/goon) , [nds](https://github.com/qedus/nds) を使っている場合は、 `google.golang.org/appengine` にがっつり依存しているので、Libraryたちが対応するまでは移行できません。
逆に考えるといつの日かLibraryが何かの対応をしてくれるかもしれないので、それまでのんびりしていてもよいかもしれません。

`google.golang.org/appengine` でも、 `cloud.google.com/go/datastore` でも差し替えれるようにした [go.mercari.io/datastore](https://github.com/mercari/datastore) を使っておくという手もあります。

##### Search -> ...?

形態素解析ベースの全文検索機能は現状GCPにはない。
公式ドキュメントでは自前でElastic Searchを運用するのがマイグレーション先になっている。

##### Mail -> SendGrid?

メール送信はずいぶん昔に非推奨になっているので、サードパーティに頼る形に。

##### TaskQueue -> Cloud Tasks

[Cloud Tasks](https://cloud.google.com/tasks/) がベータになっている。
ただ、まだPushしかない。
また、Cloud Tasksに移行すると、TaskQueue.AddがDatastore Transactionに参加できなくなる。

##### Cron -> Cloud Scheduler

[Cloud Scheduler](https://cloud.google.com/scheduler/) がベータになっている。

##### Image -> ...?

Image Serviceは地味に強力な機能で、ほぼ無料で動的にサムネイルなどを作れる強力な機能でした。
GCPの機能としては無いので、サードパーティの移行先として各種CDNのImage Optimizationを使うことを考える必要があります。

* [Akamai Image Manager](https://www.akamai.com/jp/ja/products/web-performance/image-manager.jsp)
* [Fastly Image Optimization](https://www.fastly.com/io)
* [SAKURA Internet Image Flux](https://www.sakura.ad.jp/services/imageflux/)

## 2nd Generationへのマイグレーション
## Go1.11へのマイグレーション

基本的には [公式ドキュメント通り](https://cloud.google.com/appengine/docs/standard/go111/go-differences#migrating-appengine-sdk) に行えばよい。
Copy link
Contributor

@apstndb apstndb Jul 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

公式ドキュメントでは optional だが strongly recommended として App Engine API からの移行について言及しているが期限が迫る Go 1.9 から Go 1.11 への移行に必須ではないため、このドキュメントでは扱わないというコンテキストを書いておいた方が、公式ドキュメントを読んで混乱する人が減りそうですね。

最低限、必要なのはapp.yamlの修正とmain packageを作成すること。
Expand Down Expand Up @@ -177,25 +84,18 @@ Go 1.9までは `app.yaml` の位置がWorking Dirでしたが、Go 1.11の場
[サンプル](https://github.com/sinmetal/codelab/commit/f3eb2cacb0af05bcd733742e225fe68544fda2b3) のようにトップをmain packageにしてしまえば、OKです。
2つ目はシンボリックリンクなどを利用して、両方のPathでアクセスできるようにしてしまう方法です。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app.yaml で main を指定できる機能は移行用にできたものだと思うので、これを使うことで解決できるのでは。
https://cloud.google.com/appengine/docs/standard/go111/config/appref?hl=en#main

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


## 2nd Generationが目指すゴール
### Local開発環境の移行

2nd GenerationはGCPより前からあるApp Engineの資産を精算しようとしているように見えます。
App Engine Standard 1st Generationはそれだけで完成されたProductでしたが、GCPが登場してからの世界観とはなじまない部分が出てきています。
それを解決しようと [Managed VMs](https://qiita.com/sinmetal/items/68f0e21e1f33e3a553a1) そして [App Engine Flex](https://qiita.com/sinmetal/items/080a79702b060b33de69) が生まれたりもしましたが、完全には解決できませんでした。
2nd Generationは今のGCPと親和性の高いWeb ApplicationのためのPlatform as a Serviceを新たに作り出すのがゴールになりそうです。
ただ、その道は始まったばかりで、まだしばらくかかると思います。
元々は `goapp` というToolを利用してLocal Serverの立ち上げ、UnitTest, Deployを行っていたけど、Go1.11からは標準のやり方に寄せるようになっている。

## いつGo1.11にアップデートすべきか?
* UnitTest `go test`
* LocalServer `dev_appserver.py`
* Deploy `gcloud app deploy`

これから新しくApp Engineでアプリケーションを作り始める場合は、Go1.11で作り始めるのがおすすめです。
その時にApp Engine APIを使うかどうかは少し悩ましいところですが、現状無料で便利なものが多いので、使っちゃってもいいんじゃないかと思います。
特に個人で開発してる場合は、Quotaに悩まされたりすることもないはずなので、少なくとも2021年ぐらいまでは何の問題もないでしょう。
組織で利用する場合は、いつの日か別のものに置き換えれるのが楽になるようにするのや、UnitTestのために薄いWrapperを作っておくとよいかもしれません。
Go 1.11から依存関係の解決に [Go Modules](https://github.com/golang/go/wiki/Modules) が使えるようになっている。
また、Deploy時に裏で [Google Cloud Build](https://cloud.google.com/cloud-build/) を利用するようになっている。
Go Modulesを利用した時にDeployの時間が長くなることがあるので、その場合は https://github.com/gcpug/nouhau/issues/87 のようなやり方をすると高速化できる。

しかし、1st Generationで作られたアプリケーションを2nd Generationへの移行は大きなBreaking Changeを伴います。
まだ、1.11がベータで、1.9が使えなくなるのは2021年ぐらいだと思うので、そんなに急いでアップデートしなければいけないわけでもありません。
特に `login:admin` は移行先が難しいので、これに依存している場合は [Cloud Identity-Aware Proxy](https://cloud.google.com/iap/) が進化するのをしばらく待つのも一つの選択肢です。
`Go 1.11の機能がものすごく使いたい` `urlfetch, socketで困っている` `Cloud Spannerを使いたい` などの理由がない限りはもう少し待ってもいいかもしれません。
## いつGo1.11にアップデートすべきか?

`login:admin を使っていない` `ログがばらばらになってもさほど気にならない` 場合は、割と簡単にマイグレーションが可能です。
ベータなのが気にならなければ、さくっとGo 1.11にしてしまってもよいでしょう。
App Engine 1.9は2019年10月1日に新たにDeployをすることができなくなるので、Go 1.9のアプリケーションがある場合、すぐにGo1.11への移行をすべき。