GAE/Go1.9試行（その0：「クイックスタート」）

# お題
GAE(Google App Engine)を理解するために、実際に自分でアプリ作って乗せてみる。
Java版の方は仕事で使ったので少しはわかってきた。（さわりレベルだけど）いくつか記事も書いた。
今後はGolang版で試してみる。
まずは、お決まりのクイックスタートを試してみる。

# 開発環境
### # OS
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
```

### # Golang
```
$ go version
go version go1.9.7 linux/amd64
```

バージョンの切り替えは[goenv](https://github.com/syndbg/goenv)で行っている。

### # gcloud
```
$ gcloud version
Google Cloud SDK 224.0.0
```

# 前提
- [スタンダード環境](https://cloud.google.com/appengine/docs/the-appengine-environments?hl=ja)での開発とする。
- GCPプロジェクトは作成済み。
- gcloudコマンドインストール済み。

# 実践

手順は↓に従いつつ、つど、関係するファイルの中身を確認していく。
https://cloud.google.com/appengine/docs/standard/go/quickstart?hl=ja

## ■サンプルプロジェクト取得

手順通り。

```
$ go get -u -d github.com/GoogleCloudPlatform/golang-samples/appengine/helloworld/...
$
$ ll golang-samples/appengine/helloworld/
合計 16K
-rw-r--r-- 1 koge koge   69 11月 12 02:51 app.yaml
-rw-r--r-- 1 koge koge  391 11月 12 02:51 hello.go
```

### hello.go

```go
package main

import (
	"fmt"
	"net/http"

	"google.golang.org/appengine"
)

func main() {
	http.HandleFunc("/", handle)
	appengine.Main()
}

func handle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, world!")
}
```

`http`パッケージを使って、「`/`」アクセス時の挙動を定義するのはGolangでWebアプリ作成する上で基本。
通常は、そのあと、「`http.ListenAndServe(":8080", nil)`」として指定ポートで待ち受けるサーバ起動ロジックを書くのだけど、代わりに「`appengine.Main()`」と書く。
（まあ、内部でラップしてるんだろうな）

一応、確認。

```go:[google.golang.org/appengine/appengine.go]
func Main() {
	internal.Main()
}
```

```go:[google.golang.org/appengine/internal/main_vm.go]
func Main() {
	installHealthChecker(http.DefaultServeMux)

	port := "8080"
	if s := os.Getenv("PORT"); s != "" {
		port = s
	}

	host := ""
	if IsDevAppServer() {
		host = "127.0.0.1"
	}
	if err := http.ListenAndServe(host+":"+port, http.HandlerFunc(handleHTTP)); err != nil {
		log.Fatalf("http.ListenAndServe: %v", err)
	}
}

func installHealthChecker(mux *http.ServeMux) {
	// If no health check handler has been installed by this point, add a trivial one.
	const healthPath = "/_ah/health"
	hreq := &http.Request{
		Method: "GET",
		URL: &url.URL{
			Path: healthPath,
		},
	}
	if _, pat := mux.Handler(hreq); pat != healthPath {
		mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) {
			io.WriteString(w, "ok")
		})
	}
}
```

うん、やっぱり。
ヘルスチェックの口（AppEngineでは、なぜか「`/_ah/`」というパスから始まる）を追加して、（環境変数でホストとポート指定があれば採用しつつ、サーバ起動するようになってる。

### app.yaml

App Engineの挙動を決める設定ファイル。
どういう要素を定義するかは↓に記載してある。
https://cloud.google.com/appengine/docs/standard/go/config/appref?hl=ja

```yaml
runtime: go
api_version: go1

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

クイックスタート用の`app.yaml`では必要最低限の定義がしてある。

| 要素 | 説明 | 備考 |
| :--- | :--- | :--- |
| runtime | GAEアプリを動かすための実行環境を指定する。 | 必須要素 |
| api_version | `runtime`のAPIのバージョン。<br>`go1`を指定すると、アプリデプロイのたびに最新のサポートされている実行時環境が使用される（現在`go1.9`）。<br>明示的にするなら`go1.9`と書く。 | 必須要素 |
| handlers | URLパターンのリストとそれらの処理方法の説明。<br>GAEは、アプリコードの実行、ないし、静的ファイルのホスティングによって、指定のURLアクセス時の挙動を制御できる。 | 必須要素 |
| handlers/url | どのパスにアクセスした時にハンドリング処理を発動するか。 | `handlers`指定時は必須要素 |
| handlers/script | GAEアプリルートからのスクリプトのパスを指定する。<br>ただし、Goアプリの場合は常に`_go_app`にする必要がある。 | 任意要素 |

## ■ローカル起動

ローカルで起動する時は、起動スクリプトがPythonみたい。

```
$ dev_appserver.py app.yaml
INFO     2018-11-11 19:03:00,371 devappserver2.py:224] Using Cloud Datastore Emulator.
We are gradually rolling out the emulator as the default datastore implementation of dev_appserver.
　　　〜〜〜
INFO     2018-11-11 19:03:02,127 dispatcher.py:256] Starting module "default" running at: http://localhost:8080
INFO     2018-11-11 19:03:02,129 admin_server.py:152] Starting admin server at: http://localhost:8000
INFO     2018-11-11 19:03:04,904 instance.py:294] Instance PID: 11917
```

うん、起動OK。
![001.png](https://qiita-image-store.s3.amazonaws.com/0/180599/8a501a53-370d-f70a-9a2f-8c68de60264f.png)

## ■デプロイ

※当然、「`gcloud init`」や「`gcloud auth login`」は済んでいる前提。

```
$ gcloud app deploy
Services to deploy:

descriptor:      [/work/src/golang/src/github.com/GoogleCloudPlatform/golang-samples/appengine/helloworld/app.yaml]
source:          [/work/src/golang/src/github.com/GoogleCloudPlatform/golang-samples/appengine/helloworld]
target project:  [【プロジェクトID】]
target service:  [default]
target version:  [20181112t042229]
target url:      [https://【プロジェクトID】.appspot.com]


Do you want to continue (Y/n)?  y

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 87 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.                                                                                                                                                                         
Setting traffic split for service [default]...done.                                                                                                                                                        
Deployed service [default] to [https://【プロジェクトID】.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse
```

デプロイOK。
![002.png](https://qiita-image-store.s3.amazonaws.com/0/180599/d6337319-7484-c8f0-3a9c-d7362bb014ea.png)

GCPコンソール上でも、ランタイム「go」のインスタンスがデプロイされていることを確認。
![003.png](https://qiita-image-store.s3.amazonaws.com/0/180599/0b27697a-1823-fad8-2a35-58438237ae41.png)


# まとめ
GAEを使うと、用意したアプリをGCPに乗っけるまでが早い。
過去にGKE+goの組み合わせはやったことがあるけど、GAEの制約に乗っかることで、どのくらい楽になったり、融通の効かなさに嫌になったりするのか、いろいろ試してみよう。