Goで書いたサーバーをCloud Runで動かす

Goで書いたサーバーをデプロイする先を探していて、[Google App Engine(SE)](https://qiita.com/croquette0212/items/1e9df0f25f69b97d06e2)や[Arukas](https://qiita.com/croquette0212/items/b86d9f39b5c19c3e3de1)、[Heroku](https://qiita.com/croquette0212/items/2b85aa2c6b2933244f07)などを試してきたが、今回は新たにCloud Runを試したのでその手順をここに記しておく。

# 前提
* Google Cloud Platformのアカウント(請求先の登録が必要)
* Google Cloud Platformのプロジェクト作成
* Google Cloud SDKのインストール
* Dockerがインストールされていること+起動していること

この辺りの説明は省略。

# Cloud Runの有効化(初回のみ)
<img width="1315" alt="enable cloud run" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/bfa6902b-ba84-abe5-229e-d21c3379a144.png">

Cloud consoleでCloud Runのページを開き、Cloud Runを有効にしておく。フルマネージド環境で使う場合は「CLOUD RUNの使用を開始する」を選べばOK。

# 検証用サーバープログラムの準備

リクエストがくるとHello worldを返すだけのシンプルなHTTPサーバーを用意した。
Cloud Runで動かすコンテナは、環境変数`PORT`で指定されたポートでListenできるようにしておかなければいけないので、Listenするポート番号を引数で指定できるようにしている。(環境変数を直接参照しても良いが)

https://cloud.google.com/run/docs/reference/container-contract?hl=ja#port

```main.go
package main

import (
    "fmt"
    "net/http"
    "os"
    "strconv"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello world\n")
}

func main() {
    port, _ := strconv.Atoi(os.Args[1])
    fmt.Printf("Starting server at Port %d", port)
    http.HandleFunc("/", handler)
    http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}


```

動作確認のため、ローカル3000版ポート指定で起動してみる。

```sh
go run main.go 3000
```

別のShellからcurlでリクエストを送ってみて、Hello worldが返ってきたらOK。サーバーを停止する。

```sh
curl http://localhost:3000/
```

# Dockerイメージの作成

```Dockerfile:Dockerfile
FROM golang:latest as builder

ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
WORKDIR /go/src/github.com/yokoe/cloudrunexp
COPY . .
RUN go build main.go

# runtime image
FROM alpine
COPY --from=builder /go/src/github.com/yokoe/cloudrunexp /app

CMD /app/main $PORT

```

alpineをベースに、最小限のサーバーのイメージを作る。サーバー起動時の引数に環境変数`$PORT`を渡すことで、`$PORT`番号のポートでListenするようにする。

```sh
docker build -t cloudrunexp .
```

ビルドに失敗する場合はDockerが起動しているか確認する。

```sh
docker run -e "PORT=3000" -p 3000:3000 -t cloudrunexp
```

動作確認用に`$PORT`=3000で、サーバーを起動してみる。ポートフォワーディングをして、`http://localhost:3000/`でコンテナ内のサーバーにアクセスし、レスポンスが返ってくることを確認する。

```sh
curl http://localhost:3000/
```

# Container RegistryへのPush
作成したイメージをGoogle Container Registry(GCR)にPushする。

## Container Registry APIの有効化(初回のみ)
Container Registry APIを有効にしたことがない場合は、コンソールから有効にしておく。

<img width="680" alt="gcrapi.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/db773793-2d83-0c9a-d6ff-657fbe28d9b7.png">

## Docker認証ヘルパーの有効化(初回のみ)
```sh
gcloud auth login
gcloud auth configure-docker
```

gcloudで認証し、Docker認証ヘルパーを有効にすることで、`docker push`コマンドでGCRにイメージをPushできるようになる。

https://cloud.google.com/container-registry/docs/advanced-authentication?hl=ja

## タグをつける
```
docker tag cloudrunexp gcr.io/go-server-exp/cloudrunexp:firstbuild
```

アップデートしていく時に、どのイメージがどのビルドかわかるようにタグをつけておく。今回は最初なので`firstbuild`というタグをつけた。

## GCRにイメージをPushする

```
docker push gcr.io/go-server-exp/cloudrunexp:firstbuild
```

`docker push`コマンドを使って、`firstbuild`タグのついたイメージをGCRにPushする。

<img width="778" alt="スクリーンショット 2020-05-21 22.06.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/0cc5cc8f-93ca-954a-efa5-b1cbafcac49d.png">

Pushに成功すると、Cloud consoleのContainer Registry画面でPushされたイメージのリストを確認できる。

# サービスの作成
## Container Registryでイメージの確認

<img width="828" alt="スクリーンショット 2020-05-21 22.19.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/ead2d348-6904-3d99-a8c2-471cbd51c774.png">

Container Registryのイメージのメニューから「Cloud Runへデプロイ」を選ぶと、Cloud Runのサービス作成画面が開く。

## サービスの作成
<img width="1324" alt="create service" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/9adc080b-8ee5-1f20-43a9-52caf063def3.png">

自分で管理しているGKE上で動かしたいわけではないので、フルマネージドを選ぶ。また、パブリック(誰でもアクセスできる)なウェブサーバーとして動かしたいので「未認証の呼び出しを許可」を選択。

<img width="555" alt="スクリーンショット 2020-05-21 22.22.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/b8879b92-b8d1-8087-963b-d38cda19ba91.png">

コンテナイメージが、先ほどContainer Registryで選んだイメージになっていることを確認して「作成」する。

<img width="1115" alt="スクリーンショット 2020-05-21 22.23.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/3d4e9a01-3424-182b-c95c-e81bcabeadc7.png">

作成完了すると、早速利用可能となり、自動生成されたURLが表示される。アクセスしてみて、Hello worldが表示されれば成功。カスタムドメインを割り当てたい場合は、URL横のinfoボタンから、「MANAGE CUSTOM DOMAINS」に進み、マッピングを追加すれば簡単に利用できた。

なお、今回はサービスの設定、デプロイはWeb UI経由で行ったがCI経由、Terraformなどを使っても操作できる。

# 制限
リクエストを返した後の処理は停止されてしまうため、go routineを使って非同期処理を発火させる、みたいなことはできない。重い処理を実行する場合はCloud Tasksなどの処理と組み合わせる必要があり、若干手間がかかる。

# 感想
GKEでDeploymentやService、Ingressの設定をするのに比べると非常に楽だった。カスタムドメインの割当ても簡単だったし、ちょっとしたHTTPサーバーを立ち上げるのには結構良さそう。

[HerokuのDocker Deploy](https://qiita.com/croquette0212/items/2b85aa2c6b2933244f07)も同様に簡単で良かったが、GCSやCloudSQLなどGCP系のサービスと組み合わせる場合はCloud Runも良さそう。

# 参考
https://cloud.google.com/container-registry/docs/quickstart?hl=ja