LoginSignup
9
11

More than 5 years have passed since last update.

Google App EngineでGo + gin frameworkでデプロイにハマる事と解決方法

Posted at

前提条件

  • Goでgin-gonic frameworkでアプリを作る
  • ローカルではIntelli-J IDEを使って開発し、起動し、ソースレベルデバッグしたい
  • サーバーは、Google App Engine(GAE)上で動かす
  • ginのLoadHTMLGlobでtemplateをロードできるようにする

GAEのデプロイに関する話

  • app.yamlファイルにGAEで動かすための設定をいろいろ書く
  • GAEは特殊でinit()という関数が最初に呼ばれる
  • mainパッケージのmain()があるgoファイルを含んではならない
  • もしあると、このようなエラーが出てデプロイできない
RuntimeError: 2016/12/04 12:06:47 go-app-builder: Failed parsing input: parser:
found a top level package main with function main, 
but main does not call appengine.Main();
see https://godoc.org/google.golang.org/appengine#Main for more information
  • app.yamlと同一フォルダにあるファイルおよびディレクトリがアップロード対象になる

Intelli-Jの話

  • mainパッケージのmain()関数がないと起動できない
  • つまりローカル開発ではmainパッケージのmain()関数が必要

ハマる問題

  • mainパッケージがデプロイ対象にならないようにする必要がある
    • app.yamlのskip_filesに書いてもだめ
    • go buildのtagオプションで切り分けるのもだめ
  • app.yamlと同じディレクトリに、テンプレートファイルがないと、router.LoadHTMLGlob()でテンプレートファイルロードに失敗する。以下のようなエラーが出る
html/template: pattern matches no files: `./templates/**/**/*`

フォルダ構成

  • main.go
  • app/
    • controllers/
    • models/
    • app_init.go
  • gae/

    • app.yaml
    • init.go
    • templates/
      • home/
        • index.tmpl
    • static/
      • hoge.js
      • hoge.css

  • appフォルダはアプリに関するソースファイルおよびテンプレートを配置
  • templatesはテンプレートファイル置き場
  • staticフォルダは静的ファイル置き場
  • gaeフォルダはGAEに関するファイル
  • app/main.goは、mainパッケージでmain関数がある
  • gae/init.goは、gaeパッケージでinit関数がある
    • パッケージ名はmainでなければなんでも良い

main.goでもinit.goでも共通で初期化できるようにする。

app/app_init.go
package app

〜〜〜中略〜〜〜

func app_init(gae bool) *gin.Engine {
    router := gin.Default()
    // router.GET("/", root())とか
    if !gae {
        // GAEでないなら、ginのrouter.Static()でstaticディレクトリ指定
        // GAEの時は、app.yamlに従う
        router.Static("/static", "./gae/static")
    }
    if gae {
        router.LoadHTMLGlob("./templates/**/**/*")
    } else {
        router.LoadHTMLGlob("./gae/templates/**/**/*")
    }
    return router
}

ローカルではポート指定してHTTPサーバー起動するところまで書く

main.go
package main

〜〜〜中略〜〜〜

func main() {
    router := app.app_init(false)
    router.Run(":5000")
}

GAEはrouter.Run()で起動しないことに注意

init.go
package gae

〜〜〜中略〜〜〜

func init() {
    router := app.app_init(true)
    http.Handle("/", router)
}

app.yamlにGAE上でのstaticディレクトリ参照を書く

app.yaml
application: myapp
version: v1

runtime: go
api_version: go1

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: _go_app

ポイント

  • main.goとinit.goを別のフォルダに置く
  • Intelli-Jは、main.goのmain()を起点にして起動する
  • GAE上ではinit.goのinit()が起点となり起動する
  • それぞれテンプレートディレクトリまで適切なパスが渡るようにする
  • GAEのデプロイコマンドは、gaeフォルダのapp.yamlを指定し、そのフォルダのファイル、ディレクトリがデプロイ対象になる
  • templatesディレクトリもgaeフォルダ以下に置く
  • デプロイコマンド例
$ appcfg.py update ./gae/app.yaml
9
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11