Golangで実装したAPIをCloud Runにデプロイする
やること
- Go言語で作成したAPIをCloud Runへデプロイしてみる
Cloud Run とは
Cloud Runは簡単にいうと、自分が作成したdockerコンテナをそのままGCP上で動かすことができます。 さらにはコンテナ数のスケーリングや新バージョンのローリングデプロイを自動でやってくれるなど、Cloud Runはフルマネージドなサービスです。
AWSのLambdaやGCPのCloud Functionsも似たようなサービスですが、これらは1関数をデプロイするといったものです。
また開発時はローカル環境でdockerで動かして、本番ではサーバに必要なライブラリをインストールしてようやくデプロイ、みたいなことをするケースが多いと思いますが、
Cloud Runの場合は環境変数だけ注意しておけば、ローカルと同じように本番環境もただdockerコンテナを動かすだけなので非常にデプロイが楽です。
それと、フルマネージドということで、Kubanetesなどの設定を頑張らなくてもスケーリングや負荷分散などの設定は画面をぽちぽちやるだけでできてしまいます。 最小コンテナ数、最大コンテナ数を決めることができたり1コンテナあたりの最大同時アクセス数やタイムアウト値なども設定できます。
Cloud Runはコンテナ起動時間に対して課金される課金システムです。最小コンテナ数を0に設定しておけばリクエストが全くない時間帯は課金されないように設定することも可能です(ただしコンテナ起動のオーバヘッドによるレスポンスの遅延などは気にする必要あり。)
ちなみに下記記事で紹介したDjangoで作成したWebアプリもCloud Runにデプロイして公開しています。
Cloud Runを使ってみる
Google Cloud Platformのアカウント作成
GCPのページからサインアップしてください。 初回であれば$300分のクレジットが得られたり、新規限定でさまざまなサービスを無料でできる枠もあります。 個人で触ったりする分には課金枠に達しないので問題ないと思ってます。
gcloud CLIのインストール
mac osを使用しているので下記から実施しました。
上記urlでインストーラーをダウンロード後、下記コマンドでインストール。
cd ${インストーラのあるディレクトリ} ./google-cloud-sdk/install.sh
gcloud CLIにログインする
gcloud init You must log in to continue. Would you like to log in (Y/n)? → Y # ここでYを入力する、サインアップしたemailアドレスとpasswordを聞かれると思う。 Pick cloud project to use: [1] moritomo [2] Create a new project Please enter numeric choice or text value (must exactly match list item): 1 # 新しいプロジェクトを作成する場合は "Create a new project"のものを選択 (この場合は1)
利用するGCPサービスの有効化
ここではCloud Runにデプロイに必要となるGCPのサービスをcliからアクセスできるように 各サービスのAPI利用を有効にします。
artifact registry
artifact registry
はdocker imageを管理するrepositoryとして使用します。
Cloud Runにはartifact registry上のimagewを指定してデプロイすることができます。 artifact registryを使用することでdocker imageのバージョン管理なども簡単にできます。
下記コマンドでartifact registry apiの利用を有効化します。これによりローカルでビルドしたdocker imageをartifact registryにpushすることができます。
gcloud services enable artifactregistry.googleapis.com
さっそくCloud Runにデプロイしてみる
APIの準備
今回デプロイしてみるAPIは下記記事で紹介したGolangで実装した簡単なREST APIです。
APIをCloud Run上にコンテナとして稼働させるためにはdocker imageを作成する必要があるのでDockerfile
を用意します。
FROM golang:1.17 as build # コンテナ上にアプリケーション(main.goのビルド成果物)を配置するdirectoryを作成 WORKDIR /app # ソースをコピー COPY ./ ./ RUN go mod download # 任意です # 環境によって環境変数を読み分けるための設定(.env.${_STAGE}にあたる) ARG _STAGE ENV STAGE=${_STAGE} # goファイルのビルド RUN GOOS=linux GOARCH=amd64 go build -mod=readonly -v -o server # docker コンテナの50001ポートをこのサービスのために使用 EXPOSE 50001 # apiサーバを起動 CMD GO_ENV=${STAGE} /app/server
artifact registryにrepositoryを作成
APIのdocker imageを保存しておくためのrepositoryをartifact registryに作成します。
※下記コマンドはartifact registory apiを有効化する必要があります。
REPOSITORY_NAME=go-rest-user-api # 自分のアプリの名前 LOCATION_NAME=us-west1 # artifact registryを作成したいリージョンロケーション名 gcloud artifacts repositories create ${REPOSITORY_NAME} \ --repository-format=docker --location=${LOCATION_NAME}
GCPのコンソールへアクセスする。
検索ボックスから「artifact registry」を入力すると、artifact registryのダッシュボードページへ遷移すると作成したレポジトリが確認できます。
docker build と docker push
さっそくAPIのdocker imageを作成し、artifact registryにpushします。
docker build
REPOSITORY_NAME=go-rest-user-api # 自分のアプリの名前 LOCATION_NAME=us-west1 # artifact registryを作成した時と同じリージョンロケーション名 PROJECT_NAME=moritomo # ここは自分のGCPのproject nameをセット # --build-arg _STAGE=devは筆者の場合.envを使用するためなので必要に応じて消してください。 # 引数がいらない場合: docker build --tag ${LOCATION_NAME}-docker.pkg.dev/${GCP_PROJECT_NAME}/${REPOSITORY_NAME}/images . docker build --build-arg _STAGE=dev --tag ${LOCATION_NAME}-docker.pkg.dev/${GCP_PROJECT_NAME}/${REPOSITORY_NAME}/images . # docker imageが期待通りのTAG名で生成されているか確認 docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE us-west1-docker.pkg.dev/moritomo/go-rest-user-api/images latest 5d497872ff41 8 minutes ago 1.59GB
docker push
# docker push ${タグ名}で artifact registryにpushする docker push ${LOCATION_NAME}-docker.pkg.dev/${GCP_PROJECT_NAME}/${REPOSITORY_NAME}/images
再度、GCPのコンソールへアクセスしartifact registryのダッシュボードページへ遷移可能すると、pushされていることが確認できます。
Cloud Runへデプロイ
GCPコンソールの検索ボックスから「cloud run」と入力しCloud Runのダッシュボードへアクセスします。
「サービスを作成」を押す
「既存のコンテナ イメージから 1 つのリビジョンをデプロイする」の「選択」から、artifact registryにpushしたdocker imageを選択します。
各設定は下記のようにしておけば問題ないです。 「サービス名」: 好きなサービス名(アプリと同じ名前にしてみました)
「リージョン」:好きなリージョン名
「CPU の割り当てと料金」: リクエストの処理中にのみにCPUを割り当てる
「自動スケーリング」:最小インスタンス0, 最大インスタンス数 1 (トライしてみるだけなのでここは1でいい)
「認証*」:認証が必要(念の為)
「コンテナ、変数とシークレット、接続、セキュリティ」のトグルをクリックしてportの設定をします。
Generalのコンテナポート: 50001 (これはDockerfileでEXPOSE
しているport番号を指定してください)
他の設定に関しては本番サービスでは下記は割と使うんだろうなと思います
変数とシークレット :必要であれば環境変数をセット
gRPCを利用する場合→ 接続:「http/2 エンドツーエンドを使用する」にチェック
Capacity : 1コンテナあたりのスペックを指定できます
設定し終わったら「作成」ボタンをCloud Runサービスの作成を完了する。
「作成」ボタンを押してしばらく待つと、サービスが出来上がります。
これでデプロイ完了です。
動作確認してみる。
今回はデプロイ時に認証を必要とするという設定にしました。
なのでまずはgcloudにログインしているユーザに作成サービスへのアクセス権限できるようにIAMを設定します。
先ほど作成したサービス名と自分がログインしているメールアドレスを指定して下記コマンドを実行します。
gcloud run services add-iam-policy-binding ${作成したサービス名} \ --member='user:自分のメールアドレス' \ --role='roles/run.invoker' Please specify a region: [1] asia-east1 [2] asia-east2 [3] asia-northeast1 [4] asia-northeast2 [5] asia-northeast3 [6] asia-south1 [7] asia-south2 [8] asia-southeast1 [9] asia-southeast2 [10] australia-southeast1 [11] australia-southeast2 [12] europe-central2 [13] europe-north1 [14] europe-west1 [15] europe-west2 [16] europe-west3 [17] europe-west4 [18] europe-west6 [19] northamerica-northeast1 [20] northamerica-northeast2 [21] southamerica-east1 [22] southamerica-west1 [23] us-central1 [24] us-east1 [25] us-east4 [26] us-west1 [27] us-west2 [28] us-west3 [29] us-west4 [30] cancel Please enter your numeric choice: 26 # サービスを作成したリージョンに一致する番号を入力してみた
gcloud auth print-identity-token
このトークンを使用して、作成したCloud Runサービスにもアクセスすることができるので、下記のようにGET リクエストを実施します。 Cloud RunのURLは下記のようにサービスのダッシュボードからコピーできます(赤四角の部分)。
curl -H \ "Authorization: Bearer $(gcloud auth print-identity-token)" \ https://${セキュリティのためendpointはバインドしてます}/api/users | jq . % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 667 100 667 0 0 1515 0 --:--:-- --:--:-- --:--:-- 1519 { "status": 200, "message": "ユーザ情報取得に成功しました。", "user_count": 5, "users": [ { "id": "CRe4XPnE8DjD4bjk7n2Y", "name": "testuser6", "prefecture": "神奈川県", "createdAt": "2022-01-29T13:52:38.936004Z", "updatedAt": "" }, { "id": "HAnwlDB7I7G6VVUtmkfw", "name": "testuser4", "prefecture": "", "createdAt": "", "updatedAt": "" }, { "id": "oaAwBZMSdmEC4OEbr99B", "name": "testuser5", "prefecture": "東京都", "createdAt": "2022-01-29T12:00:00Z", "updatedAt": "2022-01-29T13:50:59.146083Z" } ] }