Try Golang! Google App Engineへのデプロイ時にvendorで困った話
Be careful when deploying Golang app with vendor to GAE.
Google App Engine(以下、GAE)にGoのアプリケーションをデプロイする際、サードパーティ製のパッケージをvendorに格納してデプロイを実施したら、エラーが発生しまくった時の話です。ちなみに、今回組込むパッケージは、Echo 3.2.3。こちらはGo 1.8の環境で動くので、GAEのbeta環境を使用しています。
そもそもGoのvendorって?
Goにおける一般的なサードパーティ製パッケージの取得方法はgo get
です。ただこの方法では、パッケージはGOPATH配下に格納されるため、複数のプロジェクトを開発する際に制約が生まれてしまいます。そこでGo 1.5から登場したのがvendoringで、vendor
という名前のディレクトリにパッケージを格納することで、プロジェクト毎に依存パッケージのバージョンを管理することができるようになりました。
(project root)
|- app
| `- main.go
`- vendor
`- github.com/.../... <- main.goから参照可能
GAEへのデプロイ時にエラーが発生!
ところが、このvendoringを利用したプロジェクトをGAEにデプロイしようとすると、エラーが発生してしまいます。dev_appserver.py
を使用したローカル実行ではうまくいくのに!
実際にエラーを発生させたモジュール構成はこんな感じ。GOPATHを空にしてEchoをgo get
したものを、vendor
に移動しただけです。Echoが参照しているパッケージがいくつかあるので、vendor
配下は少し複雑です。
(project root)
|- app
| |- app.go
| `- app.yaml
`- vendor
|- github.com
| |- dgrijalva/jwt-go/...
| |- labstack
| | |- echo/...
| | `- gommon/...
| |- mattn/...
| `- valyala/fasttemplate/...
`- golang.org/x/...
GAEへのデプロイ時に発生したエラーはこんな感じ。そもそもEcho本体すら見つけることができません。
$ gcloud app deploy
ERROR: ... failed with return code [1].
...
2017/10/17 10:53:26 failed analyzing
cannot find package "github.com/labstack/echo" in any of:
($GOROOT not set)
.../github.com/labstack/echo (from $GOPATH)
...
エラーを解消してデプロイリベンジ!
- 対策その1
実はGAEのデプロイでimport文を解決する際、参照するディレクトリにvendor
は含まれていません。含まれるのは「app.yaml
を格納しているディレクトリとその配下」と「GOPATH以下」の2つ。ということで、ひとまずvendoringをやめて通常のGOPATHでやってみましょう。
$ gcloud app deploy
...
ERROR: (gcloud.app.deploy) Error Response: [9] Deployment contains files that cannot be compiled: Compile failed:
...
github.com/valyala/fasttemplate/template.go:12: can't find import: "github.com/valyala/bytebufferpool"
むむむ。Echoは見つかったようですが、別のパッケージが見つからないと怒られましたね。
- 対策その2
上のエラーは、valyala/fasttemplate
パッケージ内でvendor
が利用されており、ここに格納されているvalyala/bytebufferpool
が見つけられなかったために起きています。ということで、見つけられなかったモジュールは自前でgo get
しましょう。
$ gcloud app deploy
...
ERROR: (gcloud.app.deploy) Error Response: [9] Deployment contains files that cannot be compiled:
うーむ。パッケージはすべて見つかったようですが、コンパイルができないとな。
- 対策その3
自前でgo get
したモジュールと、もともとvendor
内にあったモジュールで競合が起きているので、vendor
内のモジュールは削除してしまいましょう。これで、GAEへのデプロイができるようになりました!
手動でパッケージ管理なんてやってられない!
正直、上の作業を毎回するのは大変!それに結局グローバルなGOPATHを使っているので、プロジェクト毎の管理ができていません。ということで、これらを解消するために、GlideというGoのパッケージ管理ツールとシンボリックリンクを使用します。
- Goのパッケージ管理ツール Glide
go get
の代わりに下のコマンドを実行することで、依存パッケージ内のvendor
を解決しながら、vendoring構成に基づいてパッケージをダウンロードしてくれます。また、パッケージ内のvendor
ディレクトリの削除もしてくれます。
$ glide up -v
- シンボリックリンクなGOPATH
GlideだけではGAEへのデプロイでエラーが発生するので、いっそのことデプロイ時だけ、GOPATHを変更してしまいましょう。ただvendor
の下には、GOPATHの下にあるsrc
ディレクトリがないので、以下のようにシンボリックリンクを使います。
(project root)
|- app
| |- app.go
| `- app.yaml
|- vendor
| |- github.com
| `- golang.org/x/...
`- gopath/src -> (project root)/vendor
GAEへのデプロイ時のみ、GOPATHを(project root)/gopath
に書き換えて実行してあげればうまくいきます。
ここまで書いてあれですが、GAEのデプロイ時もvendoringルールに則って解決してくれれば、こんな面倒くさいことはしなくて済むんですよね。Google Cloud SDKの今後の改善に期待しましょう!
以下は、大変参考にさせていただいたサイトです。