GAE/Go+glide的な構成での環境構築とローカルサーバの起動までの手順備忘録
#プロジェクト構成
この解説では、最終的に以下のようなディレクトリ構成になる。
$GOPATH
├── app
│ ├── app.yaml <= app.yamlはsrcディレクトリの外に配置
│ └── main.go
└── src
├── glide.yaml <= glide.yamlはsrc直下
├── glide.lock
├── {package} <= 基本的なソースコードはsrc以下のパッケージに
└── vendor <= アプリで利用する外部パッケージ
GAE/Goの仕様でapp.yamlが配置されているディレクトリ以下のコードが全てビルドされてしまう。
この仕様のため、vendoringを使用して外部ライブラリを利用しようとすると、ほぼ間違いなくビルドでこけてしまうのでapp.yamlはsrcディレクトリには含めないようにしている。
参考
Google App EngineでGoを動かすときに知っておくべきこと(ソースコード・ビルド編) - Hatena Blog
開発ツール
以下のツールをインストール
必須
- AppEngine SDK
- glide
- python2.7
その他(あると便利)
-
goenv
通常のGo開発環境とGAE/Goの環境の切り替えに -
pyenv
AppEngine SDKを動かすのに、python2系が必須のため、pythonのバージョンの切り替えが必要な場合
######参考
goenvでgae/goと普通のgoの環境を切り替える - Qiita
環境構築
プロジェクト作成
goenv未使用の場合
-
GOPATH
以下にapp
ディレクトリを作成する -
app
ディレクトリ内に、app.yaml
を作成する
application: {プロジェクト名}
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
goenvを使用する場合
- 以下のコマンドでプロジェクトを作成する
goenv -gae -go {$APPENGINE_SDK/goroot} -deps $GOPATH {プロジェクト名}
- 自動生成されたディレクトリ内に、
app
ディレクトリを作成 -
src
ディレクトリ内にapp.yaml
ファイルが自動生成されているので、app
ディレクトリに移動する
この時点で以下のような構成になる
${GOPATH}
├── activate(goenvでプロジェクトを作成した場合)
├── app
│ └── app.yaml
└── src
└── {プロジェクト名}.go(goenvでプロジェクトを作成した場合)
glide初期設定
glideの初期設定など
-
src
ディレクトリ内でglide create
を実行 -
glide.yaml
が自動生成されるので、使用する外部パッケージを記載するか、glide get {パッケージ名}
を実行する
# cloud.google.com/go/storageを使用する場合
# glide getを実行した場合は自動でパッケージが追記される
package: .
import:
- package: cloud.google.com/go
subpackages:
- storage
ここまでで以下のような構成になっているはず(glide.yaml
を直接編集した場合はまだglide.lock
とvendor
ディレクトリは作成されない)
$GOPATH
├── activate(goenvでプロジェクトを作成した場合)
├── app
│ └── app.yaml
└── src
├── glide.yaml
├── glide.lock
├── vendor
└── {プロジェクト名}.go(goenvでプロジェクトを作成した場合)
サンプルコード
ローカルサーバーの起動確認と、glideでの外部ライブラリ管理確認のサンプルコード。
DataStoreを使用して簡単なデータ登録と取得を行うAPIを作成してみる。
- ディレクトリ構成
$GOPATH
├── activate
├── app
│ ├── app.yaml
│ └── main.go
└── src
├── glide.lock
├── glide.yaml
├── sample
│ └── crud.go
└── vendor
- app.yaml
application: sample-crud
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
- glide.yaml
package: .
import:
- package: golang.org/x/net
subpackages:
- context
- package: google.golang.org/appengine
version: ~1.0.0
subpackages:
- datastore
- ソースコード
package main
import (
"net/http"
"sample"
)
func init() {
http.Handle("/foo", sample.GetCRUDSampleHandler())
}
package sample
import (
"encoding/json"
"fmt"
"net/http"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
)
const KindFoo = "foo"
type Foo struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
}
func GetCRUDSampleHandler() *CRUDSample {
return new(CRUDSample)
}
type CRUDSample struct {
ctx context.Context
w http.ResponseWriter
r *http.Request
}
func (c *CRUDSample) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c.ctx, c.w, c.r = appengine.NewContext(r), w, r
switch r.Method {
case http.MethodGet:
c.Get()
case http.MethodPost:
c.Post()
default:
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "Not found.")
}
}
type RespGetFoo struct {
Id int64 `json:"id"`
Foo string `json:"foo"`
Bar string `json:"bar"`
}
func (c *CRUDSample) Get() {
var foos []Foo
keys, err := datastore.NewQuery(KindFoo).GetAll(c.ctx, &foos)
if err != nil {
c.responseServerError(err)
return
}
result := make([]RespGetFoo, len(keys))
for i, f := range foos {
result[i] = RespGetFoo{keys[i].IntID(), f.Foo, f.Bar}
}
c.responseJson(http.StatusOK, result)
}
func (c *CRUDSample) Post() {
var foo Foo
err := json.NewDecoder(c.r.Body).Decode(&foo)
if err != nil {
c.responseServerError(err)
return
}
key := datastore.NewIncompleteKey(c.ctx, KindFoo, nil)
newKey, err := datastore.Put(c.ctx, key, &foo)
if err != nil {
c.responseServerError(err)
return
}
response := map[string]int64{"id": newKey.IntID()}
c.responseJson(http.StatusCreated, response)
}
func (c *CRUDSample) responseJson(status int, v interface{}) {
c.w.WriteHeader(status)
json.NewEncoder(c.w).Encode(v)
}
func (c *CRUDSample) responseServerError(err error) {
c.w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(c.w, err)
}
実行
- 依存ライブラリの取得
src
ディレクトリに移動して、glide up
を実行。
プロジェクトや、外部ライブラリの依存関係を解決してvendor
ディレクトリにぶち込んでくれる。
$ cd src
$ glide up
- ローカルサーバー起動
app
ディレクトリに移動し、goapp serve app.yaml
を実行
$ cd app
$ goapp serve app.yaml
注意点
ローカルサーバの起動にはpython2系が必要なため、ローカルサーバの起動前にpythonのバージョンを変更しておくこと。
pyenv
などを使っている場合は
$ cd app
$ pyenv local 2.7.13
とかを実行して、app
ディレクトリ以下のpythonバージョンのを固定しておくと便利
以上、GAE/Go+glideの環境準備からローカルサーバーの起動までの手順。
今後glideでのパッケージ管理は基本になるかと思うので、手順などや構成などを忘れないように。