Cloud Functions 실행 환경

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

런타임

Cloud Functions에서 지원하는 언어 런타임은 다음과 같습니다. 명령줄에서 또는 Terraform을 통해 함수를 배포하는 경우 런타임 ID 값이 필요합니다.

런타임 기본 이미지 런타임 ID
Node.js 16(권장) Ubuntu 18.04 nodejs16
Node.js 14 Ubuntu 18.04 nodejs14
Node.js 12 Ubuntu 18.04 nodejs12
Node.js 10 Ubuntu 18.04 nodejs10
Node.js 8(지원 중단됨) Ubuntu 18.04 nodejs8
Node.js 6(지원 중단됨) Debian 8 nodejs6
Python 3.10(미리보기) Ubuntu 22.04 python310
Python 3.9(권장) Ubuntu 18.04 python39
Python 3.8 Ubuntu 18.04 python38
Python 3.7 Ubuntu 18.04 python37
Go 1.16(권장) Ubuntu 18.04 go116
Go 1.13 Ubuntu 18.04 go113
Go 1.11 Ubuntu 18.04 go111
자바 17(미리보기) Ubuntu 22.04 java17
자바 11(권장) Ubuntu 18.04 java11
.NET Core 3.1(권장) Ubuntu 18.04 dotnet3
Ruby 3.0(권장) Ubuntu 18.04 ruby30
Ruby 2.7 Ubuntu 18.04 ruby27
Ruby 2.6 Ubuntu 18.04 ruby26
PHP 8.1(미리보기) Ubuntu 18.04 php81
PHP 7.4(권장) Ubuntu 18.04 php74

달리 명시되지 않는 한 함수를 배포할 때 함수 및 배포 패치가 런타임 및 종속 항목에 적용됩니다. 여기에는 특정 언어의 커뮤니티에서 만든 업데이트와 패치가 포함되며, 안정성을 위해 일정 기간 테스트한 후에 제공됩니다. 마찬가지로 Cloud Functions는 운영체제나 포함된 패키지와 같은 실행 환경의 다른 구성 요소를 업데이트할 수 있습니다. 이러한 업데이트는 함수를 안전하게 유지하는 데 유용합니다.

스테이트리스(Stateless) 함수

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

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

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}`);
};

Python

# Global variable, modified within the function by using the global keyword.
count = 0

def statelessness(request):
    """
    HTTP Cloud Function that counts how many times it is executed
    within a specific instance.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    global count
    count += 1

    # Note: the total function invocation count across
    # all instances may not be equal to this value!
    return 'Instance execution count: {}'.format(count)

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
)

// count is a global variable, but only shared within a function instance.
var count = 0

// ExecutionCount is an HTTP Cloud Function that counts how many times it
// is executed within a specific instance.
func ExecutionCount(w http.ResponseWriter, r *http.Request) {
	count++

	// Note: the total function invocation count across
	// all instances may not be equal to this value!
	fmt.Fprintf(w, "Instance execution count: %d", count)
}

자바


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutionCount implements HttpFunction {

  private final AtomicInteger count = new AtomicInteger(0);

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    count.getAndIncrement();

    // Note: the total function invocation count across
    // all instances may not be equal to this value!
    BufferedWriter writer = response.getWriter();
    writer.write("Instance execution count: " + count);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ExecutionCount
{
    public class Function : IHttpFunction
    {
        // Note that this variable must be static, because a new instance is
        // created for each request. An alternative approach would be to use
        // dependency injection with a singleton resource injected into the function
        // constructor.
        private static int _count;

        public async Task HandleAsync(HttpContext context)
        {
            // Note: the total function invocation count across
            // all servers may not be equal to this value!
            int currentCount = Interlocked.Increment(ref _count);
            await context.Response.WriteAsync($"Server execution count: {currentCount}");
        }
    }
}

Ruby

require "functions_framework"

# Global variable, but scoped only within one function instance.
$count = 0

FunctionsFramework.http "execution_count" do |_request|
  $count += 1

  # NOTE: the total function invocation count across all instances
  # may not be equal to this value!
  "Instance execution count: #{$count}"
end

PHP

PHP 함수는 요청 간에 상태(예: 변수 값)를 보존할 수 없습니다.

함수 호출 전반에서 상태를 공유해야 하는 경우 함수는 Datastore, Firestore 또는 Cloud Storage와 같은 서비스를 사용하여 데이터를 유지해야 합니다. 사용 가능한 스토리지 옵션의 전체 목록은 스토리지 옵션 선택을 참조하세요.

자동 확장 및 동시 실행

Cloud Functions는 수신받은 요청을 함수의 인스턴스에 할당하여 처리합니다. 요청의 양과 기존 함수 인스턴스의 개수에 따라 Cloud Functions는 기존 인스턴스에 요청을 할당하거나 새 인스턴스를 만들 수 있습니다.

함수의 각 인스턴스는 한 번에 하나의 동시 요청만을 처리합니다. 즉, 코드가 요청을 처리하는 동안에는 다른 요청이 동일한 인스턴스로 라우팅되지 않습니다. 따라서 원래 요청이 사용자가 요청한 전체 리소스(CPU 및 메모리)를 사용할 수 있습니다.

인바운드 요청의 양이 기존 인스턴스의 개수를 초과하는 경우 Cloud Functions는 여러 개의 새로운 인스턴스를 시작하여 요청을 처리할 수 있습니다. 이러한 자동 확장을 통해 Cloud Functions는 함수의 다른 인스턴스를 사용하여 여러 요청을 동시에 처리할 수 있습니다.

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

자동 확장 동작 제어

Cloud Functions를 사용하면 원하는 시점에 공존할 수 있는 함수 인스턴스의 총 개수 제한을 설정할 수 있습니다. 무제한으로 확장하는 것이 바람직하지 않은 경우도 있습니다. 예를 들어 함수가 Cloud Functions와 같은 수준으로 확장할 수 없는 데이터베이스 등의 리소스에 의존하는 경우가 있습니다. 요청의 양이 급증하면 Cloud Functions에서 데이터베이스가 감당할 수 있는 양보다 많은 함수 인스턴스를 생성할 수 있습니다.

콜드 스타트

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

  • 함수를 배포하는 경우

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

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

함수가 지연 시간에 민감한 경우 최소 인스턴스 한도를 설정하여 콜드 스타트를 방지하세요.

함수 인스턴스 수명

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

함수 범위와 전역 범위 비교

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

Node.js

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

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res 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}`);
};

Python

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return 'Instance: {}; function: {}'.format(instance_var, function_var)

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

자바


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

public class Scopes implements HttpFunction {
  // Global (instance-wide) scope
  // This computation runs at instance cold-start.
  // Warning: Class variables used in functions code must be thread-safe.
  private static final int INSTANCE_VAR = heavyComputation();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Per-function scope
    // This computation runs every time this function is called
    int functionVar = lightComputation();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Instance: %s; function: %s", INSTANCE_VAR, functionVar);
  }

  private static int lightComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).sum();
  }

  private static int heavyComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).reduce((t, x) -> t * x).getAsInt();
  }
}

Ruby

# Global (instance-wide) scope.
# This block runs on cold start, before any function is invoked.
#
# Note: It is usually best to run global initialization in an on_startup block
# instead at the top level of the Ruby file. This is because top-level code
# could be executed to verify the function during deployment, whereas an
# on_startup block is run only when an actual function instance is starting up.
FunctionsFramework.on_startup do
  instance_data = perform_heavy_computation

  # To pass data into function invocations, the best practice is to set a
  # key-value pair using the Ruby Function Framework's built-in "set_global"
  # method. Functions can call the "global" method to retrieve the data by key.
  # (You can also use Ruby global variables or "toplevel" local variables, but
  # they can make it difficult to isolate global data for testing.)
  set_global :my_instance_data, instance_data
end

FunctionsFramework.http "tips_scopes" do |_request|
  # Per-function scope.
  # This method is called every time this function is called.
  invocation_data = perform_light_computation

  # Retrieve the data computed by the on_startup block.
  instance_data = global :my_instance_data

  "instance: #{instance_data}; function: #{invocation_data}"
end

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

함수 실행 타임라인

함수는 함수가 실행되는 동안에만 요청된 리소스(CPU 및 메모리)에 액세스할 수 있습니다. 실행 기간이 아닌 시점에 실행되는 코드는 실행이 보장되지 않으며 언제든지 중단될 수 있습니다. 따라서 항상 함수 실행의 끝을 정확히 알리고 그 이후에는 코드를 실행하지 않아야 합니다. 자세한 내용은 HTTP 함수, 백그라운드 함수, CloudEvent 함수를 참조하세요.

예를 들어 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 함수를 참조하세요.

  • 이벤트 기반 함수는 오류 메시지를 로깅하고 반환해야 합니다. 이에 대한 예시는 백그라운드 함수CloudEvent 함수를 참조하세요.

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

코드 또는 호출한 다른 코드에서 포착되지 않은 예외가 발생하거나 현재 프로세스가 비정상적으로 종료되면 함수 인스턴스는 다음 호출을 처리하기 전에 재시작될 수 있습니다. 이 경우 콜드 스타트가 더 많이 발생되어 지연 시간이 길어질 수 있으므로 권장하지 않습니다.

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

시간 제한

함수 실행 시간은 함수 배포 시 지정할 수 있는 시간 제한 기간을 통해 제한됩니다. 기본적으로 함수는 1분 후에 시간이 초과되지만 이 기간을 최대 9분까지 연장할 수 있습니다.

함수 실행 시 제한시간이 초과되면 오류 상태가 즉시 호출자로 반환됩니다. 시간이 초과된 함수 인스턴스에서 사용하는 CPU 리소스가 제한되고 요청 처리가 즉시 일시중지될 수 있습니다. 일시중지된 작업은 후속 요청에서 진행될 수도 있고 진행되지 않을 수도 있으므로 예기치 않은 부작용이 발생할 수 있습니다.

아래 스니펫에는 함수 실행 시작 후 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
};

Python

def timeout(request):
    print('Function running...')
    time.sleep(120)

    # May not execute if function's timeout is <2 minutes
    print('Function completed!')
    return 'Function completed!'

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// Timeout sleeps for 2 minutes and may time out before finishing.
func Timeout(w http.ResponseWriter, r *http.Request) {
	log.Println("Function execution started...")
	time.Sleep(2 * time.Minute)
	log.Println("Function completed!")
	fmt.Fprintln(w, "Function completed!")
}

자바


package functions;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class AfterTimeout implements HttpFunction {
  private static final Logger logger = Logger.getLogger(AfterTimeout.class.getName());

  // Simple function to return "Hello World"
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    logger.info("Function running...");
    TimeUnit.MINUTES.sleep(2);

    // May not execute if function's timeout is <2 minutes
    logger.info("Function completed!");
    BufferedWriter writer = response.getWriter();
    writer.write("Function completed!");
  }
}

경우에 따라 위 코드가 예기치 않은 방식으로 성공적으로 실행될 수 있습니다. 함수가 시간 초과되는 경우를 가정해 보세요. CPU 제한을 통해 요청을 처리하는 인스턴스가 일시중지됩니다. 대기 중인 작업이 일시중지됩니다. 후속 요청이 동일한 인스턴스로 라우팅되면 작업이 재개되고 Function running...이 로그에 표시됩니다.

이러한 동작의 일반적인 증상은 한 요청의 작업과 로그가 후속 요청으로 '유출'되는 것으로 보이는 것입니다. 일시중지된 작업이 반드시 재개되는 것은 아니기 때문에 이러한 동작에 의존해서는 안 됩니다. 대신 다음 기법의 조합을 사용하여 함수의 시간 초과를 방지할 수 있습니다.

  1. 예상하는 함수 실행 시간보다 제한 시간을 길게 설정합니다.
  2. 실행 중 남은 시간을 추적하고 정리/종료를 일찍 수행합니다.

Google Cloud CLI를 사용하여 함수의 최대 실행 시간을 설정하려면 배포 시 --timeout 플래그를 사용하세요.

gcloud functions deploy FUNCTION_NAME --timeout=TIMEOUT FLAGS...

위의 명령어에서 FLAGS...는 함수 배포 중에 전달하는 다른 옵션을 나타냅니다. deploy 명령어의 전체 참조는 gcloud functions deploy를 확인하세요.

다음과 같이 Cloud Console에서 함수를 생성하는 동안 제한 시간을 설정할 수도 있습니다.

  1. Cloud Console의 Cloud Functions 개요 페이지로 이동합니다.

  2. 함수 만들기를 클릭합니다.

  3. 함수의 필수 필드를 작성합니다.

  4. 더보기를 클릭하여 고급 설정을 확인합니다.

  5. 제한 시간 필드에 값을 입력합니다.

메모리

Google Cloud CLI를 사용하여 함수의 메모리를 설정하려면 MB 수로 --memory 플래그를 사용하세요. 예를 들면 다음과 같습니다.

gcloud functions deploy FUNCTION_NAME --memory=MEMORY

기본적으로 각 함수에 할당된 메모리는 256MB입니다.

파일 시스템

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

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

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);
    }
  });
};

Python

def list_files(request):
    import os
    from os import path

    root = path.dirname(path.abspath(__file__))
    children = os.listdir(root)
    files = [c for c in children if path.isfile(path.join(root, c))]
    return 'Files: {}'.format(files)

Go


// Package tips contains tips for writing Cloud Functions in Go.
package tips

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// ListFiles lists the files in the current directory.
// Uses directory "serverless_function_source_code" as defined in the Go
// Functions Framework Buildpack.
// See https://github.com/GoogleCloudPlatform/buildpacks/blob/56eaad4dfe6c7bd0ecc4a175de030d2cfab9ae1c/cmd/go/functions_framework/main.go#L38.
func ListFiles(w http.ResponseWriter, r *http.Request) {
	files, err := ioutil.ReadDir("./serverless_function_source_code")
	if err != nil {
		http.Error(w, "Unable to read files", http.StatusInternalServerError)
		log.Printf("ioutil.ListFiles: %v", err)
		return
	}
	fmt.Fprintln(w, "Files:")
	for _, f := range files {
		fmt.Fprintf(w, "\t%v\n", f.Name())
	}
}

자바


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

public class FileSystem implements HttpFunction {

  // Lists the files in the current directory.
  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    File currentDirectory = new File(".");
    File[] files = currentDirectory.listFiles();
    PrintWriter writer = new PrintWriter(response.getWriter());
    writer.println("Files:");
    for (File f : files) {
      writer.printf("\t%s%n", f.getName());
    }
  }
}

C#

using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Threading.Tasks;

namespace FileSystem
{
    public class Function : IHttpFunction
    {
        public async Task HandleAsync(HttpContext context)
        {
            string[] files = Directory.GetFiles(".");

            await context.Response.WriteAsync("Files:\n");
            foreach (string file in files)
            {
                await context.Response.WriteAsync($"\t{file}\n");
            }
        }
    }
}

Ruby

require "functions_framework"

FunctionsFramework.http "concepts_filesystem" do |_request|
  files = Dir.entries "."
  "Files: #{files.join "\n"}"
end

PHP


use Psr\Http\Message\ServerRequestInterface;

function listFiles(ServerRequestInterface $request): string
{
    $contents = scandir(__DIR__);

    $output = 'Files:' . PHP_EOL;

    foreach ($contents as $file) {
        $output .= "\t" . $file . PHP_EOL;
    }

    return $output;
}

또한 함수와 함께 배포된 다른 파일에서 코드를 로드할 수 있습니다.

파일 시스템 자체는 완전히 쓰기 가능하며(기본 OS에서 사용하는 파일 제외) Cloud Functions 인스턴스의 메모리 내에 저장됩니다.

네트워크

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

Node.js

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

/**
 * HTTP Cloud Function that makes an HTTP request
 *
 * @param {Object} req Cloud Function request context.
 * @param {Object} res Cloud Function response context.
 */
exports.makeRequest = async (req, res) => {
  const url = 'https://example.com'; // URL to send the request to
  const externalRes = await fetch(url);
  res.sendStatus(externalRes.ok ? 200 : 500);
};

Python

def make_request(request):
    """
    HTTP Cloud Function that makes another HTTP request.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    import requests

    # The URL to send the request to
    url = 'http://example.com'

    # Process the request
    response = requests.get(url)
    response.raise_for_status()
    return 'Success!'

Go


// Package http provides a set of HTTP Cloud Functions samples.
package http

import (
	"fmt"
	"net/http"
	"time"
)

var urlString = "https://example.com"

// client is used to make HTTP requests with a 10 second timeout.
// http.Clients should be reused instead of created as needed.
var client = &http.Client{
	Timeout: 10 * time.Second,
}

// MakeRequest is an example of making an HTTP request. MakeRequest uses a
// single http.Client for all requests to take advantage of connection
// pooling and caching. See https://godoc.org/net/http#Client.
func MakeRequest(w http.ResponseWriter, r *http.Request) {
	resp, err := client.Get(urlString)
	if err != nil {
		http.Error(w, "Error making request", http.StatusInternalServerError)
		return
	}
	if resp.StatusCode != http.StatusOK {
		msg := fmt.Sprintf("Bad StatusCode: %d", resp.StatusCode)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "ok")
}

자바


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;

public class SendHttpRequest implements HttpFunction {

  // Create a client with some reasonable defaults. This client can be reused for multiple requests.
  // (java.net.httpClient also pools connections automatically by default.)
  private static HttpClient client =
      HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException, InterruptedException {
    // Create a GET sendHttpRequest to "http://example.com"
    String url = "http://example.com";
    var getRequest = java.net.http.HttpRequest.newBuilder().uri(URI.create(url)).GET().build();

    // Send the sendHttpRequest using the client
    var getResponse = client.send(getRequest, BodyHandlers.ofString());

    // Write the results to the output:
    var writer = new PrintWriter(response.getWriter());
    writer.printf("Received code '%s' from url '%s'.", getResponse.statusCode(), url);
  }
}

C#

using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;

namespace SendHttpRequest
{
    // Dependency injection configuration, executed during server startup.
    public class Startup : FunctionsStartup
    {
        public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
        {
            // Make an HttpClient available to our function via dependency injection.
            // There are many options here; see
            // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
            // for more details.
            services.AddHttpClient<IHttpFunction, Function>();
        }
    }

    // Function, decorated with the FunctionsStartup attribute to specify the startup class
    // for dependency injection.
    [FunctionsStartup(typeof(Startup))]
    public class Function : IHttpFunction
    {
        private readonly HttpClient _httpClient;

        public Function(HttpClient httpClient) =>
            _httpClient = httpClient;

        public async Task HandleAsync(HttpContext context)
        {
            string url = "http://example.com";
            using (HttpResponseMessage clientResponse = await _httpClient.GetAsync(url))
            {
                await context.Response.WriteAsync($"Received code '{(int) clientResponse.StatusCode}' from URL '{url}'.");
            }
        }
    }
}

Ruby

require "functions_framework"
require "net/http"

FunctionsFramework.http "concepts_requests" do |_request|
  url = "example.com"
  response = Net::HTTP.get_response url, "/"
  "Received code: #{response.code} from url: #{url}"
end

PHP


use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;

function makeRequest(ServerRequestInterface $request): ResponseInterface
{
    // This sample uses the GuzzleHTTP client
    // See its documentation for usage information
    // https://docs.guzzlephp.org/en/stable/

    // Specify the URL to send requests to
    $client = new Client([
        'base_uri' => 'https://example.com',
    ]);

    // Send the request
    $url_response = $client->get('/');

    $function_response = new Response(
        $url_response->getStatusCode(),
        [], // headers
        ''  // body
    );

    return $function_response;
}

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

여러 함수

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

배포된 함수 간에 데이터를 공유하기 위해 Datastore, Firestore 또는 Cloud Storage와 같은 스토리지 서비스를 사용할 수 있습니다. 또는 적절한 트리거를 사용하여 한 함수에서 다른 함수를 호출할 수 있습니다. 예를 들어 HTTP 함수의 엔드포인트에 HTTP를 요청하거나 Pub/Sub 주제에 메시지를 게시하여 Pub/Sub 함수를 트리거합니다.