Node.js 런타임

Node.js 런타임은 애플리케이션 코드 및 종속 항목을 설치한 후 가변형 환경에서 해당 애플리케이션을 실행하는 소프트웨어 스택입니다.

  • 18 이상 버전은 빌드팩을 사용하여 빌드되므로 app.yaml 파일에서 운영체제를 선택해야 합니다. 예를 들어 Node.js 20을 사용하려면 Ubuntu 22를 운영체제로 지정해야 합니다.

  • 16 버전 이하는 Docker를 사용하여 빌드됩니다.

  • 기본 Node.js 엔진은 최신 LTS 버전을 사용합니다.

지원되는 Node.js 버전의 전체 목록과 해당 Ubuntu 버전은 런타임 지원 일정을 참조하세요.

패키지 관리자

배포 중에 런타임은 npm, yarn 또는 Pnpm 패키지 관리자를 사용하여 종속 항목을 설치하고 애플리케이션을 시작합니다. 패키지 관리자는 다음 로직으로 설정됩니다.

  • 기본 패키지 관리자는 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 런타임 버전 1.18 이상의 경우 운영체제를 지정하려면 app.yaml 파일에 runtime_configoperating_system 설정을 포함해야 합니다.

새 런타임을 사용하려면 gcloud CLI 버전 420.0.0 이상을 설치해야 합니다. gcloud components update 명령어를 실행하여 CLI 도구를 업데이트할 수 있습니다. 설치된 버전을 보려면 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 이상의 경우 build 스크립트가 package.json에서 감지되면 런타임 환경에서 기본적으로 npm run build를 실행합니다. 애플리케이션을 시작하기 전에 빌드 단계를 추가로 제어해야 하는 경우 gcp-build 스크립트를 package.json 파일에 추가하여 커스텀 빌드 단계를 제공할 수 있습니다.

빌드가 npm run build 스크립트를 실행하지 못하도록 하려면 다음 중 하나를 수행해야 합니다.

  • 값이 비어 있는 gcp-build 스크립트를 package.json 파일에 추가합니다. "gcp-build":""
  • 값이 비어 있는 GOOGLE_NODE_RUN_SCRIPTS 빌드 환경 변수를 app.yaml 파일에 추가합니다.

    build_env_variables:
      GOOGLE_NODE_RUN_SCRIPTS: ''
    
빌드 환경 변수 지정에 대한 자세한 내용은 app.yaml 파일의 build_env_variables 섹션을 참조하세요.

애플리케이션 시작

런타임은 package.json에 지정된 명령어를 사용하는 npm start를 사용하여 애플리케이션을 시작합니다. 예:

"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로, Google Cloud Console에 표시됩니다.
NODE_ENV 앱이 배포된 경우 값은 production입니다.
PORT HTTP 요청을 수신할 포트 8080로 설정합니다.

app.yaml을 사용하여 추가 환경 변수를 설정할 수 있습니다.

메타데이터 서버

애플리케이션의 각 인스턴스에서 Compute Engine 메타데이터 서버를 사용해 호스트 이름, 외부 IP 주소, 인스턴스 ID, 커스텀 메타데이터, 서비스 계정 정보 등의 인스턴스 관련 정보를 쿼리할 수 있습니다. 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.');
});