【GCP】Cloud Run 色々試した時の覚書
GCPのCloud Runを色々試した際に、メモした内容をつらつら書いています。
まずは Hello World的なことをやってみる
クイックスタート: 事前にビルドされたサンプル コンテナをデプロイする | Cloud Run のドキュメント | Google Cloud
↑のサンプルを試しにやってみたいと思います。
↑のサービスの作成を選択
↑サンプルコンテナでテストを選択
↑未認証の呼び出しを許可にチェック
作成をクリックし、Cloud Runが作成されるまで待ちます。
作成されたら表示されているURLにアクセスし、以下の様な画面が表示されればOKです ✨
ポチポチやって簡単にデプロイして試す事ができました。
デプロイ方法
次は実際に自作したコンテナイメージをCloud Run上で動かしてみたいと思います。
コンテナイメージをPushする先は現時点では以下の2種類あります。
- Container Registry
- Artifact Registry
Artifact Registry は従来の Container Registry の機能を拡張したもので、
Docker イメージに加えて、言語・OSパッケージ管理も提供している
Container Registry と違い、1つのプロジェクト内に複数のリポジトリを作成可能で、その他にも様々な拡張がされている
gcloud使ってデプロイ
今回は gcloud
CLIを使って Dockerfileを用意してコマンド実行するとおまかせでよしなにやってくれる方法を試してみたいと思います。
ディレクトリ構成
- gcp
- README.md
- docker-compose.yml
- cloudrun
- ソースファイル色々
- .gcloudignore
- deploy.sh
- etc...
docker-compose.yml
version: '3.0'
services:
gcloud:
image: google/cloud-sdk:376.0.0-alpine
volumes:
- .:/usr/src
- gcloud_config_data:/root/.config/gcloud
working_dir: /usr/src
entrypoint: bash
volumes:
gcloud_config_data:
CLIが使える環境を↑の docker-compose.yml
にて準備し、早速コンテナ起動してアカウントログインを実施してみます。
$ docker-compose run --rm gcloud
$ gcloud auth login --no-launch-browser
↑で表示されたURLにアクセスしGCPを扱うアカウントで連携、認証コードを入力してログインしておきます。
gcloud auth login | Google Cloud CLI Documentation
$ gcloud projects list # プロジェクト一覧確認
$ gcloud config set project [プロジェクト名] # 対象のプロジェクトを設定
$ gcloud config list # 設定されているか確認
↑で実際に連携されたかの確認や、デフォルトのプロジェクトなどを必要に応じて設定しておきます。
サンプルのExpressを実装する
試すものとして、Expressを用いて /hello
にアクセスするとタイムスタンプを返すような簡単なものをデプロイしてみたいと思います。
const express = require('express');
const app = express();
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Listening on port ${port}.`);
});
app.get("/hello", async (req, res) => {
const word = new Date();
res.send(`Hello, timestamp ${word.getTime()}!`);
});
↑を index.js
として保存。
FROM node:12.4.0-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN yarn install
COPY . ./
CMD [ "yarn", "start" ]
Dockerfile
を↑で用意しておく。
{
"name": "node",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.17.3"
}
}
package.json
は↑の様な設定。
#! /bin/bash
# エラーで処理中断
set -ex
# サービス名
export SERVICE_NAME=$1
gcloud run deploy \
${SERVICE_NAME}
--region asia-northeast1
--source .
今回は、デプロイ用のスクリプトとして deploy.sh
を↑の内容で作成しました。
いざ、デプロイしてみます。
bash-5.1# ./deploy.sh express-sample
+ export SERVICE_NAME=express-sample
+ SERVICE_NAME=express-sample
+ gcloud run deploy express-sample
Deploying from source. To deploy a container use [--image]. See https://cloud.google.com/run/docs/deploying-source-code for more details.
Source code location (/usr/src/cloudrun):
Deploying from source requires an Artifact Registry Docker repository to store built
containers. A repository named [cloud-run-source-deploy] in region [asia-northeast1] will
be created.
Do you want to continue (Y/n)? Y
This command is equivalent to running `gcloud builds submit --tag [IMAGE] /usr/src/cloudrun` and `gcloud run deploy express-sample --image [IMAGE]`
Allow unauthenticated invocations to [express-sample] (y/N)? y
Building using Dockerfile and deploying container to Cloud Run service [express-sample] in project [xxxxx] region [asia-northeast1]
⠹ Building and deploying new service... Building Container.
✓ Creating Container Repository...
✓ Uploading sources...
⠹ Building Container... Logs are available at [https://console.cloud.google.com/cloud-b
uild/builds/........].
. Creating Revision...
. Routing traffic...
. Setting IAM Policy...
デプロイに成功するとGCP内に関連したリソースとして
- Cloud Storage
- Code Build
- Artifact Registry
- Cloud Run
に何かしらリソースが作成されます。
今回作成されたサービスの詳細は
-
容量
-
変数とシークレット
-
接続
↑の様な設定でデプロイされていました。
削除
$ gcloud run services delete SERVICE-NAME
削除対象としては Cloud Run
のみで Cloud StorageやCode Build, Artifact Registryは残ったままでした。
Railsを動かしてみる
Cloud Run 環境での Rails の実行 | Ruby | Google Cloud
↑の記事を参考に、Cloud RunでRailsを動かして、DatabaseはCloudSQL for MySQLと連携する想定で試してみます。
環境構築
以下のVerでRailsプロジェクトを新規作成し試してみます。
- Ruby: 3.1.0
- Rails: 7.0.1
DB設定
最終的な database.yml
は以下のようになりました。(CloudSQLはパブリックIPとした場合)
staging
がCloudSQLの接続設定になります。
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
host: <%= ENV.fetch('RAILS_DB_HOST') { 'mysql' } %>
development:
<<: *default
database: xxxx_development
test:
<<: *default
database: xxxx_test
staging:
<<: *default
database: xxxx_staging
username: <%= ENV.fetch('CLOUDSQL_USERNAME') { 'root' } %>
password: <%= ENV.fetch('CLOUDSQL_PASSWORD') { '' } %>
socket: <%= ENV.fetch('CLOUDSQL_SOCKETPATH') { '' } %>
host: localhost
ログに関して
GKE上RailsのアプリケーションログをStackdriver Loggingで運用する方法 | by Riki Shimma | Medium
↑を見る限り方法は2通りありそうで、
- google-cloud-ruby gem を利用して、API経由でログを保存する
- 標準出力
単純に2. 標準出力
する方が簡単そうだったので設定を追加
config.log_formatter = ::Logger::Formatter.new
if ENV['RAILS_LOG_TO_STDOUT'].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
config.colorize_logging = false
end
プライベートIPなCloudSQL接続にSSLを使用する
Serverless VPC Accessのコネクタを通じてSSLでプライベートIPなCloud SQLに接続する場合
development:
adapter: mysql2
database: some_database
host: localhost
username: rails
password: password
sslca: /path/to/rails/root/db/cacert.pem
sslkey: /path/to/rails/root/db/test01.key
sslcert: /path/to/rails/root/db/test01.cer
↑こちらを参考に事前に証明書を作成し、参照できる場所に格納しておきます。
デプロイ用の deploy.sh
の内容を以下で作成して実行します。
#! /bin/bash
# エラーで処理中断
set -ex
# dev / stg / prod
export ENV=$1
# CloudSQL接続先
export INSTANCE_CONNECTION_NAME=$2
# MASKTER_KEY
export RAILS_MASTER_KEY=$3
# CloudSQLのパスワード
export CLOUDSQL_PASSWORD=$4
# CloudSQLのホスト
export CLOUDSQL_HOST=$5
# サービス名
export SERVICE_NAME=xxxx-${ENV}
gcloud run deploy \
${SERVICE_NAME} \
--min-instances 1 \
--add-cloudsql-instances ${INSTANCE_CONNECTION_NAME} \
--set-env-vars "RAILS_ENV=${ENV}" \
--set-env-vars "RAILS_LOG_TO_STDOUT=true" \
--set-env-vars "RAILS_MASTER_KEY=${RAILS_MASTER_KEY}" \
--set-env-vars "CLOUDSQL_USERNAME=xxxx" \
--set-env-vars "CLOUDSQL_PASSWORD=${CLOUDSQL_PASSWORD}" \
--set-env-vars "CLOUDSQL_HOST=${CLOUDSQL_HOST}" \
--vpc-connector=xxxxxx \
--region asia-northeast1 \
--source .
バッドノウハウ
Cloud Run上でRailsからCloud SQLに接続できない時は host: localhost
が必要かも
host でエラーが出ていたので、↑の設定をしたらいけました :sparkles:
GCPのPubSubトリガーで反応するサンプルをデプロイする
Pub/Sub の push によるトリガー | Cloud Run のドキュメント | Google Cloud
↑こちらを参考に試してみたいと思います。
トピックの作成
今回使用するトピック名は test-topic
として進めていきます。
CLIでトピック作成
$ gcloud pubsub topics create test-topic
#=> Created topic [projects/[プロジェクト名]/topics/test-topic].
ExpressのサンプルをPubSub用に修正する
こちらを参考に修正。
const express = require('express');
const app = express();
app.use(express.json());
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Listening on port ${port}.`);
});
app.post('/', (req, res) => {
if (!req.body) {
const msg = 'no Pub/Sub message received';
console.error(`error: ${msg}`);
res.status(400).send(`Bad Request: ${msg}`);
return;
}
if (!req.body.message) {
const msg = 'invalid Pub/Sub message format';
console.error(`error: ${msg}`);
res.status(400).send(`Bad Request: ${msg}`);
return;
}
const pubSubMessage = req.body.message;
const name = pubSubMessage.data
? Buffer.from(pubSubMessage.data, 'base64').toString().trim()
: 'NONE';
console.log(`Receive ${name}!`);
res.status(204).send();
});
module.exports = app;
早速デプロイする
$ ./deploy.sh pubsub-sample
Pub/Sub と Cloud Run の連携設定
-
プロジェクトで Pub / Sub 認証トークンを作成できるようにする
$ gcloud projects add-iam-policy-binding $PROJECT_ID \ --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com \ --role=roles/iam.serviceAccountTokenCreator
-
Cloud Run に作成したサービスを起動するためのサービスアカウントを作成
$ gcloud iam service-accounts create cloud-run-pubsub-invoker \ --display-name "Cloud Run Pub/Sub Invoker"
-
作成したサービスアカウントにサービスを起動する権限を付与
$ gcloud run services add-iam-policy-binding $SERVICE_NAME \ --member=serviceAccount:cloud-run-pubsub-invoker@${PROJECT_ID}.iam.gserviceaccount.com \ --role=roles/run.invoker
あとは、トピックを購読するサブスクリプションを、endpointをcloudrunのURLに、auth-service-account
を↑で作成したサービスアカウントを設定して作成してやればOKです。
Firestoreにアクセスする
Cloud Runの中からFirestoreにアクセスする方法を調査。
実は「Cloud Datastore ユーザー」の権限を持つサービスアカウントを関連づけてしまえばアクセスできるようになります。
実装例
@google-cloud/firestoreパッケージを追加
$ yarn add @google-cloud/firestore
パッケージ追加したらそのまま特になにもしなくても使用できます。
import { Firestore } from '@google-cloud/firestore';
const db = new Firestore();
app.get('/fs', async (req: express.Request, res: express.Response) => {
const ret = await db.doc('xxxx').get();
res.json(ret.data());
});
Discussion