[GAE/Go] Gin で 作成した Web アプリを Google App Engine 上で動作させる (環境構築編：direnv + glide)

Go 製  Web フレームワーク Gin を使って簡単な Web アプリケーションを作成する過程で、色々と問題点が出てきてちょっとハマりました。

ググったところ、通常の Go と GAE/Go の開発環境が混在するとビルドやデプロイ時に不都合が生じることが分かりましたので、今回「direnv + glide」を使って GAE/Go 専用の開発環境を構築し直しました。以下、自分用手順メモになります。

なお、GAE/Go 専用の開発環境の必要性については、下記記事にてとてもわかりやすく説明されておりますので、そちらをご覧いただければと思います。

>- [Google App EngineでGoを動かすときに知っておくべきこと（ソースコード・ビルド編）]
(http://motemen.hatenablog.com/entry/2016/11/gae-go-building)

>- [GAE/Goのハマったところ(´･ω･`)]
(http://qiita.com/hogedigo/items/fae5b6fe7071becd4051)

>- [MacにGo言語の開発環境を構築する【準備編】 (direnv+go1.5Vendoring+glide)]
(http://blog.flup.jp/2016/02/16/dev_golang_keeping_vendoring/)


また、今回手順を実施するにあたり、多くの部分において以下の記事を参考にさせていただいております。

>- [Go+App Engine+Cloud SQLで始めるGo言語Webアプリケーション開発]
(https://developers.eure.jp/tech/go-appengine-sql-waf/)

>- [GAE/Go+glide的な構成での環境構築 ~ローカルサーバー立ち上げまで~]
(http://qiita.com/ryutah/items/d864310c62f0385d876d)

　
※ 環境
　macOS Sierra バージョン 10.12.3
　MacBook Air (Early 2014)


###1. Go のインストール
```Terminal:Terminal
$ brew update (# 念のためアップデート実施)
$ brew install go
$ go env GOROOT (# $GOROOT の確認)
    /usr/local/Cellar/go/1.8/libexec
```

###2. $GOPATH の設定
```Terminal:Terminal
$ cd $HOME
$ mkdir go
$ vim ~/.bash_profile (# .bash_profile ファイル内に下記内容を記述)

  # Set path for Golang
  export GOPATH="$HOME/go"
  export PATH="$PATH:/usr/local/Cellar/go/1.8/libexe/bin:$GOPATH/bin"
```
```Terminal:Terminal
$ source ~/.bash_profile (# 設定を反映させる)
```

###3. Python version 2.7 のインストール
Google App Engine の設定を行う際、Python version 2.7 を使用する必要がある。
当方の環境では Anaconda 仮想環境のバージョンを指定できるよう設定してあるので、そちらを利用する。(※ 設定の詳細は [過去記事](http://qiita.com/togoturns/items/b2ae361a669933c823f5) の手順 3 ～ 7 を参照)

```Terminal:Terminal
$ pyenv global anaconda2-4.2.0 (# 全てのディレクトリに対し、Python version 2.7 (anaconda2-4.2.0) の仮想環境を設定)
$ python --version (# python のバージョン確認)
    Python 2.7.12 :: Anaconda 4.2.0 (x86_64) 
```

###4. Google Cloud SDK インストール
下記ページより、Mac OS X（x86_64）用の SDK アーカイブパッケージをダウンロードし、解凍後に展開された全てのファイルを任意のディレクトリ直下に配置。

(参照)
- [Mac OS X 用のクイックスタート (Cloud SDK のインストール)]
(https://cloud.google.com/sdk/docs/quickstart-mac-os-x)

```Terminal:Terminal
$ cd $HOME
$ mkdir .go_appengine (# Cloud SDK ファイル配置用ディレクトリを任意の名前で作成)

# 上記ページからダウンロード・展開した Cloud SDK ファイルを、作成したディレクトリ (ここでは ".go_appengine" ディレクトリ) 直下に配置し、その後下記コマンドを実施

$ cd $HOME/.go_appengine (# Cloud SDK 配置用ディレクトリに移動)
$ ./google-cloud-sdk/install.sh (# Cloud SDK ツールのパスを自動で追加)
$ gcloud init (# Cloud SDK の初期化、初期化設定の詳細は上記ページを参照)
```

###5. App Engine SDK インストール
Go App Engine 拡張機能を含む gcloud コンポーネントをインストール。

(参照)
- [Go言語 パッケージ管理ツールと、ウェブアプリケーションでよく使うセット]
(http://qiita.com/m1takahashi/items/65e3a723a7c4d8b834b4)

```Terminal:Terminal
$ gcloud components install app-engine-go
$ gcloud components update (# 念のためアップデート実施)

$ cd $HOME/.go_appengine/google-cloud-sdk/platform/google_appengine
$ chmod +x goapp godoc gofmt (# "goapp", "godoc", "gofmt" の 3 ファイルに実行権限を付与)
```

###6. Google Cloud SDK および APP Engine SDK のパスを追加
```Terminal:Terminal
$ cd $HOME
$ vim ~/.bash_profile (# .bash_profile ファイル内に下記内容を記述)

  ## Set path for GoogleCloudSDK
  export GCLOUD_SDK="$HOME/.go_appengine/google-cloud-sdk"
  export PATH="$PATH:$GCLOUD_SDK/bin"

  ## Set path for App Engine SDK for GO
  export APPENGINE_SDK="$HOME/.go_appengine/google-cloud-sdk/platform/google_appengine"
  export PATH="$PATH:$APPENGINE_SDK"
```

```Terminal:Terminal
$ source ~/.bash_profile (# 設定を反映させる)
```

###7. Google Cloud Platform 管理画面でプロジェクトを作成
Google Cloud Platform ページにサインインし、管理画面からデプロイするプロジェクトを作成する。

　1) [Google Cloud Platform 管理画面 (ダッシュボード)] 
(https://console.cloud.google.com/home/dashboard) にサインイン
　2) 画面左上のハンバーガーメニュー (ツールとサービス) をクリック
　3) "IAM と管理" をクリック
　4) "すべてのプロジェクト" をクリック
　5) "+ プロジェクトを作成" をクリック
　6) "新しいプロジェクト" に任意のプロジェクト名を入力し、"作成" をクリック
　7) 新規作成されたプロジェクトに <プロジェクト ID> が割り当てられるのでメモっておく

###8. direnv のインストール
direnv はディレクトリごとに環境変数を切り替えることができるツール。direnv の詳細については下記記事を参照。

(参照)
- [改めて、direnvを使いましょう！]
(http://blog.hde.co.jp/entry/2015/02/27/182117)

```Terminal:Terminal
$ brew install direnv
$ cd $HOME
$ vim ~/.bash_profile (# .bash_profile ファイル内に下記内容を記述)

  ## Set for direnv
  eval "$(direnv hook bash)" # Bash のフックを設定
  $ export GO15VENDOREXPERIMENT=1 # v1.6 以降の Go では必要ないらしいが念のため
  $ export EDITOR=vim # 任意の使用エディタを指定

```
```Terminal:Terminal
$ source ~/.bash_profile (# 設定を反映させる)
```

###9. direnv の設定
direnv を使って、`$GOPATH` をプロジェクトのルート ディレクトリ、`$GOROOT` を App Engine SDK の `/goroot` ディレクトリにそれぞれ設定する。

(参照)
- [GoogleAppEngine + Go言語（GAE/g）で作るウェブアプリケーション 準備編]
(http://qiita.com/m1takahashi/items/06c1901c1029db1ec71d)

```Terminal:terminal
$ cd $HOME
$ mkdir mygo (# ワークペース用ディレクトリを任意の名前で作成)
$ cd mygo
$ mkdir go-project (# プロジェクトのルート ディレクトリをプロジェクト名で作成)
$ cd go-project
$ direnv edit . (# ".envrc" ファイルの作成、エディタが起動するのでファイル内に下記内容を記述)

  ## Set for direnv
  # App Engine SDK の存在を確認、無ければエラーを返す
  goapp=`which goapp 2>&1`
  if [[ ! "${?}" = "0" ]]; then
    echo "Need to install `goapp` to execute this script."
    exit 127
  fi

  APPENGINE_SDK="`dirname $goapp`/goroot"
  export GOROOT="${APPENGINE_SDK}" # GOROOT を App Engine SDK 配下の "/goroot" ディレクトリに設定
  export GOPATH="`pwd`" # GOPATH をプロジェクトのルート ディレクトリに設定
  export GOBIN="${GOPATH}/bin"
  # export PATH=${PATH}:${GOBIN} # 必要であれば $PATH を設定
```

設定が正常に完了すると下記文言が表示される。

```Terminal:
direnv: loading .envrc
direnv: export +GOBIN +GOROOT ~APPENGINE_SDK ~GOPATH
```

<参考>
direnv の設定後、下記エラーが表示された場合は `direnv allow` コマンドを実行する。

```Terminal:
direnv: error exit status 1
direnv: loading .envrc is blocked. Run `direnv allow` to approve its content.
```

```Terminal:terminal
$ direnv allow
```

###10. glide のインストール
パッケージ管理を行うため、 glide をインストール。glide の詳細については下記記事を参照。

(参照)
- [glide - パッケージ管理のお困りの方へ -]
(http://qiita.com/tienlen/items/8e192e68d6b18bec3b4a)

```Terminal:terminal
$ brew install glide
```

###11. サンプルプログラムの作成
下記記事を参考に、Go 製 Web ワークフレーム　Gin を使って "Hello, world!!" を表示するサンプルプログラム、および `app.yaml` ファイルを作成。

(参照)
- [Go+App Engine+Cloud SQLで始めるGo言語Webアプリケーション開発]
(https://developers.eure.jp/tech/go-appengine-sql-waf/)

作成したサンプルプログラムは、以下の構成でディレクトリを新規に作成し配置する。

- ディレクトリ構成

```Terminal:
go-project # プロジェクトのルート ディレクトリ (= $GOPATH)
  ├── .envrc # 環境変数管理ファイル
  ├── app
  │   ├── app.yaml # GAE Manifest 
  │   └── main.go　# 初期ロードファイル
  └── src
      └── samples # パッケージ ディレクトリ
          └── gowaf.go # パッケージ ファイル
```


- ソースファイル

```Terminal:app/main.go
package main

import (
　　"samples"
)

func init() {
　　//どちらかを選択
　　//samples.InitNetHTTP()
　　samples.InitGin()
}
```

```Terminal:src/samples/gowaf.go
package samples

import (
　　"fmt"
　　"net/http"
　　"github.com/gin-gonic/gin"
)

func InitNetHTTP() {
　　http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
　　　　fmt.Fprint(w, "Hello, world!")
　　})
}

func InitGin() {
　　r := gin.Default()
　　r.GET("/", func(c *gin.Context) {
　　　　c.String(http.StatusOK, "Hello, world!!")
　　})
　　http.Handle("/", r)
}
```

- app.yaml ファイル

```Terminal:app/app.yaml
application: <プロジェクト ID> (# 手順 7 でメモした "プロジェクト ID" を記載)
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app
```

###12. glide の実行
`glide` コマンドを実行し、必要な外部パッケージをインストール。

(参照)
 - [Goのパッケージ管理glideを初めて使う時のハマリポイント]
(http://tech.cookbiz.co.jp/engineering-1114)
 - [【Go】direnvとglideで開発環境構築してパッケージ管理]
(http://kimagureneet.hatenablog.com/entry/2016/12/12/220725)
 - [APIs and Reference]
(https://cloud.google.com/appengine/docs/standard/go/apis)

```Terminal:terminal
$ cd $HOME/mygo/go-project/src
$ glide create (# /src ディレクトリ内にあるファイルの import 文を解析し、"glide.yaml" ファイルを自動で生成)
$ glide up (# /vendor ディレクトリが自動で生成され、その中にパッケージがインストールされる)

<参考>
　# "glide get <パッケージ名>" コマンドで、"/vendor" ディレクトリ内に指定したパッケージがインストールされ、
   併せて "glide.yaml" ファイルに指定パッケージ名が追記される
```

`glide` コマンド実行後のディレクトリ構成は以下のとおり。

- ディレクトリ構成

```Terminal:
go-project 　# プロジェクトのルート ディレクトリ (= $GOPATH)
  ├── .envrc # 環境変数管理ファイル
  ├── app
  │   ├── app.yaml # GAE Manifest
  │   └── main.go　# 初期ロードファイル
  └── src
      ├── glide.lock
      ├── glide.yaml # glide
      ├── samples # パッケージ ディレクトリ
      │ 　└── gowaf.go # パッケージ ファイル
      └── vendor # 外部パッケージ用ディレクトリ
          ├── github.com
          ├── golang.org
          ├── google.golang.org
          └── gopkg.in
```

- glide.yaml ファイル

```Terminal:src/glide.yaml
package: .
import:
- package: github.com/gin-gonic/gin
  version: ~1.1.4
- package: google.golang.org/appengine
  version: ~1.0.0
```

###13. アプリケーションの起動
`goapp serve` コマンドを実行し、localhost でアプリケーションを起動。

```Terminal:terminal
$ cd $HOME/mygo/go-project/app
$ goapp serve app.yaml
```
ブラウザーを起動し、アドレス欄に "http://localhost:8080" を入力。"Hello World!!" と表示されれば成功。

###14. Google App Engine にデプロイ
`goapp deploy` コマンドを実行し、App Engine にデプロイ。

```Terminal:Terminal
$ cd $HOME/mygo/go-project/app
$ goapp deploy -application go-project .
```
ブラウザーを起動し、アドレス欄に "https://<プロジェクト ID>.appspot.com" を入力。"Hello World!!" と表示されれば成功。


<参考>
(1) 前回のデプロイが中途で終了する等して、再デプロイ時に下記エラーが表示された場合、`appcfg.py rollback` コマンドを実行後、再度デプロイを実施する。

```Terminal:
Error 409: --- begin server output ---
Another transaction by user Ogami.Togo is already in progress for app: 
b~stock-collection, version: 1. That user can undo the transaction with "appcfg rollback".
--- end server output ---
```



```Terminal:Terminal
$ cd $HOME/mygo/go-project/app
$ appcfg.py rollback . 
```

(2) 新規バージョンをデプロイしたのにも関わらず、表示が前バージョンの画面から切り替わらない場合、Google App Engine の "App Engine" > "バージョン管理" から、新規バージョンに対して "トラフィック割り当て" を 100 % 割り当てるようにする。操作手順等の詳細については、下記記事を参照。

(参照)
- [Google App Engineのバージョン管理]
(http://qiita.com/senameru/items/c0df24d3371ecf3f6e2e)

　
以上で完了です。結局、今週も環境構築で終わってしまった・・・ぐぬぬ (-_-メ)