25
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GAE/Go+glide的な構成での環境構築 ~ローカルサーバー立ち上げまで~

Last updated at Posted at 2017-02-06

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

開発ツール

以下のツールをインストール

必須

その他(あると便利)

環境構築

プロジェクト作成

goenv未使用の場合
  • GOPATH以下にappディレクトリを作成する
  • appディレクトリ内に、app.yamlを作成する
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 {パッケージ名}を実行する
glide.yaml
# cloud.google.com/go/storageを使用する場合
# glide getを実行した場合は自動でパッケージが追記される

package: .
import:
  - package: cloud.google.com/go
    subpackages:
      - storage

ここまでで以下のような構成になっているはず(glide.yamlを直接編集した場合はまだglide.lockvendorディレクトリは作成されない)

$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
app.yaml
application: sample-crud
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app
  • glide.yaml
glide.yaml
package: .
import:
- package: golang.org/x/net
  subpackages:
  - context
- package: google.golang.org/appengine
  version: ~1.0.0
  subpackages:
  - datastore
  • ソースコード
app/main.go
package main

import (
	"net/http"
	"sample"
)

func init() {
	http.Handle("/foo", sample.GetCRUDSampleHandler())
}
src/sample/crud.go
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でのパッケージ管理は基本になるかと思うので、手順などや構成などを忘れないように。

25
15
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
25
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?