Cloud Runにローカルから一発デプロイできる魔法のオプションを試してみた

## はじめに
この記事は、[「Google Cloud Platform Advent Calendar 2020」](https://qiita.com/advent-calendar/2020/gcp)の23日目の記事です。

Cloud Runの`gcloud run deploy`コマンドについ先日`--source`オプションがベータ版に追加されたということでdocを読んでると、こいつがかなり強力そうだったので試し&中身を追ってみました。

release note : https://cloud.google.com/run/docs/release-notes#December_16_2020
docs : https://cloud.google.com/sdk/gcloud/reference/beta/run/deploy#--source


## TL;DR
ローカルで`gcloud run deploy --source`一発で本当にリリースできた

`--source`オプションでやってることは実は意外とシンプル
└ ソースコードをgcsに圧縮して上げて、buildpacksを用いてランタイム検出してイメージを作成、デプロイ

## --sourceオプションとは
今まではGCRに上がっているイメージを`--image`オプションを指定することでcliでデプロイできていました。
GCRに上げるには別途コマンドを用意する必要がありました。

`--source`オプションとはこれら全てを取っ払い、**デプロイしたいローカルのパスを指定するだけでCloud Runにデプロイしてくれる**というものです。

...

そう、GCRに上げる手間が無いので、今まで秘伝のタレのごとく使い回していた`.circleci/config.yml`に記載してるCDジョブから開放されます！！

## 試してみる
さっそくやってみましょう
### サービス作成
とりあえず、Cloud Runをコンソールから作成します
![スクリーンショット 2020-12-22 21.04.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/bd6b2e6d-a814-ebe1-4c9d-7740853d3c4d.png)

コンソールから作成する際、イメージの指定が必須だったため、`gcr.io/cloudrun/hello`を使用しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/cd171221-c061-f36d-479b-03e33da2f0a5.png)
疎通確認できました

### 簡単なhttpサーバ作成
超ミニマムなhttpサーバ作成しました

``` main.go
package main

import (
	"fmt"
	"net/http"
)

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

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}
```
ただ`hello world`と出てくるだけです。
Cloud Runがデフォルトで8080開けてたのでこちらも8080開けときます。

### いざ実践

``` shell
❯ gcloud beta run deploy test --project xxx --region xxx --platform managed --source .

Building using Buildpacks and deploying container to Cloud Run service [test] in project [xxx] region [xxx]
✓ Building and deploying... Done.
  ✓ Uploading sources...
  ✓ Building Container... Logs are available at [https://console.cloud.google.com/cloud-build/builds/xxx].
  ✓ Creating Revision...
  ✓ Routing traffic...
Service [test] revision [test-00004-hos] has been deployed and is serving 100 percent of traffic.
Service URL: https://xxx.a.run.app
```

1分くらいで終了しましたw(GAE FEとは...)
早速確認してみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/c0906a4f-362b-c1b8-3bc7-38f15e8cd439.png)

わーお...
今まで一生懸命yamlを書いていた自分が馬鹿らしくなるくらい簡単にデプロイができました。
`--source` 恐るべし


## 中身を追ってみる
せっかくなので`--source`で何をやっているのか追ってみたいと思います。

改めて出力内容を確認すると

```shell

Building using Buildpacks and deploying container to Cloud Run service [test] in project [xxx] region [xxx]
✓ Building and deploying... Done.
  ✓ Uploading sources...
  ✓ Building Container... Logs are available at [https://console.cloud.google.com/cloud-build/builds/xxx].
  ✓ Creating Revision...
  ✓ Routing traffic...
Service [test] revision [test-00004-hos] has been deployed and is serving 100 percent of traffic.
Service URL: https://xxx.a.run.app
```

やっていることは以下の４つのようです

- Uploading sources
    - gcsにソースコードをアップロード
- Building Container
    - イメージの作成・GCRへアップロード
- Creating Revision
    - ↑のイメージを用いてCloud Runの新しいリビジョンの作成
- Routing traffic
    - トラフィックを新しいリビジョンに100%移行

個人的に
**「なぜdokcerfile作成してないのにgolangのイメージでビルドできてるんだろう」**
という点が謎だったので、ビルドログを見ながら詳細を追っていきたいと思います。

### Uploading sources

ビルドログの最初になにやらgcsにアップロードしてるようなログが見つかりました。

```
Fetching storage object: gs:/xxx_cloudbuild/source/yyy.tgz#zzz
Copying gs://xxx_cloudbuild/source/yyy.tgz#zzz...
/ [0 files][    0.0 B/  3.6 MiB]                                                
/ [1 files][  3.6 MiB/  3.6 MiB]                                                
Operation completed over 1 objects/3.6 MiB. 
```
GCSをみてみるとたしかにアップロードされてますね。
おそらく後述のイメージビルドのときにここのリソースを見てビルドしているんでしょう。

![スクリーンショット 2020-12-22 22.00.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/4642f02d-fb71-123e-9734-3b5ac71a6965.png)

ダウンロードしてみると、たしかにソースコードがはいってました。

![スクリーンショット 2020-12-22 22.04.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/71f99c19-708b-a1ee-4b1f-679bbeae6a18.png)


### Building Container

さらにログを追っていくと、今度は何やら`buildpacks`というイメージをダウンロードしており、そこからGoのランタイムを用いて自分のコードをビルドしてるようでした。
この`buildpacks`なるものがデプロイしたいプロジェクトのランタイムを推測してくれるものっぽいです。
[調べてみると](https://cloud.google.com/blog/ja/products/containers-kubernetes/google-cloud-now-supports-buildpacks)ビンゴ
> ビルダーにはソースコード言語を自動検出する機能が備わっています。




```
Status: Downloaded newer image for gcr.io/buildpacks/builder:latest
v1: Pulling from buildpacks/gcp/run
0630ec5cfbac: Already exists
cb52d192561b: Already exists
3c2cba919283: Already exists
a9868d531da1: Already exists
6e839ca0739c: Already exists
Digest: sha256:9eee082ca3961dc89d4b720552c62b50d90bb9ea40fb92fd7d6e5d80b0ba0f3c
Status: Downloaded newer image for gcr.io/buildpacks/gcp/run:v1
0.9.3: Pulling from buildpacksio/lifecycle
5c7fe08dec51: Pulling fs layer
bc1d6b18b4c1: Pulling fs layer
5c7fe08dec51: Verifying Checksum
5c7fe08dec51: Download complete
bc1d6b18b4c1: Verifying Checksum
bc1d6b18b4c1: Download complete
5c7fe08dec51: Pull complete
bc1d6b18b4c1: Pull complete
Digest: sha256:bc253af2edf1577717618cb3a95f0f16bb18fc9e804efbcc1b85f657d931a757
Status: Downloaded newer image for buildpacksio/lifecycle:0.9.3
===> DETECTING
[detector] 3 of 6 buildpacks participating
[detector] google.go.runtime  0.9.1
[detector] google.go.build    0.9.0
[detector] google.utils.label 0.0.1
===> ANALYZING
[analyzer] Previous image with name "gcr.io/xxx/cloud-run-source-deploy/test:yyy" not found
===> RESTORING
===> BUILDING
[builder] === Go - Runtime (google.go.runtime@0.9.1) ===
[builder] Using runtime version from go.mod: 1.13
[builder] Installing Go v1.13
```

こうして作成されたイメージはGCRにあがります。

```
[exporter] *** Images (015d5a9fefbb):
[exporter]       gcr.io/xxx/cloud-run-source-deploy/test:yyy
[exporter] Adding cache layer 'google.go.runtime:go'
Successfully built image gcr.io/xxx/cloud-run-source-deploy/test:yyy
PUSH
Pushing gcr.io/xxx/cloud-run-source-deploy/test:yyy
DONE
```

### Creating Revision, Routing traffic
こちらはimage指定する通常のdeployコマンドと一緒のフローですので割愛


## 最後に
中身を追ったことで、ブラックボックスみが深いコマンドがだいぶ咀嚼できました。

今回はソースコードがgolang & 2ファイルのみでしたが、ソースコードをgcsにあげてる仕組み上これが`node.js`とかになってくると`node_modules`もアップロードしだすと思うので速度が気になる所。

ただ、バージョン管理したいような大規模なものじゃない限りもう全部これでいいじゃん、という気分になりました。Cloud Runすごい。
この魔法、これから活用する機会が多そうです。

最後までありがとうございました。

## 参考
https://cloud.google.com/run/docs/release-notes
https://cloud.google.com/sdk/gcloud/reference/beta/run/deploy#--source
https://cloud.google.com/blog/ja/products/containers-kubernetes/google-cloud-now-supports-buildpacks