ローカルで作ったDockerコンテナをCloud Run で公開するまで

[Google Cloud Platform Advent Calendar 2019](https://qiita.com/advent-calendar/2019/gcp)の６日めです。


[こちらの記事](https://qiita.com/aki36-an/items/bb25af254c5566328960)で作成したDockerコンテナをCloudRunにアップしたい！というわけでやってみました。


#必要なもの
- Google Cloud SDKの最新バージョンのインストール
- 課金が有効になっているGCPプロジェクト
- アプリの入ったDockerコンテナ（Dockerはインストールされている前提：sudoなしで実行できるように）
　（ベータ版の間は「HTTP」しか対応していないとの噂あり）

#環境
- MacOS Mojave 10.14.6
- Docker 19.03.4
- Google Cloud SDK 270.0.0


#準備
準備として必要なのは以下です。

- デプロイ対象のGCPプロジェクトの確認
- Croud Run の有効化
- 認証ヘルパーの設定
- ローカルイメージの編集
 
##デプロイ対象GCPプロジェクトの確認
UP先のプロジェクトが正しいか、gcloudの設定を確認しておきます。
間違えて違うプロジェクトにUPしちゃうと、えらいことになりますので:sweat_smile:
設定を確認するだけなら`gcloud config list`で表示できますが、
もし編集が必要な場合は`gcloud config set ~`コマンドを使って設定してください。
（もしくは`gcloud init`?ちょっと調べてくださいー）

```console
$ gcloud config list
[compute]
region = asia-northeast2
zone = asia-northeast2-b
[core]
account = sample＠test.com(アカウントのメールアドレス)
disable_usage_reporting = False
project = sandbox-a（プロジェクト名）
```

##CroudRunの有効化
GCPのメニューから、「コンピューティング」 > 「Cloud Run」を
選択すると、こんな画面になります。
「CLOUD RUN の使用を開始する」 をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/953c70fc-3ef8-82ba-53b9-08736cd416ba.png)
これで有効化はOK！

##認証ヘルパーの設定
次のコマンドを実行して、gcloudを認証ヘルパーとして使用します。
認証方法としては他にも存在するようですが、どれも~~めんどくさそう~~高度なので保留にします。
気になる方は[こちら](https://cloud.google.com/container-registry/docs/advanced-authentication?hl=ja)をどうぞ。

```
$ gcloud auth configure-docker
```

##ローカルイメージの編集
UPしようとしていたdockerイメージは[こちらの記事](https://qiita.com/aki36-an/items/bb25af254c5566328960)にて作成したものです。
開発用にホットリロードつけるため、「docker-compose」を使用しています。
ところがどっこい。CloudRunでは**docker-composeは使えない**そうです。
(知らなかった。。。）

確かによく考えたら、ホットリロードの設定だったり、
複数コンテナを同時に起動させるだったり、
役割としてはローカル用と言われればその通りかも。
公開したらファイル更新ではなくて、イメージごと更新ですものね。。。
危ない危ない。またモノシリックの罠にハマるところでしたよ。。。

というわけで、Dockerfileを修正します。
###Dockerfile
- ホットリロード用のライブラリの読み込み削除
- 実行コマンド追加（開発用ではdocker-compose.ymlに記載していた）

```Dockerfile
# 公式 golang ランタイムを親イメージとして使用
FROM golang:1.13
RUN mkdir -p /attendance/app
WORKDIR /attendance/app
COPY ./app /attendance/app
RUN go get -u github.com/labstack/echo/...
RUN go get golang.org/x/oauth2
#ホットリロード用なのでコメントアウト
#RUN go get github.com/oxequa/realize 
#実行コマンド追加
CMD ["go", "run", "/attendance/app/server.go"]　
```

###server.go
CloudRunのドキュメントには以下の通り書かれてます。
平たい話が、**起動するポートは「PORT」っていう名前の環境変数から取得してね**、
ってことでしょうか。
番外編でやるんですけど、GUIでのデプロイ時にコンテナポートを設定する項目があります。
ここの項目を使うためなのでしょうね。

それもあって、ソース内へのハードコーディングはして欲しくないようです。

>
Listening for requests on PORT
The container must listen for requests on 0.0.0.0 on the port defined by the PORT environment variable.
In Cloud Run container instances, the PORT environment variable is always set to 8080, but for portability reasons, your code should not hardcode this value.
(PORTでリクエストをリッスンする
 コンテナは、PORT環境変数で定義されたポートで0.0.0.0でリクエストをリッスンする必要があります。
 Cloud Runコンテナインスタンスでは、PORT環境変数は常に8080に設定されますが、移植性の理由から、
 コードでこの値をハードコーディングしないでください。)
>
https://cloud.google.com/run/docs/reference/container-contract?hl=ja

というわけで、以下の内容を追加しました。

```golang:server.go
import "os"  //追加

func main() {
	e := echo.New()
	routing(e) // ルーティング
  // 追加 ここから ---------------------
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
  // 追加　ここまで ---------------------

	e.Logger.Fatal(e.Start(":" + port)) //[port]部分を変数に修正
}
```

#タグつけ & Container Registry へのPUSH
上記の通り修正した者たちに、次のコマンドを実行して、pushしたいイメージに
タグ付け（命名？！）を行います。submitをつけているので、そのままPUSHまで行います。

```
$ gcloud builds submit --tag [ホスト名]/[GCPプロジェクト名]/[GCP内でのイメージ名]：[タグ]
```
ex) ` gcloud builds submit --tag asia.gcr.io/sandbox-a/hello:v1`

**(2019/12/9 追記)**
※ここで認証エラーが発生した場合は`$ gcloud auth login `を実行してみてください。
 ブラウザが起動して、使用するアカウントの認証を行います。
 （いろんなアカウントを行ったりきたりしているとちょいちょい発生します。。。）



**[ホスト名]**
　イメージのUP先のリージョンを設定します。
　居住地にかかわらず、以下の４つの中からどこでも好きなところを指定できます。

- gcr.io      ：米国（今後、場所が変更になる可能性あり）
- us.gcr.io   ：米国（ただし、gcr.ioからは独立したストレージバケット）
- eu.gcr.io   ：欧州連合
- asia.gcr.io ：アジア

**[GCPプロジェクト名]**
　先に確認したプロジェクトIDを設定します。
　※たまに、プロジェクト名とプロジェクトIDが異なる場合があるそうですが、
　　あくまで**「プロジェクトID」**を指定してください。
　プロジェクトIDに「：（コロン）」が含まれる場合は[こちら](https://cloud.google.com/container-registry/docs/overview?hl=ja#domain-scoped_projects)を参照してください。

**[GCP内でのイメージ名]：[タグ]**
　ローカルマシン上のイメージ名とは異なるイメージ名を設定する必要があるようです。
　タグは省略可能ですが、その場合、デフォルトで「latest」が設定されます。
　バージョン管理をちゃんと行う意味でも、タグは設定しておいた方がいいかも。

完了すると、こんな感じで表示されます。

```
ID                                    CREATE_TIME                DURATION  SOURCE                                                                                     IMAGES                                STATUS
exxxxxxx-fxxx-4xxx-8xxx-46xxxxxxxxxx  2019-12-02T06:36:51+00:00  1M12S     gs://sandbox-axxxxxx_cloudbuild/source/15xxxxxxxx.65-abac751c9418xxxxxxxxxxxxxxxxxxxx.tgz  asia.gcr.io/sandbox-axxxxxx/hello:v1  SUCCESS
```



GCP > [ツール] > [Container Registry]でも確認できます。
（似たような名前がいっぱいあるのは試行錯誤の末消せなくなったのです・・・）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/1b6a932b-6ff1-069e-6ad1-7bdd7367a70d.png)

※ここで設定した「asia.gcr.io〜」のタグはあとで使います。

#deploy
いよいよ、デプロイです。
こちらは、GCPの Croud Shell にて実行します。（ここをポチってするやつ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/d11118ff-e4ca-2609-eb73-826a2c9bb143.png)

以下のコマンドを実行します。
対話型で設定していきます。

```console
$ gcloud run deploy --image [さっき設定したタグ] --platform managed
ex)gcloud run deploy --image asia.gcr.io/sandbox-a/hello:v1 --platform managed

Please specify a region:
 [1] asia-northeast1
 [2] europe-west1
 [3] us-central1
 [4] us-east1
 [5] cancel
Please enter your numeric choice:  1 <--　ここでリージョンを選択

To make this the default region, run `gcloud config set run/region asia-northeast1`.

Service name (hello):　
　　--> サービス名。空白にすると、デフォルトの「hello」が採用される。上書きしたければ記載してEnter

Allow unauthenticated invocations to [hello] (y/N)?  y
　　--> [y]を選択すると、認証無しになる。アクセス認証を設定したい場合は[N]にする。

Deploying container to Cloud Run service [hello] in project [sandbox-akiando] region [asia-northeast1]
✓ Deploying new service... Done.                                   
  ✓ Creating Revision... Revision deployment finished. Waiting for health check to begin.
  ✓ Routing traffic...
  ✓ Setting IAM Policy...
Done.
Service [hello] revision [hello-00001-yiv] has been deployed 
and is serving 100 percent of traffic 
at https://hello-vlxxxxxxxx-xx.x.run.app
```
最後に表示されている`https://hello-vlxxxxxxxx-xx.x.run.app`の部分がURL。


というわけで、ブラウザでやってみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/1bef5b61-9084-92fa-0173-cb51d39776ac.png)
よしきた！
これで、デプロイまではできました。

はー、長かった！！

#まとめ
とりあえず、作ったDockerコンテナをGCPのサービスに公開することはできました。
GKEでやるつもりだったんだけど、CloudRunを触ってみたくて。。。
GKE版を試してみて、違いを見てみるのも楽しいかもですねー。

あとは認証情報の設定が残っているな。。。

（むしろデプロイの仕方よりも、性能比較の方がいい？　
　・・・それはどなたかにお任せしますw）



#番外編その１：　GUIでのデプロイ
GUIでもdeployはできます。
（Container Registryへの登録まではCUIですが）
あまり推奨はされてないという噂を聞きましたが、一応、公式のドキュメントにも
載っていたので記載してみます。

[コンピューティング] > [Cloud Run]を選択して、「サービスを作成」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/534e31e4-bbe4-b9d2-0fe3-8555eca1a2c0.png)

使用イメージは「選択」をクリックすると、リストが表示されます。
さっきpushしたイメージが表示されてますね。なんて便利。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/7135c18b-5b45-ccf0-43d5-0982d1cfb2c0.png)


で、こんな感じで入力します。
コンテナはさっき選択できたし、サービス名はわかりやすく変更しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/d4781d24-0aed-8c3e-28e6-3cba1e577349.png)
それから「オプションのリビジョン設定を表示」リンクをクリックします。
基本的にデフォルトから変更することはないと思うんだけど、ビビりなので
MAXインスタンス数だけ、デフォルトの1000から10に変えときました。
（もっと少なくてもいいけど。お金は大事）

合わせてハマりどころだったのが、ポートは今の所8080にしておいたほうがいいのかも。
ちょっとここは変更するとうまくいかなかったので今後検証します。
（噂ではベータ版は８０８０のみ対応だったみたい。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/50afce48-cd51-941f-4bac-7af7836367fc.png)

入力できたら「作成」ボタンをクリックします。
ちょっと待ちますが、こんな感じで緑のチェックがついたらOKかと。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/4f528d4a-25b9-4e87-2486-e9c7c06cddb9.png)

で、赤いところのリンクを押すと、ブラウザに表示される！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/11488da1-12eb-9c1d-5384-d21581e2f832.png)

やったね！



#番外編その２：　docker-credential-gcr
もしかしたらいらないかもだけど。
上記の手順でうまくいかなかったら、これも試してみてください。
（いろいろ試行錯誤しながらだったので、もしかしたらいるのかも。多分関係ないと思うんだけどなー）

```
 $ gcloud components install docker-credential-gcr
 $ docker-credential-gcr gcr-login
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/e8462cef-baee-e622-5fd0-19e0bb0f05b0.png)

こんな文字がブラウザに表示されたらOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/ffceedce-7e5d-5795-ea12-f142804b6494.png)





#参考
https://cloud.google.com/container-registry/docs/pushing-and-pulling?hl=ja
https://toranoana-lab.hatenablog.com/entry/2018/10/22/123848

#Special Thanks!!
となりの堤さん