Cloud Run을 사용하여 동적 콘텐츠 제공 및 마이크로서비스 호스팅

Cloud RunFirebase Hosting와 페어링하여 마이크로서비스로 REST API를 빌드할 수 있습니다

Cloud Run를 사용하면 다음 작업을 할 수 있습니다. 컨테이너 이미지로 패키징된 애플리케이션 배포하기 그런 다음 Firebase Hosting를 사용하는 경우 HTTPS 요청을 전달하여 컨테이너화된 앱을 빌드할 수 있습니다

  • Cloud Run 지원 여러 언어 (Go, Node.js, Python, Java 포함)를 사용하면 원하는 프로그래밍 언어와 프레임워크를 사용할 수 있습니다
  • Cloud Run 자동 및 수평 확장 수신된 요청을 처리하도록 컨테이너 이미지를 처리한 다음 줄어들 수 있습니다
  • 요청 처리 도중 소비한 CPU, 메모리, 네트워킹에 대해서만 비용을 지불하면 됩니다.

예: 통합된 Cloud Run의 사용 사례 및 샘플 Firebase Hosting님, 서버리스 개요


이 가이드에서는 다음을 수행하는 방법을 설명합니다.

  1. 간단한 Hello World 애플리케이션 작성
  2. 앱을 컨테이너화하여 Container Registry에 업로드
  3. 컨테이너 이미지를 Cloud Run에 배포
  4. 컨테이너화된 앱으로 Hosting 요청 전달

제공하는 동적 콘텐츠의 성능을 높이기 위해 필요에 따라 캐시 설정을 조정할 수 있습니다.

시작하기 전에

Cloud Run를 사용하기 전에 몇 가지 초기 작업을 완료해야 합니다. 여기에는 Cloud Billing 계정 설정, Cloud Run 사용 설정 등이 포함됩니다. API, gcloud 명령줄 도구 설치

프로젝트에 대한 결제 설정

Cloud Run무료 사용 할당량을 제공합니다. 하지만 노트북에서 계정 Cloud Billing 사용하거나 Cloud Run을 사용해 보세요.

API 사용 설정 및 SDK 설치

  1. Google API 콘솔에서 Cloud Run API를 사용 설정합니다.

    1. Google 검색 앱 홈 화면의 오른쪽 상단에 있는 Cloud Run API 페이지 API 콘솔에서 확인할 수 있습니다

    2. 메시지가 나타나면 Firebase 프로젝트를 선택합니다.

    3. Cloud Run API 페이지에서 사용 설정을 클릭합니다.

  2. Cloud SDK를 설치하고 초기화합니다.

  3. gcloud 도구가 올바른 프로젝트에 구성되었는지 확인합니다.

    gcloud config list

1단계: 샘플 애플리케이션 작성

Cloud Run다양한 다른 언어 아래 샘플에 나와 있는 언어 외에 다른 언어도 사용할 수 있습니다.

Go

  1. helloworld-go라는 새 디렉터리를 만든 후 이 위치로 디렉터리를 변경합니다.

    mkdir helloworld-go
    cd helloworld-go
  2. helloworld.go라는 새 파일을 만든 후 다음 코드를 추가합니다.

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("helloworld: received a request")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
    	log.Print("helloworld: starting server...")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("helloworld: listening on port %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

    이 코드는 PORT 환경 변수로 정의한 포트를 리슨하는 기본 웹 서버를 생성합니다.

앱이 완성되었으며 컨테이너화되어 Container Registry

Node.js

  1. helloworld-nodejs라는 새 디렉터리를 만든 후 이 위치로 디렉터리를 변경합니다.

    mkdir helloworld-nodejs
    cd helloworld-nodejs
  2. 다음 콘텐츠로 package.json이라는 파일을 만듭니다.

    {
      "name": "knative-serving-helloworld",
      "version": "1.0.0",
      "description": "Simple hello world sample in Node",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {
        "express": "^4.20.0"
      }
    }
    
  3. index.js라는 새 파일을 만든 후 다음 코드를 추가합니다.

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('Hello world received a request.');
    
      const target = process.env.TARGET || 'World';
      res.send(`Hello ${target}!\n`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });
    

    이 코드는 PORT 환경 변수로 정의한 포트를 리슨하는 기본 웹 서버를 생성합니다.

앱이 완성되었으며 컨테이너화되어 Container Registry

Python

  1. helloworld-python라는 새 디렉터리를 만든 후 이 위치로 디렉터리를 변경합니다.

    mkdir helloworld-python
    cd helloworld-python
  2. app.py라는 새 파일을 만든 후 다음 코드를 추가합니다.

    import os
    
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        target = os.environ.get('TARGET', 'World')
        return 'Hello {}!\n'.format(target)
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
    

    이 코드는 PORT 환경 변수로 정의한 포트를 리슨하는 기본 웹 서버를 생성합니다.

앱이 완성되었으며 컨테이너화되어 Container Registry

자바

  1. 자바 SE 8 이상 JDKCURL을 설치합니다.

    다음 단계에서 새 웹 프로젝트를 만들려면 이 작업만 수행하면 됩니다. 나중에 설명하게 될 Dockerfile은 컨테이너에 모든 종속 항목을 로드합니다.

  2. 콘솔에서 cURL과 unzip 명령어를 차례로 사용하여 새로운 빈 웹 프로젝트를 만듭니다.

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d name=helloworld \
        -d artifactId=helloworld \
        -o helloworld.zip
    unzip helloworld.zip

    이렇게 하면 SpringBoot 프로젝트가 만들어집니다.

  3. / 매핑을 처리하기 위해 @RestController를 추가하여 src/main/java/com/example/helloworld/HelloworldApplication.java에서 SpringBootApplication 클래스를 업데이트하고 @Value 필드도 추가하여 TARGET 환경 변수를 제공합니다.

    package com.example.helloworld;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    public class HelloworldApplication {
    
      @Value("${TARGET:World}")
      String target;
    
      @RestController
      class HelloworldController {
        @GetMapping("/")
        String hello() {
          return "Hello " + target + "!";
        }
      }
    
      public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
      }
    }
    

    이 코드는 PORT 환경 변수로 정의한 포트를 리슨하는 기본 웹 서버를 생성합니다.

앱이 완성되었으며 컨테이너화되어 Container Registry

2단계: 앱을 컨테이너화하여 Container Registry에 업로드

  1. 소스 파일과 동일한 디렉터리에 Dockerfile이라는 새 파일을 만들어 샘플 앱을 컨테이너화합니다. 다음 콘텐츠를 파일에 복사합니다.

    Go

    # Use the official Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    FROM golang:latest as builder
    
    ARG TARGETOS
    ARG TARGETARCH
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Retrieve application dependencies using go modules.
    # Allows container builds to reuse downloaded dependencies.
    COPY go.* ./
    RUN go mod download
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build the binary.
    # -mod=readonly ensures immutable go.mod and go.sum in container builds.
    RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -mod=readonly -v -o server
    
    # Use the official Alpine image for a lean production container.
    # https://hub.docker.com/_/alpine
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM alpine:3
    RUN apk add --no-cache ca-certificates
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /app/server /server
    
    # Run the web service on container startup.
    CMD ["/server"]
    

    Node.js

    # Use the official lightweight Node.js 12 image.
    # https://hub.docker.com/_/node
    FROM node:12-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install production dependencies.
    RUN npm install --only=production
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    # Use the official lightweight Python image.
    # https://hub.docker.com/_/python
    FROM python:3.7-slim
    
    # Allow statements and log messages to immediately appear in the Knative logs
    ENV PYTHONUNBUFFERED True
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Run the web service on container startup. Here we use the gunicorn
    # webserver, with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app
    

    자바

    # Use the official maven/Java 8 image to create a build artifact: https://hub.docker.com/_/maven
    FROM maven:3.5-jdk-8-alpine as builder
    
    # Copy local code to the container image.
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    
    # Build a release artifact.
    RUN mvn package -DskipTests
    
    # Use the Official OpenJDK image for a lean production stage of our multi-stage build.
    # https://hub.docker.com/_/openjdk
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM openjdk:8-jre-alpine
    
    # Copy the jar to the production image from the builder stage.
    COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar
    
    # Run the web service on container startup.
    CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/helloworld.jar"]
    

  2. 다음을 실행하여 Cloud Build로 컨테이너 이미지를 빌드합니다. 명령어를 실행합니다.

    gcloud builds submit --tag gcr.io/PROJECT_ID/helloworld

    성공하면 다음 이미지 이름이 포함된 성공 메시지가 표시됩니다.
    (gcr.io/PROJECT_ID/helloworld)

이제 컨테이너 이미지가 Container Registry에 저장되며 다음 경우에 재사용될 수 있습니다. 있습니다.

Cloud Build 대신 로컬에 설치된 버전을 사용할 수 있습니다. Docker 이미지를 컨테이너를 로컬에서 빌드합니다.

3단계: 컨테이너 이미지를 Cloud Run에 배포

  1. 다음 명령어를 사용하여 배포합니다.

    gcloud run deploy --image gcr.io/PROJECT_ID/helloworld

  2. 메시지가 표시되면 다음 단계를 수행합니다.

최상의 성능을 위해 다음 리전을 사용하여 Cloud Run 서비스를 Hosting과 같은 위치에 배치합니다.

  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1

Hosting에서 Cloud Run에 대한 재작성은 다음에서 지원됩니다. 다음 리전:

  • asia-east1
  • asia-east2
  • asia-northeast1
  • asia-northeast2
  • asia-northeast3
  • asia-south1
  • asia-south2
  • asia-southeast1
  • asia-southeast2
  • australia-southeast1
  • australia-southeast2
  • europe-central2
  • europe-north1
  • europe-southwest1
  • europe-west1
  • europe-west12
  • europe-west2
  • europe-west3
  • europe-west4
  • europe-west6
  • europe-west8
  • europe-west9
  • me-central1
  • me-west1
  • northamerica-northeast1
  • northamerica-northeast2
  • southamerica-east1
  • southamerica-west1
  • us-central1
  • us-east1
  • us-east4
  • us-east5
  • us-south1
  • us-west1
  • us-west2
  • us-west3
  • us-west4
  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1
  1. 배포가 완료될 때까지 잠시 기다립니다. 성공하면 명령줄에 서비스 URL이 표시됩니다. 예를 들면 다음과 같습니다. https://helloworld-RANDOM_HASH-us-central1.a.run.app

  2. 웹브라우저에서 서비스 URL을 열고 배포한 컨테이너로 이동합니다.

다음 단계에서는 Google Cloud 콘솔에서 이 컨테이너화된 앱에 액세스하는 방법을 Firebase Hosting URL을 넣으면 캠페인에 대한 동적 콘텐츠를 생성할 수 있습니다. Firebase에서 호스팅하는 사이트입니다.

4단계: 컨테이너화된 앱으로 호스팅 요청 전달

재작성 규칙을 사용하면 특정 패턴과 일치하는 요청을 단일 대상으로 전달할 수 있습니다.

다음 예는 페이지의 모든 요청을 전달하는 방법을 보여줍니다. Hosting 사이트의 /helloworld을(를) 통해 다음 서비스의 시작 및 실행을 트리거합니다. helloworld 컨테이너 인스턴스에 사용할 수 있습니다

  1. 다음 사항을 확인하세요.

    CLI 설치 및 초기화에 대한 자세한 안내 Hosting, 다음을 참고하세요. Hosting 시작 가이드

  2. firebase.json 파일을 엽니다.

  3. hosting 섹션 아래에 다음 rewrite 구성을 추가합니다.

    "hosting": {
      // ...
    
      // Add the "rewrites" attribute within "hosting"
      "rewrites": [ {
        "source": "/helloworld",
        "run": {
          "serviceId": "helloworld",  // "service name" (from when you deployed the container image)
          "region": "us-central1",    // optional (if omitted, default is us-central1)
          "pinTag": true              // optional (see note below)
        }
      } ]
    }
  4. 프로젝트 디렉터리의 루트에서 다음 명령어를 실행하여 호스팅 구성을 사이트에 배포합니다.

    firebase deploy --only hosting

이제 다음 URL을 통해 컨테이너에 연결할 수 있습니다.

  • Firebase 하위 도메인:
    PROJECT_ID.web.app/PROJECT_ID.firebaseapp.com/

  • 연결된 모든 커스텀 도메인:
    CUSTOM_DOMAIN/

Hosting 구성 페이지 방문 재작성 규칙 자세히 알아보기 다음을 수행할 수 있습니다. 자세한 내용은 응답 우선 순위 다양한 Hosting 구성에 사용할 수 있습니다.

로컬에서 테스트

개발 단계에서는 컨테이너 이미지를 로컬에서 실행하고 테스트할 수 있습니다. 대상 자세한 안내를 보려면 Cloud Run 문서

다음 단계