Cloud Functions 실행 환경

Cloud Functions는 Google이 사용자를 대신해 인프라 및 운영체제 및 런타임 환경을 모두 처리해 주는 완전 관리형 서버리스 환경에서 실행됩니다. 각 Cloud Functions는 저마다 분리된 보안 실행 컨텍스트에서 실행되고 자동으로 확장되며 다른 함수와 독립된 수명 주기를 갖습니다.

이 문서에서는 이것이 Cloud Functions 설계 및 구현에 미치는 영향에 대해 설명합니다.

지원되는 런타임 및 런타임 업데이트

Cloud Functions는 현재 v6.14.0 버전의 Node.js 런타임을 제공합니다. 가능하면 Node의 LTS 출시 주기를 따르며 출시될 때마다 런타임의 부 버전과 패치 버전을 신속하게 업데이트합니다.

기본 이미지

Cloud Functions는 위에서 언급한 버전으로 설치된 Node.js 런타임을 제공하며 데비안 기반 실행 환경을 사용하고 gcr.io/google-appengine/nodejs Docker 이미지의 콘텐츠를 포함합니다.

FROM gcr.io/google-appengine/nodejs
RUN install_node v6.14.0

이미지에 포함된 내용을 보기 위해 해당 GitHub 프로젝트를 확인하거나 이미지 자체를 가져와서 검사할 수 있습니다. 언어 런타임(Node.js) 업데이트는 일반적으로 자동으로 이루어지며(별다른 알림이 없는 한) 기본 이미지 정의의 변경사항을 포함합니다.

동시 실행

Cloud Functions는 여러 함수 인스턴스를 실행하여 현재 부하에 맞게 함수를 확장할 수 있습니다. 이러한 인스턴스는 병렬적으로 실행되므로 둘 이상의 함수가 동시에 실행됩니다.

그러나 각 함수 인스턴스는 한 번에 하나의 동시 요청만 처리합니다. 따라서 코드가 하나의 요청을 처리하는 동안 다음 요청이 동일한 함수 인스턴스로 라우팅될 일이 없으므로 처음 요청은 사용자가 요청한 리소스(CPU 및 메모리) 전체를 사용할 수 있습니다.

동시 요청은 서로 다른 함수 인스턴스에 의해 처리되므로 변수 또는 로컬 메모리를 공유하지 않습니다. 자세한 내용은 이 문서의 뒷부분에서 설명합니다.

상태 비추적 함수

Cloud Functions는 서버리스 패러다임을 구현합니다. 즉 서버 또는 가상 머신과 같은 기반 인프라에 대한 걱정 없이 코드를 실행할 수 있습니다. Google이 함수를 자동으로 관리하고 크기를 조정하도록 하려면 함수는 상태 비추적이어야 합니다. 즉 하나의 함수 호출은 이전 호출에서 설정한 인메모리 상태에 의존해서는 안 됩니다. 그러나 종종 성능 최적화를 위해 기존 상태를 재사용할 수 있습니다. 자세한 내용은 도움말 및 유용한 정보의 권장사항을 참조하세요.

예를 들어 전역 변수와 메모리와 파일 시스템 또는 다른 상태를 공유하지 않는 다른 함수 인스턴스에서 호출을 처리할 수 있으므로 다음 함수에서 반환하는 카운터 값은 총 함수 호출 횟수와 일치하지 않습니다.

Node.js

// Global variable, but only shared within function instance.
let count = 0;

/**
 * HTTP Cloud Function that counts how many times
 * it is executed within a specific instance.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.executionCount = (req, res) => {
  count++;

  // Note: the total function invocation count across
  // all instances may not be equal to this value!
  res.send(`Instance execution count: ${count}`);
};

함수 호출 전반에서 상태를 공유해야 하는 경우 함수는 Cloud DatastoreCloud FirestoreCloud Storage와 같은 서비스를 사용하여 데이터를 유지해야 합니다. 이 페이지에는 사용 가능한 저장소 옵션의 전체 목록이 포함되어 있습니다.

완전 시작

다음 두 가지 경우 새로운 함수 인스턴스가 시작됩니다.

  • 함수를 배포하는 경우

  • 새로운 함수 인스턴스가 자동으로 생성되어 부하에 맞게 확장되거나 간혹 기존 인스턴스를 대체하는 경우

새로운 함수 인스턴스를 시작하려면 런타임(Node.js) 및 코드를 로드해야 합니다. 함수 인스턴스 시작(완전 시작)을 포함하는 요청은 기존 함수 인스턴스에 대한 요청보다 느릴 수 있습니다. 하지만 함수가 일정한 부하를 받는 경우 함수가 자주 충돌하여 함수 환경을 재시작해야 하는 것이 아니라면 완전 시작 횟수는 일반적으로 무시할 수 있습니다. 오류를 적절히 처리하여 완전 시작을 방지하는 방법을 알아보려면 오류를 참조하세요.

함수 인스턴스 수명

진행 중인 트래픽 부족으로 인해 인스턴스의 수가 줄어들거나 함수가 충돌하지 않는 한 일반적으로 함수 인스턴스를 실행하는 환경은 탄력적이며 후속 함수 호출에 의해 재사용됩니다. 즉 하나의 함수 실행이 끝나면 다른 함수 호출이 동일한 함수 인스턴스에 의해 처리될 수 있습니다. 따라서 가능하면 전역 범위의 호출에서 상태를 캐시하는 것이 좋습니다. 다음 호출이 동일한 함수 인스턴스에 도달한다는 보장이 없으므로 이 캐시 없이도 함수가 동작할 준비가 되어 있어야 합니다(상태 비추적 함수 참조).

함수 범위와 전역 범위

단일 함수 호출 시 진입점으로 선언된 함수의 본문만 실행하게 됩니다. 함수 정의를 포함하는 것으로 기대되는 함수 파일의 전역 범위는 모든 완전 시작마다 실행되지만 인스턴스가 이미 초기화된 경우에는 실행되지 않습니다.

Node.js

// Global (instance-wide) scope
// This computation runs at instance cold-start
const instanceVar = heavyComputation();

/**
 * HTTP Cloud Function that declares a variable.
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.scopeDemo = (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
};

새로운 함수 인스턴스(및 새로운 함수 인스턴스의 모든 후속 생성)에서 함수 코드가 호출되기 전에 전역 범위가 정확히 한번 실행되었다고 가정할 수 있습니다. 그러나 전역 범위 실행의 총 횟수 또는 타이밍은 Google에서 관리하는 자동 확장에 의존하므로 이를 신뢰해서는 안 됩니다.

함수 실행 타임라인

함수는 함수가 실행되는 동안에만 요청된 리소스(CPU 및 메모리)에 액세스할 수 있습니다. 실행 시간을 벗어난 코드는 실행이 보장되지 않으며 언제든지 중지될 수 있습니다. 따라서 항상 함수 실행의 끝을 정확히 알려야 하며(HTTP 함수백그라운드 함수 참조) 이를 초과하여 코드를 실행해서는 안 됩니다.

예를 들어 HTTP 요청 전송 이후에 실행된 코드는 언제든지 중단될 수 있습니다.

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to early HTTP response
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterResponse = (req, res) => {
  res.end();

  // This statement may not execute
  console.log('Function complete!');
};

실행 보장

일반적으로 함수는 수신 이벤트가 있을 때마다 한 번씩 호출됩니다. 그러나 오류 시나리오가 다양하기 때문에 Cloud Functions는 항상 단일 호출을 보장하지 않습니다.

단일 이벤트에 대한 함수의 최대 또는 최소 호출 횟수는 함수 유형에 따라 다릅니다.

  • HTTP 함수는 최대 한 번만 호출됩니다. 이는 HTTP 호출의 동기식 특성 때문이며 함수 호출을 처리하는 도중 발생하는 모든 오류는 재시도 없이 반환됩니다. HTTP 함수 호출자는 필요한 경우 오류를 처리하고 재시도합니다.

  • 백그라운드 함수는 최소 한 번 이상 호출됩니다. 이는 이벤트 처리의 비동기식 특성 때문이며, 이 경우 응답을 기다리고 오류 발생 시 재시도할 호출자가 없습니다. 발생한 이벤트는 오류 발생 시 재시도할 가능성이 있는 함수를 호출하며(함수 배포 시 요청된 경우) 다른 이유로 인해 단발적으로 함수를 중복 호출합니다(오류 발생 시 재시도가 요청되지 않은 경우에도).

실행을 재시도할 때 함수가 제대로 작동하도록 하려면 함수가 멱등성을 갖도록 구현하여 이벤트가 여러 번 전송되더라도 원하는 결과(및 부작용)를 내도록 할 수 있습니다. 즉 HTTP 함수의 경우 호출자가 HTTP 함수 엔드포인트에 호출을 다시 시도하더라도 원하는 값을 반환합니다. 함수가 멱등성을 갖도록 하는 방법에 대한 자세한 내용은 백그라운드 함수 재시도를 참조하세요.

오류

함수가 함수 유형에 따라 오류를 알리도록 하기 위한 권장 방법은 다음과 같습니다.

  • HTTP 함수는 오류를 나타내는 적절한 HTTP 상태 코드를 반환해야 합니다. 예시는 HTTP 함수를 참조하세요.

  • 백그라운드 함수는 콜백 매개변수(콜백 구문을 사용하는 경우) 또는 거부된 프라미스(프라미스 구문을 사용하는 경우)를 통해 오류 객체를 반환해야 합니다. 이에 대한 예시는 백그라운드 함수를 참조하세요.

이와 같은 방법으로 오류가 반환되면 오류를 반환한 함수 인스턴스는 정상 작동하는 것으로 라벨이 표시되며 필요한 경우 추가 요청을 처리할 수 있습니다.

코드 또는 호출한 다른 코드가 포착되지 않은 예외를 발생시키거나 현재 프로세스를 중단하면 함수 인스턴스는 다음 호출을 처리하기 전에 재시작될 수 있습니다. 이 경우 완전 시작을 더 많이 발생시켜 지연 시간이 길어질 수 있으므로 권장하지 않습니다.

Cloud Functions의 오류를 신고하는 방법에 대한 자세한 내용은 오류 신고를 참조하세요.

시간제한

함수 실행 시간은 함수 배포 시 지정된 시간제한 값을 통해 제한됩니다. 기본 제한시간은 1분이며 9분까지 늘어날 수 있습니다. 함수 실행 시 제한시간을 초과하면 오류 상태가 즉시 반환됩니다. 실행 중인 나머지 코드는 종료될 수 있습니다.

예를 들어 아래 스니펫은 함수 실행 후 2분 뒤에 실행되도록 예약한 코드를 포함합니다. 만약 제한시간을 1분으로 설정하면 해당 코드가 실행되지 않을 수 있습니다.

Node.js

/**
 * HTTP Cloud Function that may not completely
 * execute due to function execution timeout
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.afterTimeout = (req, res) => {
  setTimeout(() => {
    // May not execute if function's timeout is <2 minutes
    console.log('Function running...');
    res.end();
  }, 120000); // 2 minute delay
};

파일 시스템

함수 실행 환경은 실행 가능한 함수 파일 및 로컬 종속성과 같은 배포된 함수 패키지에 있는 파일 및 디렉토리를 포함합니다. 이러한 파일은 읽기 전용 디렉토리에서 사용할 수 있으며 이는 함수 파일의 위치에 따라 결정될 수 있습니다. 함수의 디렉토리는 현재 작업 디렉토리와 다를 수 있습니다.

다음은 함수 디렉토리에 있는 파일 목록의 예시입니다.

Node.js

const fs = require('fs');

/**
 * HTTP Cloud Function that lists files in the function directory
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.listFiles = (req, res) => {
  fs.readdir(__dirname, (err, files) => {
    if (err) {
      console.error(err);
      res.sendStatus(500);
    } else {
      console.log('Files', files);
      res.sendStatus(200);
    }
  });
};

또한 함수와 함께 배포된 다른 파일에서 코드를 로드할 수 있습니다. 예를 들어 다음 코드는 함수 디렉토리의 하위 디렉토리에서 스크립트를 로드합니다.

Node.js

const path = require('path');
const loadedModule = require(path.join(__dirname, 'loadable.js'));

/**
 * HTTP Cloud Function that runs a function loaded from another Node.js file
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.runLoadedModule = (req, res) => {
  console.log(`Loaded function from file ${loadedModule.getFileName()}`);
  res.end();
};

파일 시스템에서 쓰기가 가능한 유일한 곳은 /tmp 디렉토리로(os.tmpdir()에서 정의한 대로), 임시 파일을 함수 인스턴스에 저장하는 데 해당 디렉토리를 사용할 수 있습니다. 이는 'tmpfs' 볼륨으로 알려진 로컬 디스크 마운트 지점으로, 볼륨에 기록된 데이터가 메모리에 저장되는 곳입니다. 이는 함수를 위해 프로비저닝된 메모리 리소스를 사용합니다.

파일 시스템의 나머지 부분은 읽기 전용이며 함수에서 액세스할 수 있습니다.

네트워크

함수는 런타임 또는 타사 제공업체가 제공하는 표준 라이브러리를 사용하여 공용 인터넷에 액세스할 수 있습니다. 예를 들어 request 모듈을 사용하여 다음과 같이 HTTP 엔드포인트를 호출할 수 있습니다.

Node.js

const request = require('request');

/**
 * HTTP Cloud Function that makes an HTTP request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.makeRequest = (req, res) => {
  // The URL to send the request to
  const url = 'https://example.com';

  request(url, (err, response) => {
    if (!err && response.statusCode === 200) {
      res.sendStatus(200);
    } else {
      res.sendStatus(500);
    }
  });
};

네트워킹 최적화에 설명된 대로 함수 호출 시 네트워크 연결을 재사용하세요 그러나 2분 동안 사용하지 않은 연결은 시스템에 의해 종료될 수 있으며 이후 종료된 연결을 사용하려고 시도할 경우 '연결 재설정' 오류가 발생합니다. 코드는 종료된 연결을 제대로 처리하는 라이브러리를 사용하거나 하위 수준의 네트워킹 구조를 사용하는 경우 이를 명시적으로 처리해야 합니다.

여러 함수

배포된 각 함수는 동일한 소스 파일에서 배포된 함수를 포함하여 다른 모든 함수와 격리됩니다. 특히 메모리, 전역 변수, 파일 시스템 및 다른 상태를 공유하지 않습니다.

배포된 함수 간에 데이터를 공유하려면 Cloud DatastoreCloud FirestoreCloud Storage와 같은 저장소 서비스를 사용해야 합니다. 또는 적절한 트리거를 사용하여 하나의 함수를 다른 함수에서 호출할 수 있습니다. 예를 들어 HTTP 함수의 엔드포인트로 HTTP 요청을 전송하거나 Cloud Pub/Sub 주제에 메시지를 게시하여 Cloud Pub/Sub 함수를 트리거할 수 있습니다.

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Cloud Functions 문서