Express(Node.js)+TypeScriptな環境をCloud Runに「エイヤ!!!」でデプロイする
はじめに
毎回忘れるので、備忘録代わりに書いておきます。
この記事では、Express(Node.js)+TypeScriptなプロジェクトをCloud Runにデプロイすることを目指します。
前提条件
-
gcloud
コマンドラインツールが有効になっている環境 - Macのzshで実行(WSL2やLinux環境でも動くと思いますが他の環境でうまく行かない場合は適宜コマンドを読み替えてください)
- Node.jsおよびnpmのインストール(可能ならば最新のLTS版が望ましい)
Express(Node.js)+TypeScriptのプロジェクト作成
プロジェクトのディレクトリーを作成し、npmで初期化します(入力画面は、ほとんどEnterで問題ないです)
❯ mkdir express-ts
❯ cd express-ts
❯ npm init
必要なモジュールをnpmでインストールします。
❯ npm install typescript @types/node ts-node-dev express @types/express npm-run-all
tsconfig.jsonを作成します。
❯ npx tsc --init
お好みで、tsconfig.json
の中身を書き換えましょう
下記は一例です。
-
src
ディレクトリー配下にTypeScriptのソースコードを配置する -
dist
ディレクトリー配下にトランスコンパイルされたJavaScriptファイルを配置する
という設定になっています。その他にもTypeScriptのオプションを変更しています。
{
"compilerOptions": {
"allowJs": true,
"target": "ES2020",
"module": "commonjs",
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"strictNullChecks": true,
"alwaysStrict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
src
ディレクトリーをexpress-ts
ディレクトリー配下に作成しapp.ts
ファイルを作成します。
import express, { Application, Request, Response } from "express";
const app: Application = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/", async (req: Request, res: Response) => {
return res.status(200).send({
message: "Hello World!",
});
});
const port = process.env.PORT || 8000;
try {
app.listen(port, () => {
console.log(`Running at Port ${port}...`);
});
} catch (e) {
if (e instanceof Error) {
console.error(e.message);
}
}
package.json
のmainとscriptsを下記のように書き換えましょう
"main": "dist/app.js",
"scripts": {
"start": "node .",
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "ts-node-dev --respawn src/app.ts",
"clean": "rimraf dist",
"tsc": "tsc",
"build": "npm-run-all clean tsc"
},
下記コマンドでExpressを起動させましょう。
❯ npm run dev
> express-ts@1.0.0 dev
> ts-node-dev --respawn src/app.ts
[INFO] 12:28:44 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 4.9.4)
Running at Port 8000...
また上記のように「Running at Port 8000...」という文字列が出たら、下記URLにアクセスして、サーバーが起動することを確認してください
http://localhost:8000/
また内部でnpmのts-node-dev
パッケージを使っているため、リアルタイムでのソースコード書き換え(ホットリロード)にも対応しており、開発効率を上げることができます。
たとえば「Hello World!」という文字列を書き換えて、ブラウザーをリロードすればレスポンスの文字列が書き換わります。
Cloud Runの設定
GCPで新規プロジェクトを作成し、下記コマンドでプロジェクトIDを設定してください。
[PROJECT_ID] を手動でGCPのプロジェクトIDに置き換えてコマンドを実行します。
export PROJECT_ID=[PROJECT_ID]
Cloud Runを実行するため、GCPでプロジェクトの課金が有効になっていることを確認してください。
❯ gcloud beta billing projects describe ${PROJECT_ID} | grep billingEnabled
billingEnabled: true
gcloud コマンドで操作対象のプロジェクトを新規プロジェクトに切り替えてください。
❯ gcloud config set project ${PROJECT_ID}
Updated property [core/project].
Cloud Run の利用するリージョン、プラットフォームのデフォルト値を設定します。
gcloud config set run/region asia-northeast1
gcloud config set run/platform managed
Dockerfileの配置
プロジェクトの配下に以下の内容のDockerfileを配置してください
FROM node:lts-slim
WORKDIR /usr/src/app
ENV PORT 8080
COPY package*.json ./
RUN npm install --only=production
COPY . ./
RUN npm run build
CMD [ "npm", "start" ]
デプロイ
Cloud Runへのデプロイ方法は極めてシンプルです。
gcloud run deploy
コマンド一発でいけます。
なにか聞かれたら、内容を読んでください。
※ 今回は検証用のため、なにか聞かれたら、ほとんどEnterとyでいけると思います。
❯ gcloud run deploy --source .
Service name (express-ts):
API [artifactregistry.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)?
(y/N)? y
Enabling service [artifactregistry.googleapis.com] on project [1234567890]...
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] .` and `gcloud run deploy express-ts --image [IMAGE]`
API [run.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)? (y/N)? y
Enabling service [run.googleapis.com] on project [1234567890]...
Allow unauthenticated invocations to [express-ts] (y/N)? y
Building using Dockerfile and deploying container to Cloud Run service [express-ts] in project [express-ts-123456] region [asia-northeast1]
⠧ Building and deploying new service... Uploading sources.
⠼ Building and deploying new service... Uploading sources.
✓ Uploading sources...
. Building Container...
⠏ Building and deploying new service... Uploading sources.
⠹ Building and deploying new service... Uploading sources.
X Building and deploying new service... Uploading sources.
API [cloudbuild.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)? (y/N)?
y
Enabling service [cloudbuild.googleapis.com] on project [1234567890]...
Deployment failed
ERROR: (gcloud.run.deploy) INVALID_ARGUMENT: could not resolve source: googleapi: Error 403: 1234567890@cloudbuild.gserviceaccount.com does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist)., forbidden
一度は上記のようにエラーメッセージが表示されます。これはPermission 'storage.objects.get' denied on resource (or it may not exist)., forbidden
とある通り、権限の問題です。
API [cloudbuild.googleapis.com] not enabled on project [1234567890]. Would you like to enable and retry (this will take a few minutes)?
でyを選択してるなら、権限がONになっているはずです。
慌てず、もう一度下記コマンドを実行しましょう。
❯ gcloud run deploy --source .
Service name (express-ts):
This command is equivalent to running `gcloud builds submit --tag [IMAGE] .` and `gcloud run deploy express-ts --image [IMAGE]`
Allow unauthenticated invocations to [express-ts] (y/N)? y
Building using Dockerfile and deploying container to Cloud Run service [express-ts] in project [express-ts-123456] region [asia-northeast1]
⠶ Building and deploying new service... Deploying Revision. Waiting on revision express-ts-00001-pag.
✓ Uploading sources...
✓ Building and deploying new service... Done.
⠶ Creating Revision... Revision deployment finished. Checking container health.
. Routing traffic...
✓ Setting IAM Policy...
✓ Creating Revision... Revision deployment finished. Checking container health.
✓ Routing traffic...
Enabling service [run.googleapis.com] on project [1234567890]...
Done.
Service [express-ts] revision [express-ts-00001-pag] has been deployed and is serving 100 percent of traffic.
Service URL: https://express-ts-qwerty-an.a.run.app
発行されたService URLにアクセスし、先程のlocalhostと変わらない応答結果が返却されることを確認します。
Cloud Runの環境変数やメモリーなどを編集したい場合は下記URLのコンソールに飛び、サービス名(express-ts)を選択>「新しいリビジョンの編集とデプロイ」から設定してください。
https://console.cloud.google.com/run
お疲れさまでした。
お片付け
不要になったプロジェクトを削除し、コストがかからないようにします。
- Google Cloud のデフォルトプロジェクト設定の削除
gcloud config unset project
- プロジェクトの削除
gcloud projects delete ${PROJECT_ID}
- ハンズオン資材の削除
express-ts
ディレクトリーの削除
参考サイト
gcp-getting-started-cloudrun/tutorial.md at main · google-cloud-japan/gcp-getting-started-cloudrun
ExpressとTypeScriptの環境構築
※ Google Cloud Japan公式のリファレンスはCloud Runの内部挙動まで記述されていて特に参考になります。更に知りたいことがある場合は時間のある際、目を通すようにしましょう。
Discussion
감사합니다 !!!
ありがとうごじゃいます!!