Node.js ランタイム

Node.js ランタイムは、アプリケーションのコードと依存関係をインストールして、フレキシブル環境でそのアプリケーションを実行する役割を果たすソフトウェア スタックです。

  • バージョン 18 以降は Buildpack を使用してビルドされるため、app.yaml ファイルでオペレーティング システムを選択する必要があります。たとえば、Node.js 20 を使用するには、オペレーティング システムとして Ubuntu 22 を指定する必要があります。

  • バージョン 16 以前は Docker を使用してビルドされています。

  • デフォルトの Node.js エンジンは最新の LTS リリースを使用します。

サポートされている Node.js のバージョンと、それに対応する Ubuntu のバージョンの完全なリストについては、ランタイム サポート スケジュールをご覧ください。

パッケージ マネージャー

デプロイ時に、ランタイムは npmyarnPnpm パッケージ マネージャーのいずれかを使用して、依存関係をインストールし、アプリケーションを起動します。パッケージ マネージャーは次のロジックに基づいています。

  • デフォルトのパッケージ マネージャーは npm です。
  • yarn.lock ファイルがアプリケーションのルート ディレクトリに存在する場合は、ランタイムは代わりに yarn パッケージ マネージャーを使用します。
  • Node.js ランタイム バージョン 18 とバージョン 20 のみは、pnpm-lock.yaml ファイルがアプリケーションのルート ディレクトリに存在する場合に、ランタイムは代わりに Pnpm パッケージ マネージャーを使用します。
  • package-lock.jsonyarn.lock または pnpm-lock.yaml の両方が存在する場合、デプロイはエラーが発生して失敗します。package-lock.json ファイルが必要な場合は、app.yaml ファイルの skip_files セクションにある他のパッケージ マネージャーを指定して、使用するパッケージ マネージャーを決定する必要があります。

Node.js バージョンを選択する

新しいランタイム バージョン

Node.js ランタイム バージョン 18 以降の場合、app.yaml ファイルに runtime_configoperating_system の設定を含めて、オペレーティング システムを指定する必要があります。

新しいランタイムを使用するには、gcloud CLI バージョン 420.0.0 以降をインストールする必要があります。 CLI ツールを更新するには、gcloud components update コマンドを実行します。インストールされているバージョンを表示するには、gcloud version コマンドを実行します。

必要に応じて、次の方法でバージョンを指定します。

  • app.yaml ファイルに runtime_version 設定を追加します。runtime_version の設定が指定されていない場合は、デフォルトで最新の Node.js バージョンが使用されます。たとえば

    • Ubuntu 22 で Node.js 20 を指定するには、次のようにします。
      runtime: nodejs
      env: flex
    
      runtime_config:
          operating_system: "ubuntu22"
          runtime_version: "20"
    
    • Ubuntu 22 でサポートされている最新の Node.js バージョンを指定するには、次のようにします。
      runtime: nodejs
      env: flex
    
      runtime_config:
          operating_system: "ubuntu22"
    
  • engines フィールドを使用して、アプリケーションの package.json ファイルに任意の Node.js バージョンを含める。engines フィールドを使用してバージョンを指定する場合、runtime_version 設定が優先されます。予期しない破損を回避するために、engines フィールドに Node.js のバージョンを指定することをおすすめします。たとえば

      {
        "engines": {
          "node": "20.x"
        }
      }
    

    engines.node プロパティには semver 範囲を指定できます。これを指定すると、ランタイムによって、semver 範囲に一致する最新バージョンの Node.js がダウンロードおよびインストールされます。一致するものが見つからない場合は、アプリケーションのデプロイが失敗し、ランタイムからエラー メッセージが返されます。

以前のランタイム バージョン

Node.js ランタイム バージョン 16 以前の場合、engines フィールドを使用して、アプリケーションの package.json ファイルでバージョンを指定します。

次の例では、最新の Node 9 リリースを使用するようにランタイムを構成しています。

{
  "engines": {
    "node": "9.x"
  }
}

engines.node プロパティには semver 範囲を指定できます。これを指定すると、ランタイムによって、semver 範囲に一致する最新バージョンの Node.js がダウンロードおよびインストールされます。一致するものが見つからない場合は、アプリケーションのデプロイが失敗し、ランタイムからエラー メッセージが返されます。

パッケージ マネージャー バージョン

ランタイム イメージでは、最新の yarn リリースと、最新の Node.js LTS リリースで利用可能な npm のリリースを使用することを目指しています。

engines フィールドを使用することによって、アプリケーションの package.json ファイルで別のパッケージ マネージャー バージョンを指定できます。この場合、ランタイムは engines フィールドで指定したバージョンのパッケージ マネージャーをデプロイに使用します。

yarnnpm の両方のバージョンを指定した場合は、デプロイに使用されるパッケージ マネージャーのみが必要に応じて更新されます。アプリケーションのデプロイに実際に使用されていないカスタム バージョンのパッケージ マネージャーについては、デプロイ時間を短縮するためにインストールから除外されます。

次の例では、カスタム バージョンの npm を使用するようにランタイムを構成しています。

{
  "engines": {
    "npm": "5.x"
  }
}

次の例では、カスタム バージョンの yarn を使用するようにランタイムを構成しています。

{
  "engines": {
    "yarn": ">=1.0.0 <2.0.0"
  }
}

engines.npmengines.yarn のいずれのプロパティにも semver 範囲を指定できます。

依存関係

デプロイ時に、ランタイムは npm または yarn パッケージ マネージャーを使用し、npm install または yarn install を実行することで依存関係をインストールします。使用されるパッケージ マネージャーがランタイムでどのように選択されるかの詳細については、パッケージ マネージャーのセクションをご覧ください。

また、Google App Engine での Node.js パッケージの管理の詳細については、Node.js ライブラリの使用をご覧ください。

ネイティブ機能拡張を必要とする Node.js パッケージを使用できるように、次の Ubuntu パッケージDocker イメージにプリインストールされています。

  • build-essential
  • ca-certificates
  • curl
  • git
  • imagemagick
  • libkrb5-dev
  • netbase
  • python

アプリケーションで追加のオペレーティング システムレベルの依存関係が必要な場合は、このランタイムに基づいてカスタム ランタイムを使用し、適切なパッケージをインストールする必要があります。

NPM ビルド スクリプト

Node.js ランタイム バージョン 18 以降の場合、デフォルトでは package.jsonbuild スクリプトが検出されると、ランタイム環境で npm run build が実行されます。アプリケーションの起動前にビルドステップをさらに制御する必要がある場合は、gcp-build スクリプトを package.json ファイルに追加して、カスタム ビルドステップを指定できます。

ビルドで npm run build スクリプトが実行されないようにするには、以下のいずれかを行う必要があります。

  • package.json ファイルに空の値を持つ gcp-build スクリプトを追加します: "gcp-build":""
  • app.yaml ファイルに空の値を持つ GOOGLE_NODE_RUN_SCRIPTS ビルド環境変数を追加します。

    build_env_variables:
      GOOGLE_NODE_RUN_SCRIPTS: ''
    
ビルド環境変数の指定の詳細については、app.yaml ファイルの build_env_variables セクションをご覧ください。

アプリケーションの起動

ランタイムは npm start を使用してアプリケーションを起動します。その際、package.json に指定されているコマンドが使用されます。次に例を示します。

"scripts": {
  "start": "node app.js"
}

起動スクリプトはウェブサーバーを起動し、それが PORT 環境変数で指定されたポート(一般的には 8080)で HTTP リクエストに応答します。

ランタイムの拡張

カスタム ランタイムを使用すると、App Engine フレキシブル環境で動作する Node.js アプリに機能を追加できます。カスタム ランタイムを構成するには、app.yaml ファイルの次の行を置き換えます。

runtime: nodejs

これを次のように変更します。

runtime: custom

また、app.yaml ファイルのある同じディレクトリに Dockerfile ファイルと .dockerignore ファイルを追加する必要があります。

カスタム ランタイムに Dockerfile を定義する方法については、カスタム ランタイムのドキュメントをご覧ください。

HTTPS プロキシと転送プロキシ

App Engine はロードバランサで HTTPS 接続を終了し、リクエストをアプリケーションに転送します。アプリケーションによっては、元のリクエスト IP とプロトコルを判断する必要があります。ユーザーの IP アドレスは、標準の X-Forwarded-For ヘッダーで確認できます。この情報を必要とするアプリケーションでは、プロキシを信頼するようにウェブ フレームワークを構成する必要があります。

Express.js では、次の trust proxy 設定を使用します。

app.set('trust proxy', true);

HTTPS 接続を強制する方法については、リクエストの処理方法をご覧ください。

環境変数

次の環境変数が、ランタイム環境によって設定されます。

環境変数 説明
GAE_INSTANCE 現在のインスタンスの名前。
GAE_MEMORY_MB アプリケーション プロセスで使用可能なメモリ量。
GAE_SERVICE アプリケーションの app.yaml ファイルで指定されたサービス名。サービス名が指定されていない場合は、default に設定されます。
GAE_VERSION 現在のアプリケーションのバージョン ラベル。
GOOGLE_CLOUD_PROJECT アプリケーションに関連付けられたプロジェクト ID。この ID は、Google Cloud Console に表示されます。
NODE_ENV アプリがデプロイされている場合、値は production です。
PORT HTTP リクエストを受信するポート。8080 に設定します。

app.yaml で、追加の構成変数を設定できます。

メタデータ サーバー

アプリケーションのインスタンスは、ホスト名、外部 IP アドレス、インスタンス ID、カスタム メタデータ、サービス アカウント情報など、インスタンスに関する情報を Compute Engine メタデータ サーバーから取得します。App Engine では、インスタンスごとにカスタム メタデータを設定することはできませんが、プロジェクト単位のカスタム メタデータを設定して、App Engine インスタンスや Compute Engine インスタンスから読み取ることができます。

次のサンプル関数では、メタデータ サーバーを使用して、Node.js ランタイム バージョン 16 以前とバージョン 18 以降のインスタンスの外部 IP アドレスを取得します。新しいバージョンを使用するには app.yaml を更新する必要があることに留意してください。新しいバージョンの使用の詳細については、Node.js ランタイムをご覧ください。

const express = require('express');
const fetch = require('node-fetch');

const app = express();
app.enable('trust proxy');

const METADATA_NETWORK_INTERFACE_URL =
  'http://metadata/computeMetadata/v1/' +
  '/instance/network-interfaces/0/access-configs/0/external-ip';

const getExternalIp = async () => {
  const options = {
    headers: {
      'Metadata-Flavor': 'Google',
    },
    json: true,
  };

  try {
    const response = await fetch(METADATA_NETWORK_INTERFACE_URL, options);
    const ip = await response.json();
    return ip;
  } catch (err) {
    console.log('Error while talking to metadata server, assuming localhost');
    return 'localhost';
  }
};

app.get('/', async (req, res, next) => {
  try {
    const externalIp = await getExternalIp();
    res.status(200).send(`External IP: ${externalIp}`).end();
  } catch (err) {
    next(err);
  }
});

const PORT = parseInt(process.env.PORT) || 8080;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});