NodeJS によるインナーループ開発

1. 概要

このラボでは、コンテナ化された環境で NodeJS アプリケーションの開発を担当するソフトウェア エンジニアの開発ワークフローを効率化するために設計された機能と機能について説明します。一般的なコンテナ開発では、コンテナの詳細とコンテナ ビルドプロセスを理解する必要があります。また、デベロッパーは通常、フローを中断して IDE から移動し、リモート環境でアプリケーションをテストしてデバッグする必要があります。このチュートリアルで説明したツールとテクノロジーを使用すると、開発者は IDE を離れることなく、コンテナ化されたアプリケーションを効率的に操作できます。

学習内容

このラボでは、GCP でコンテナを使用して開発する方法について学びます。

  • スターター Nodejs アプリケーションの作成
  • コンテナ開発用に Nodejs アプリケーションを構成する
  • シンプルな CRUD Rest サービスのコーディング
  • GKE へのデプロイ
  • エラー状態をデバッグする
  • ブレークポイント / ログの活用
  • 変更を GKE にホットデプロイする
  • 省略可: バックエンド永続性用の CloudSQL の統合

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列で、いつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud Console により一意の文字列が自動生成されます(通常は内容を意識する必要はありません)。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常、プロジェクト ID は「PROJECT_ID」の形式です)。好みの文字列でない場合は、別のランダムな ID を生成するか、独自の ID を試用して利用可能であるかどうかを確認することができます。プロジェクトの作成後、ID は「フリーズ」されます。
  • 3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud Console で課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルを終了した後に課金が発生しないようにリソースをシャットダウンするには、Codelab の最後にある「クリーンアップ」の手順を行います。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloudshell エディタを起動する

このラボは、Google Cloud Shell エディタで使用するように設計され、テストされています。エディタにアクセスするには、

  1. https://console.cloud.google.com で Google プロジェクトにアクセスします。
  2. 右上にある Cloud Shell エディタ アイコンをクリックします。

8560cc8d45e8c112.png

  1. ウィンドウの下部に新しいペインが開きます。
  2. [エディタを開く] ボタンをクリックします。

9e504cb98a6a8005.png

  1. エディタが開き、右側にエクスプローラ、中央にエディタが表示されます。
  2. 画面の下部にはターミナル ペインも表示されます
  3. ターミナルが開いていない場合は、`ctrl+`` のキーの組み合わせを使用して新しいターミナル ウィンドウを開きます。

gcloud を設定する

Cloud Shell で、プロジェクト ID と、アプリケーションのデプロイ先にするリージョンを設定します。これらの情報は、PROJECT_ID 変数と REGION 変数として保存します。

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

GKE クラスタとデータベースを設定する

  1. 設定スクリプトをダウンロードして実行可能にします。
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/nodejs/setup.sh
chmod +x setup.sh

このラボで使用するインフラストラクチャをプロビジョニングする

このラボでは、GKE にコードをデプロイし、Spanner データベースに保存されているデータにアクセスします。次の設定スクリプトは、このインフラストラクチャを準備します。

  1. setup.sh ファイルを開き、現在 CHANGEME に設定されているパスワードの値を編集します。
  2. 設定スクリプトを実行して、このラボで使用する GKE クラスタと Cloud SQL データベースを起動します。
./setup.sh
  1. Cloud Shell で、mynodejsapp という名前の新しいディレクトリを作成します。
mkdir mynodejsapp
  1. このディレクトリに移動して、ワークスペースとして開きます。これにより、新しく作成されたフォルダにワークスペース構成が作成され、エディタが再読み込みされます。
cd mynodejsapp && cloudshell workspace .
  1. NVM を使用して Node と NPM をインストールします。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
        
        # This loads nvm bash_completion
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  

nvm install stable

nvm alias default stable

3. 新しいスターター アプリケーションを作成する

  1. アプリケーションを初期化する

次のコマンドを実行して package.json ファイルを作成する

npm init
    Choose the entry point: (index.js) src/index.js and default values for the rest of the parameters. This will create the file with following contents
{
  "name": "mynodejsapp",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
  1. エントリー ポイントを追加する

このファイルを編集して、スクリプト "start": "node src/index.js", に開始コマンドを含めます。変更後のスクリプトは次のコード スニペットのようになります。

"scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  1. Express の依存関係を追加する

追加するコードでも express を使用するため、この package.json ファイルにその依存関係を追加しましょう。変更後の package.json ファイルは次のようになります。

​​{
  "name": "mynodejsapp",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Your Name",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4"
  }
}
  1. index.js ファイルを作成する

src という名前のソース ディレクトリを作成する

次のコードを使用して src/index.js を作成します。

const express = require('express');
const app = express();
const PORT = 8080;

app.get('/', (req, res) => {
    var message="Greetings from Node";
    res.send({ message: message });
  });

app.listen(PORT, () => {
  console.log(`Server running at: http://localhost:${PORT}/`);

});

PORT が値 8080 に設定されていることに注意してください。

マニフェストを生成する

Skaffold は、コンテナ開発を簡素化する統合ツールを提供します。このステップでは、skaffold を初期化します。これにより、ベースとなる Kubernetes YAML ファイルが自動的に作成されます。次のコマンドを実行して、プロセスを開始します。

ターミナルで次のコマンドを実行します。

skaffold init --generate-manifests

プロンプトが表示されたら、次の操作を行います。

  • ポートに 8080 と入力します。
  • y」と入力して構成を保存します

ワークスペースに 2 つのファイル(skaffold.yamldeployment.yaml)が追加されます。

アプリ名を更新する

構成に含まれるデフォルト値が、現在のところアプリケーションの名前と一致していません。デフォルト値ではなくアプリケーション名を参照するようにファイルを更新します。

  1. Skaffold 構成のエントリを変更する
  • skaffold.yaml を開く
  • 現在 package-json-image として設定されているイメージ名を選択します。
  • 右クリックして [すべての出現箇所を変更] を選択します。
  • 新しい名前を mynodejsapp として入力します。
  1. Kubernetes 構成のエントリを変更する
  • deployment.yaml ファイルを開く
  • 現在 package-json-image として設定されているイメージ名を選択します。
  • 右クリックして [すべての出現箇所を変更] を選択します。
  • 新しい名前を mynodejsapp として入力します。

skaffold.yaml ファイルの build セクションでは、buildpacks を使用してアプリケーションをコンテナ化しています。このコードには Dockerfile がありません。デベロッパーは、このアプリケーションをコンテナ化するために Docker に関する知識を必要としません。

また、この skaffold 構成により、エディタと実行中のコンテナ間でホット同期が自動的に有効になります。ホット同期を有効にするために、追加の構成は必要ありません。

4. 開発プロセスを理解する

このセクションでは、Cloud Code プラグインを使用していくつかの手順を行い、基本的なプロセスを学習し、スターター アプリケーションの構成と設定を検証します。

Cloud Code は skaffold と統合して、開発プロセスを効率化します。次の手順で GKE にデプロイすると、Cloud Code と Skaffold がコンテナ イメージを自動的にビルドして Container Registry に push し、アプリケーションを GKE にデプロイします。これはバックグラウンドで実行され、デベロッパー フローから詳細が抽象化されます。Cloud Code は、コンテナベースの開発に従来のデバッグ機能とホットシンク機能を提供することで、開発プロセスを強化します。

Kubernetes へのデプロイ

  1. Cloud Shell エディタの下部にあるペインで、Cloud Code  を選択します。

fdc797a769040839.png

  1. 上部に表示されるパネルで、[Kubernetes 上で実行する] を選択します。プロンプトが表示されたら、[Yes] を選択して現在の Kubernetes コンテキストを使用します。

cfce0d11ef307087.png

  1. コマンドを初めて実行すると、画面上部に現在の Kubernetes コンテキストを使用するかどうかを尋ねるプロンプトが表示されます。[Yes] を選択して、現在のコンテキストを受け入れて使用します。

817ee33b5b412ff8.png

  1. 次に、使用するコンテナ レジストリを尋ねるプロンプトが表示されます。Enter キーを押して、指定されたデフォルト値を受け入れます。

eb4469aed97a25f6.png

  1. 下部ペインの [出力] タブを選択して、進行状況と通知を表示する

f95b620569ba96c5.png

  1. 右側のチャンネル プルダウンで [Kubernetes: Run/Debug - Detailed] を選択すると、追加の詳細とコンテナからライブ ストリーミングされるログが表示されます。

94acdcdda6d2108.png

  1. プルダウンから [Kubernetes: Run/Debug] を選択して、簡略ビューに戻ります。
  2. ビルドとテストが完了すると、[出力] タブに Resource deployment/mynodejsapp status completed successfully と URL「Forwarded URL from service demo-app: http://localhost:8080」が表示されます。
  3. Cloud Code ターミナルで、出力(http://localhost:8080)の URL にカーソルを合わせ、表示されたツールチップで [Open Web Preview] を選択します。

レスポンスは次のようになります。

{"message":"Greetings from Node"}

ホットリロード

  1. src/index.js に移動します。挨拶メッセージのコードを 'Hello from Node' に編集します。

Output ウィンドウの Kubernetes: Run/Debug ビューで、更新されたファイルが Kubernetes のコンテナと同期されていることがすぐにわかります。

Update initiated
File sync started for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
File sync succeeded for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Update succeeded
  1. Kubernetes: Run/Debug - Detailed ビューに切り替えると、ファイル変更が認識され、ノードが再起動されることがわかります。
files modified: [src/index.js]
Copying files:map[src/index.js:[/workspace/src/index.js]]togcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Syncing 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Watching for changes...
[mynodejsapp]
[mynodejsapp]> mynodejsapp@1.0.0 start /workspace
[mynodejsapp]> node src/index.js
[mynodejsapp]
[mynodejsapp]Server running at: http://localhost:8080/
  1. ブラウザを更新して、更新された結果を表示します。

デバッグ

  1. デバッグビューに移動し、現在のスレッドを停止します 647213126d7a4c7b.png
  2. 下部のメニューで Cloud Code をクリックし、Debug on Kubernetes を選択して、debug モードでアプリケーションを実行します。
  • Output ウィンドウの Kubernetes Run/Debug - Detailed ビューで、skaffold がこのアプリケーションをデバッグモードでデプロイすることを確認します。
  • アプリケーションのビルドとデプロイには数分かかります。今回はデバッガがアタッチされていることがわかります。
Port forwarding pod/mynodejsapp-6bbcf847cd-vqr6v in namespace default, remote port 9229 -> http://127.0.0.1:9229
[mynodejsapp]Debugger attached.
  1. 下部のステータスバーの色が青からオレンジに変わり、デバッグモードになっていることを示します。
  2. Kubernetes Run/Debug ビューで、デバッグ可能なコンテナが起動していることを確認します。
**************URLs*****************
Forwarded URL from service mynodejsapp-service: http://localhost:8080
Debuggable container started pod/mynodejsapp-deployment-6bc7598798-xl9kj:mynodejsapp (default)
Update succeeded
***********************************

ブレークポイントを活用する

  1. src/index.js を開く
  2. var message="Greetings from Node"; と書かれたステートメントを見つけます。
  3. 行番号の左側にある余白をクリックして、その行にブレークポイントを追加します。ブレークポイントが設定されたことを示す赤いインジケーターが表示されます。
  4. ブラウザを再読み込みします。デバッガがブレークポイントでプロセスを停止し、GKE でリモートで実行されているアプリケーションの変数と状態を調べることができます。
  5. [変数] セクションまで下方向に移動し、"message" 変数を見つけます。
  6. Step Over 7cfdee4fd6ef5c3a.png を押して行を実行します。
  7. "message" 変数の現在の値が "Greetings from Node" に変更されていることを確認します。
  8. 変数名「target」をダブルクリックし、ポップアップで値を "Hello from Node" などの別の値に変更します。
  9. デバッグ コントロール パネルの [続行] ボタンをクリックします。
  10. ブラウザでレスポンスを確認します。入力した更新後の値が表示されます。
  11. 停止ボタン 647213126d7a4c7b.png を押して [デバッグ] モードを停止し、ブレークポイントをもう一度クリックしてブレークポイントを削除します。

5. シンプルな CRUD Rest サービスの開発

これで、コンテナ化された開発用にアプリケーションが完全に構成され、Cloud Code を使用した基本的な開発ワークフローを完了しました。以降のセクションでは、Google Cloud のマネージド データベースに接続する REST サービス エンドポイントを追加して、学習した内容を実践します。

依存関係を構成する

アプリケーション コードは、データベースを使用して REST サービスデータを永続化します。package.json ファイルに次を追加して、依存関係が利用可能であることを確認します。

  1. CRUD アプリケーション Postgres をビルドするには、package.json ファイルに pgsequelize の 2 つの依存関係を追加します。変更後の依存関係セクションは次のようになります。
    "dependencies": {
    "express": "^4.16.4",
    "pg": "^8.7.3",
    "sequelize": "^6.17.0"
  }

REST サービスをコーディングする

  1. このアプリケーションに CRUD アプリケーション コードを追加する
wget -O app.zip https://github.com/GoogleCloudPlatform/container-developer-workshop/raw/main/labs/nodejs/app.zip

unzip app.zip

このコードには

  • item のエンティティ モデルを含む models フォルダ
  • CRUD オペレーションを行うコードを含む controllers フォルダ
  • 特定の URL パターンをさまざまな呼び出しにルーティングする routes フォルダ
  • データベース接続の詳細を含む config フォルダ
  1. db.config.js ファイルのデータベース構成は、データベースへの接続に必要な環境変数を参照しています。また、受信リクエストの URL エンコードを解析する必要があります。
  2. src/index.js に次のコード スニペットを追加して、app.listen(PORT, () => { で始まる最後のセクションの直前に、メインの JavaScript ファイルから CRUD コードに接続できるようにします。
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(
 bodyParser.urlencoded({
   extended: true,
 })
)
const db = require("../app/models");
db.sequelize.sync();
require("../app/routes/item.routes")(app);
  1. deployment.yaml ファイルのデプロイを編集して、データベース接続情報を提供する環境変数を追加します。

ファイルの末尾にある spec エントリを次の定義と一致するように更新します。

    spec:
      containers:
      - name: mynodejsapp
        image: mynodejsapp
        env:
        - name: DB_HOST
          value: ${DB_INSTANCE_IP}        
        - name: DB_PORT
          value: "5432"  
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: username
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: database
  1. DB_HOST の値をデータベースのアドレスに置き換えます。
export DB_INSTANCE_IP=$(gcloud sql instances describe mytest-instance \
    --format=json | jq \
    --raw-output ".ipAddresses[].ipAddress")

envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml

アプリケーションのデプロイと検証

  1. Cloud Shell エディタの下部にあるペインで、Cloud Code を選択し、画面上部の Debug on Kubernetes を選択します。
  2. ビルドとテストが完了すると、[出力] タブに Resource deployment/mynodejsapp status completed successfully と URL「Forwarded URL from service mynodejsapp: http://localhost:8080」が表示されます。
  3. アイテムをいくつか追加します。

Cloud Shell ターミナルから、次のコマンドを実行します。

URL=localhost:8080
curl -X POST $URL/items -d '{"itemName":"Body Spray", "itemPrice":3.2}' -H "Content-Type: application/json"
curl -X POST $URL/items -d '{"itemName":"Nail Cutter", "itemPrice":2.5}' -H "Content-Type: application/json"
  1. ブラウザで $URL/items を実行して、GET をテストします。コマンドラインから curl を実行することもできます。
curl -X GET $URL/items
  1. テスト削除: 次に、実行してアイテムを削除してみます。必要に応じて item-id の値を変更します。
curl -X DELETE $URL/items/1
    This throws an error message
{"message":"Could not delete Item with id=[object Object]"}

問題を特定して修正する

  1. デバッグモードでアプリケーションを再起動し、問題を見つけます。次のヒントを参考にしてください。
  • DELETE が期待どおりの結果を返していないため、問題があることがわかります。したがって、itemcontroller.js->exports.delete メソッドにブレークポイントを設定します。
  • ステップ実行を行い、各ステップで変数を監視して、左側のウィンドウでローカル変数の値を確認します。
  • request.params などの特定の値を監視するには、この変数を [ウォッチ] ウィンドウに追加します。
  1. id に割り当てられた値が undefined であることに注意してください。コードを変更して問題を解決します。

修正後のコード スニペットは次のようになります。

// Delete a Item with the specified id in the request
exports.delete = (req, res) => {
    const id = req.params.id;
  1. アプリケーションが再起動したら、削除を試してもう一度テストします。
  2. デバッグ ツールバーの赤い四角形 647213126d7a4c7b.png をクリックして、デバッグ セッションを停止します。

6. クリーンアップ

おめでとうございます!このラボでは、新しい Nodejs アプリケーションをゼロから作成し、コンテナでホット デプロイ モードで動作するように構成しました。次に、従来のアプリケーション スタックと同じデベロッパー フローに従って、アプリケーションをリモート GKE クラスタにデプロイしてデバッグしました。

ラボの完了後にクリーンアップするには:

  1. ラボで使用したファイルを削除する
cd ~ && rm -rf mynodejsapp && rm -f setup.sh
  1. プロジェクトを削除して、関連するインフラストラクチャとリソースをすべて削除する