Cloud Runで動かすGo + Echo Framework

Google Cloud Next ’19でKnativeベースの新サービス Cloud Runが発表されました。
 [https://cloud.withgoogle.com/next/sf](https://cloud.withgoogle.com/next/sf) 

Cloud Functionに代表されるGCPのServerlessは、HTTPをトリガとする場合、Go言語では`http.HandlerFunc` 型をサポートしてやる必要がありました。
これはこれで便利なのですが、じゃあ別プラットフォームで可動している何らのFramework入りGoサーバーをCloud Functionに移行しようとする際、改修は少々入ってしまうので、移行計画なんかを立ててる間にめんどくさくなってきます（えっ）
素直にGAE使えとかGKEとかあーあー聞こえない

## Knative
Knativeの詳しい説明は、下記に譲りまして
[Welcome to Knative | Knative](https://www.knative.dev/docs/)

* Kubernetesをベースとしたオートスケール
* 稼働単位がコンテナ
* インフラの設定などを極力自動化

発表された当初、その思想や考え方から、**Knativeとは、Kubernetesを中核に置いた、オートスケールするHerokuみたいなものを実現するなにか** という印象でした。発表以降、特段触ったわけでも無かったのですが、Cloud Runというマネージド・サービスがリリースされた今、ちょっくらやってみっか！と腰を上げたところです。

## Go + Echo
過去の遺産として、Echo Frameworkで作られた、ちょっとしたAPIサーバーがいくつかありますので、こちらをCloud Runでデモ稼働させることを目的にします。
環境は `Go 1.12`  +  `go modules` を利用する前提です。

今回利用したプロジェクトの全ては
 [https://github.com/onori/cloud-run-go-echo](https://github.com/onori/cloud-run-go-echo) 
に存在します。

```main.go
package main

import (
	"net/http"
	"os"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	e := echo.New()
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})

	e.GET("/:param", func(c echo.Context) error {
		param := c.Param("param")
		return c.String(http.StatusOK, param)
	})

	e.Logger.Fatal(e.Start(":" + os.Getenv("PORT")))
}
```

試しに2つのルーティングを準備してみました。`/` でHello Worldが表示されるのと `/:param` でパラメータに入力された値がそのまま表示されます。

### Docker buildと実行テスト
ローカルのDockerでビルドしてみる、下記のようなDockerfileを作成。

```Dockerfile
#build stage
FROM golang:stretch AS builder
WORKDIR /go/src/app
COPY . .
ENV GO111MODULE=on
RUN go build -o server

#final stage
FROM gcr.io/distroless/base
COPY --from=builder /go/src/app/server /app
ENV PORT=${PORT}
ENTRYPOINT [ "/app" ]
CMD [ "/server" ]
```
上記のDockerfileから、 `docker build -t cloud-run-go-echo .`  でビルド実行後、 `docker run -e PORT=1323 -p 1323:1323 --rm cloud-run-go-echo` でコンテナ化したサーバーが動くか確認します。
`localhost:1323` で `/` が実行できていれば、問題ないでしょう。


## Cloud Runの設定
GCPのコンソールから「Cloud Run」と入力し、サービスを有効化します。
![CloudRun.png](https://qiita-image-store.s3.amazonaws.com/0/19949/acca85d3-58a1-bd8f-cb23-60f3088fa4eb.png)



今回はGKEへのインストールは行いませんでした、あくまでもサービスとしてCloud Runの利用を試します。

### Google Container Registryへイメージを登録

[CloudSDK](https://cloud.google.com/sdk/downloads?hl=JA) はインストール済とした上で話を進めます。まず、今回作成したDockerイメージをContainer Registryに合わせた形でimageを新しく作り変えましょう。手順や詳細説明は、[Container Registryのクイックスタート](https://cloud.google.com/container-registry/docs/quickstart?hl=ja)がわかりやすいです。

```
docker tag cloud-run-go-echo gcr.io/[PROJECT-ID]/cloud-run-go-echo:tag1
docker push gcr.io/[PROJECT-ID]/cloud-run-go-echo:tag1
```

push後にGCRコンソールでこのようにイメージが登録されていればOKです。
![GCR.png](https://qiita-image-store.s3.amazonaws.com/0/19949/06bd5dd7-014a-c45e-6dd7-88a26298ca43.png)


### イメージをCloud Runで実行

```
gcloud beta run deploy --image gcr.io/[PROJECT-ID]/cloud-run-go-echo:tag1                                   
Please specify a region:
 [1] us-central1
 [2] cancel
Please enter your numeric choice:  1   

To make this the default region, run `gcloud config set run/region us-central1`.

Service name: (cloud-run-go-echo):  
Deploying container to Cloud Run service [cloud-run-go-echo] in project [PROJECT-ID] region [us-central1]
Allow unauthenticated invocations to new service [cloud-run-go-echo]? 
(y/N)?  y

✓ Deploying new service... Done.                                                                                                                                                                                                                             
  ✓ Creating Revision... Deploying Revision.                                                                                                                                                                                                                 
  - Routing traffic...                                                                                                                                                                                                                                       
Done.
                                                                                          
Service [cloud-run-go-echo] revision [cloud-run-go-echo-00001] has been deployed and is serving traffic at https://xxxxxxxxx.run.app
```

`gcloud beta run deploy` からコンテナイメージを指定した後、外部公開サービスの場合は、「Allow unauthenticated invocations to new service」でyを選択してください。
その後、1分くらいでエンドポイントURLが発行されました。デプロイの手軽さからして、本当に[Heroku](https://devcenter.heroku.com/articles/container-registry-and-runtime)っぽくていいです、脳死で行ける。

### 実行テスト
発行されたURLにアクセスすると、echoのハンドラで指定された`/` のプログラムが処理されてるのがわかります。
![home.png](https://qiita-image-store.s3.amazonaws.com/0/19949/5b0983ca-8126-52cc-fbc8-adf62ed7b2fa.png)


`/:param` を渡すと、
![param.png](https://qiita-image-store.s3.amazonaws.com/0/19949/d51969a3-3711-2433-6e1b-70a68f1c99d2.png)
こちらもちゃんとパラメータの値を受け取っています！ **echoでも問題なくルーティングが出来てますね！**

### コンソールの様子
![CloudRunConsole.png](https://qiita-image-store.s3.amazonaws.com/0/19949/6c7f29f1-424f-30ab-69e0-5f3eb0b3a3b2.png)
Cloud Runのコンソール画面。ログまで見れちゃってもう色々便利っすなー。使い勝手はやはり簡易的なHerokuという印象です。

USリージョンなんだけど、TTFBどんなもんかっていうと、150msくらい。
![TTFB.png](https://qiita-image-store.s3.amazonaws.com/0/19949/9b17bc48-5fa7-5fbc-bab1-2096e2db4010.png)

## Cloud Runに東京リージョンが追加されたので計測しました
TTFBは25msほど。素晴らしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19949/aeade86a-d820-dc03-e75e-9ed31c2bcb5c.png)



## 総評
* Cloud RunはKnativeベースの新しいGCPサービス
* 使い勝手はHerokuくらいカンタン
* [価格はCPU/メモリ/リクエスト数/トラフィックから算出](https://cloud.google.com/run/pricing)


個人的にはかなり好きなサービスになりそうです。あーそうそう、こういうのでいいのよ、っていう、なんですかね、このちょうど良い感。Cloud Runに関しては更に色々調べていきます。ちなみにまだベータなのでご注意を。

間違いなど有りましたらコメント欄で指摘の程よろしくお願いいたします。