Heroku의 Node.js 앱을 Cloud Run으로 마이그레이션

이 가이드에서는 Heroku에서 실행 중인 Node.js 웹 앱을 Google Cloud의 Cloud Run으로 마이그레이션하는 방법을 설명합니다. 이 가이드는 Heroku의 앱을 Google Cloud의 관리형 서비스로 마이그레이션하려는 설계자와 제품 소유자를 대상으로 합니다.

Cloud Run은 HTTP 요청으로 호출 가능한 스테이트리스(Stateless) 컨테이너를 실행하는 관리형 컴퓨팅 플랫폼입니다. 이 플랫폼은 오픈소스 Knative를 기반으로 구축되므로, 플랫폼 간 이동이 가능하고 컨테이너 워크플로와 지속적 배포를 위한 표준을 지원합니다. Cloud Run 플랫폼은 Google Cloud 제품군과도 원활하게 통합되므로 이동성, 확장성, 복원력이 우수한 앱의 설계와 개발이 더 쉬워졌습니다.

이 가이드에서는 Heroku에서 Node.js로 제작되고 Heroku Postgres를 백업 서비스로 사용하는 앱을 Google Cloud로 마이그레이션하는 방법을 알아봅니다. 웹 앱은 컨테이너화되어 Cloud Run에서 호스팅되며 PostgreSQL용 Cloud SQL을 영구 레이어로 사용합니다.

이 가이드에서는 태스크를 확인하고 만들 수 있는 간단한 앱인 Tasks를 사용합니다. 이러한 태스크는 현재 Heroku에서 실행 중인 앱의 배포에 있는 Heroku Postgres에 저장됩니다.

이 가이드에서는 개발자가 Heroku의 기본 기능에 익숙하고 Heroku 계정을 보유하고 있거나 Heroku 계정에 대한 액세스 권한이 있다고 가정합니다. 또한 Cloud Run, Cloud SQL, Docker, Node.js에 익숙하다고 가정합니다.

목표

  • Docker 이미지를 빌드하여 Cloud Run에 앱을 배포합니다.
  • Google Cloud로 마이그레이션한 후 백엔드로 사용할 PostgreSQL용 Cloud SQL 인스턴스를 만듭니다.
  • Node.js 코드를 살펴보면서 Cloud Run이 Cloud SQL에 연결되는 방법을 이해하고 Heroku에서 Cloud Run으로 마이그레이션하는 데 코드 변경이 필요한지 확인합니다.
  • Heroku Postgres의 데이터를 PostgreSQL용 Cloud SQL로 마이그레이션합니다.
  • Cloud Run에 앱을 배포합니다.
  • 배포된 앱을 테스트합니다.

비용

이 가이드에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

Heroku에 사용된 리소스에 대한 비용도 청구될 수 있습니다.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  3. Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.

  4. Cloud SQL and Cloud Run API를 사용 설정합니다.

    API 사용 설정

환경 설정

  1. Cloud Shell을 엽니다.

    Cloud Shell 열기

  2. Cloud Shell에서 가이드 전반에 사용되는 값(예: 리전 및 영역)의 기본 설정을 할당합니다. 이 가이드에서는 us-central1을 기본 리전으로, us-central1-a를 기본 영역으로 사용합니다.

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-a
    
  3. us-central1을 Cloud Run의 기본 리전으로 사용하도록 gcloud 명령줄 도구를 구성합니다.

    gcloud config set run/region us-central1
    
  4. 이 가이드의 기본 앱 이름을 저장할 환경 변수를 만듭니다.

    export APP_NAME=tasks-web-app
    

아키텍처

다음 그림 두 개는 Heroku의 웹 앱 아키텍처(현재 상태)와 Google Cloud의 아키텍처 레이아웃(빌드할)을 간략하게 보여줍니다.

현재 Heroku의 아키텍처
그림 1. 현재 Heroku의 아키텍처

현재 Heroku에 배포된 Tasks 앱은 1개 이상의 웹 dyno로 구성되어 있습니다. 백그라운드 작업 및 예약된 태스크에 더 적합한 작업자 dyno와 달리, 웹 dyno는 HTTP 트래픽을 수신하고 응답할 수 있습니다. 이 앱은 Node.js용 Mustache 템플릿 라이브러리를 사용하여 Postgres 데이터베이스에 저장된 태스크를 표시하는 색인 페이지를 제공합니다.

개발자는 HTTPS URL을 통해 앱에 액세스할 수 있습니다. 해당 URL에 /tasks 경로를 사용하면 새 태스크를 만들 수 있습니다.

현재 Heroku의 아키텍처
그림 2. Google Cloud에 빌드된 아키텍처

Google Cloud에서 Cloud Run은 Tasks 앱을 배포하는 서버리스 플랫폼으로 사용됩니다. Cloud Run은 요청에 따라 스테이트리스(Stateless) 컨테이너를 실행하도록 설계되었습니다. 또한 자동 확장되거나 트래픽을 제공하지 않을 때는 0으로 축소되는 컨테이너화된 앱을 지원하기 위해 관리형 서비스를 사용해야 하는 경우에 적합합니다.

Heroku에서 사용되는 구성요소와 Google Cloud 간 연결 관계

다음은 Heroku 플랫폼의 구성요소와 Google Cloud 간 연결 관계를 보여주는 표입니다. 이 연결 관계를 사용하면 이 가이드에 설명된 아키텍처를 Heroku에서 Google Cloud로 변환할 수 있습니다.

구성요소 Heroku 플랫폼 Google Cloud
컨테이너 Dyno: Heroku는 Heroku앱을 빌드하고 확장할 때 컨테이너 모델을 사용합니다. dyno라고 하는 이 Linux 컨테이너는 Heroku 앱의 리소스 요구를 지원하기 위해 지정된 수까지 확장할 수 있습니다. 앱의 메모리 및 CPU 요구사항을 기준으로 다양한 dyno 유형 중에서 선택할 수 있습니다. Cloud Run 컨테이너: Google Cloud는 완전 관리형 환경 또는 Google Kubernetes Engine(GKE) 클러스터에서 실행할 수 있는 스테이트리스(Stateless) 컨테이너에서 컨테이너식 워크로드 실행을 지원합니다.
웹 앱 Heroku 앱: Dyno는 Heroku 앱의 구성 요소입니다. 일반적으로 앱은 웹 dyno와 작업자 dyno를 조합한 1개 이상의 dyno 유형으로 구성됩니다. Cloud Run 서비스: 웹 앱은 Cloud Run 서비스로 모델링할 수 있습니다. 각 서비스는 자체 HTTPS 엔드포인트를 가져와 서비스 엔드포인트에서 발생하는 트래픽에 따라 0에서 N까지 자동으로 확장 또는 축소할 수 있습니다.
데이터베이스 Heroku Postgres: PostgreSQL을 기반으로 하는 Heroku의 DaaS(서비스로서의 데이터베이스)입니다. Cloud SQL: Google Cloud의 관계형 데이터베이스용 관리형 데이터베이스 서비스로, PostgreSQL과 MySQL 등 두 가지 변형을 제공합니다.

Tasks 웹 앱 샘플을 Heroku에 배포

다음 섹션에서는 Heroku에서 사용할 명령줄 인터페이스(CLI)를 설정하고, GitHub 소스 저장소를 클론하고, 앱을 Heroku에 배포하는 방법을 보여줍니다.

Heroku용 명령줄 인터페이스 설정

  1. Cloud Shell에서 Heroku CLI를 설치합니다.

  2. Heroku 계정에 로그인합니다.

    heroku login --interactive
    

소스 저장소 클론

  1. Cloud Shell에서 Tasks 앱 GitHub 저장소 샘플을 클론합니다.

    git clone https://github.com/GoogleCloudPlatform/migrate-webapp-heroku-to-cloudrun-node.git
    
  2. 디렉터리를 저장소를 클론하여 만든 디렉터리로 변경합니다.

    cd migrate-webapp-heroku-to-cloudrun-node
    

    디렉터리에는 다음과 같은 파일이 포함됩니다.

    • 웹 앱에서 제공하는 경로의 코드가 포함된 index.js라는 Node.js 스크립트
    • 웹 앱의 종속 항목이 표시된 package.jsonpackage-lock.json 파일. 앱을 실행하려면 이러한 종속 항목을 설치해야 합니다.
    • 앱 시작 시 실행할 명령어를 지정하는 Procfile 파일. 앱을 Heroku에 배포하려면 Procfile 파일을 만들어야 합니다.
    • '/' 경로의 웹 앱에서 제공하는 HTML 콘텐츠가 포함된 views 디렉터리
    • .gitignore 파일

Heroku에 앱 배포

  1. Cloud Shell에서 Heroku 앱을 만듭니다.

    heroku create
    
  2. Heroku Postgres 부가기능을 추가하여 PostgreSQL 데이터베이스를 프로비저닝합니다.

    heroku addons:create heroku-postgresql:hobby-dev
    
  3. 부가기능이 제대로 추가되었는지 확인합니다.

    heroku addons
    

    Postgres 부가기능이 성공적으로 추가되면 다음과 비슷한 메시지가 표시됩니다.

    Owning-App               Add-on                      Plan                          Price    State
    ----------------------   ------------------------    --------------------------    -----    -----
    sample-nodejs-todo-app   postgresql-adjacent-95585   heroku-postgresql:hobby-dev   free     created
    
  4. 앱을 Heroku에 배포합니다.

    git push heroku master
    
  5. Heroku 콘솔에서 Heroku Postgres URI를 검색합니다. 또는 다음 명령어를 실행하여 URI를 검색할 수도 있습니다.

    heroku config
    

    검색된 URI는 다음 단계에서 필요하므로 기록해 둡니다.

  6. Docker 컨테이너를 실행합니다. database-uri를 이전 단계에서 기록한 Heroku Postgres URI로 바꿉니다.

    docker run -it --rm postgres psql "database-uri"
    
  7. Docker 컨테이너에서 다음 명령어를 사용하여 TASKS 테이블을 만듭니다.

    CREATE TABLE TASKS
    (DESCRIPTION TEXT NOT NULL);
    
  8. 컨테이너를 종료합니다.

    exit
    
  9. Cloud Shell에서 Heroku 앱 URL을 가져옵니다.

    heroku info
    
  10. 브라우저 창에서 앱의 URL을 엽니다. 앱은 다음과 같이 표시됩니다. 단, 사용 중인 버전에 아래 나열된 태스크가 없을 수도 있습니다.

    웹브라우저의 할 일 앱

  11. 브라우저에서 앱의 샘플 태스크를 만듭니다. 데이터베이스에서 검색된 태스크가 UI에 표시되는지 확인합니다.

Cloud Run으로 마이그레이션할 웹 앱 코드 준비

이 섹션에서는 Cloud Run에 배포할 웹 앱을 준비하기 위해 완료해야 할 단계를 자세히 설명합니다.

Docker 컨테이너를 빌드하여 Container Registry에 게시

Docker 이미지는 Cloud Run에서 실행할 앱 컨테이너를 빌드하는 데 필요합니다. 컨테이너는 수동으로 또는 빌드팩을 사용하여 빌드할 수 있습니다.

컨테이너 수동 빌드

  1. Cloud Shell에서 이 가이드 용으로 제공된 저장소를 클론하여 만든 디렉터리에 Dockerfile을 만듭니다.

    cat <<EOF > Dockerfile
    # Use the official Node image.
    # https://hub.docker.com/_/node
    FROM node:10-alpine
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    RUN npm install
    
    # Copy local code to the container image.
    COPY . /app
    
    # Configure and document the service HTTP port.
    ENV PORT 8080
    EXPOSE $PORT
    
    # Run the web service on container startup.
    CMD ["npm", "start"]
    EOF
    
  2. Cloud Build로 컨테이너를 빌드하고 해당 이미지를 Container Registry에 게시합니다.

    gcloud builds submit --tag gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1
    
  3. 생성된 Docker 이미지의 이름을 저장할 환경 변수를 만듭니다.

    export IMAGE_NAME="gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1"
    

빌드팩으로 컨테이너 빌드

  1. Cloud Shell에서 pack CLI를 설치합니다.

    wget https://github.com/buildpack/pack/releases/download/v0.2.1/pack-v0.2.1-linux.tgz
    tar xvf pack-v0.2.1-linux.tgz
    rm pack-v0.2.1-linux.tgz
    sudo mv pack /usr/local/bin/
    
  2. 기본적으로 Heroku 빌더를 사용하도록 pack CLI를 설정합니다.

    pack set-default-builder heroku/buildpacks
    
  3. Docker 이미지 이름을 저장할 환경 변수를 만듭니다.

    export IMAGE_NAME=gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1
    
  4. pack 명령어를 사용하여 이미지를 빌드한 후 Container Registry에 이 이미지를 내보내거나 게시합니다.

    pack build --publish $IMAGE_NAME
    

PostgreSQL용 Cloud SQL 인스턴스 만들기

웹 앱의 백엔드 역할을 하는 PostgreSQL용 Cloud SQL 인스턴스를 만듭니다. 이 가이드에서는 PostgreSQL이 Heroku에 배포된 샘플 앱으로 가장 적합하며 이 앱은 Postgres 데이터베이스를 백엔드로 사용합니다. 이 앱의 용도에 맞춰 관리형 Postgres 서비스에서 PostgreSQL용 Cloud SQL로 마이그레이션할 때는 스키마를 변경할 필요가 없습니다.

gcloud

  1. 다음 단계에서 만들 데이터베이스 인스턴스의 이름을 저장할 CLOUDSQL_DB_NAME이라는 환경 변수를 만듭니다.

    export CLOUDSQL_DB_NAME=tasks-db
    
  2. 데이터베이스를 만듭니다.

    gcloud sql instances create $CLOUDSQL_DB_NAME  \
        --cpu=1 \
        --memory=4352Mib \
        --database-version=POSTGRES_9_6 \
        --region=us-central1
    

    인스턴스를 초기화하는 데 몇 분 정도 걸릴 수 있습니다.

  3. Postgres 사용자의 비밀번호를 설정합니다.

    gcloud sql users set-password postgres \
        --instance=$CLOUDSQL_DB_NAME  \
        --password=postgres-password
    

Console

  1. Cloud Console에서 Cloud SQL 인스턴스 페이지로 이동합니다.

    Cloud SQL 인스턴스 페이지로 이동

  2. 인스턴스 만들기를 클릭합니다.

  3. PostgreSQL을 선택하고 다음을 클릭합니다.

  4. 인스턴스 이름으로 tasks-db를 입력합니다.

  5. postgres 사용자의 비밀번호를 만듭니다.

  6. 리전에 us-central1을 선택합니다.

  7. 다른 기본값을 수정하지 마세요.

Heroku Postgres에서 Cloud SQL로 데이터 가져오기

Cloud SQL로 데이터를 마이그레이션하는 데 사용할 수 있는 마이그레이션 패턴에는 여러 가지가 있습니다. 일반적으로 다운타임이 최소화되거나 발생하지 않는 방법이 최고의 방법으로 여겨지며, 이를 위해서는 마이그레이션할 데이터베이스의 복제본으로 Cloud SQL을 구성한 후 Cloud SQL을 마이그레이션 이후의 기본 인스턴스로 설정하는 것이 좋습니다. Heroku Postgres는 외부 복제본(팔로어)을 지원하지 않으므로, 이 가이드에서는 오픈소스 도구를 사용하여 앱의 스키마를 마이그레이션해야 합니다.

이 가이드에서 사용하는 Tasks 앱의 경우 pg_dump 유틸리티를 사용하여 Heroku Postgres의 데이터를 Cloud Storage 버킷으로 내보낸 다음 Cloud SQL로 가져옵니다. 이 유틸리티는 동종 버전 간에 또는 대상 데이터베이스의 버전이 원본 데이터베이스보다 최신일 경우에 데이터를 전송할 수 있습니다.

  1. Cloud Shell에서 샘플 앱에 연결된 Heroku Postgres 데이터베이스의 데이터베이스 사용자 인증 정보를 가져옵니다. 이 사용자 인증 정보는 다음 단계에서 필요합니다.

    heroku pg:credentials:url
    

    이 명령어는 애플리케이션의 연결 정보 문자열과 연결 URL을 반환합니다. 연결 정보 문자열의 형식은 다음과 같습니다.

    "dbname=database-name host=FQDN port=5432 user=user-name password=password-string sslmode=require"
    

    연결 정보 문자열의 FQDN(정규화 된 도메인 이름) 값의 예는 Heroku 문서를 참조하세요.

  2. 후속 단계에서 사용할 Heroku 값을 저장할 환경 변수를 설정합니다. FQDN, user-name, password-string, database-name 자리표시자를 연결 정보 문자열의 해당 값으로 바꿉니다.

    export HEROKU_PG_HOST=FQDN
    export HEROKU_PG_USER=user-name
    export HEROKU_PG_PASSWORD=password-string
    export HEROKU_PG_DBNAME=database-name
    
  3. Heroku Postgres 데이터베이스의 백업을 SQL 형식으로 만듭니다.

    docker run \
      -it --rm \
      -e PGPASSWORD=$HEROKU_PG_PASSWORD \
      -v $(pwd):/tmp \
      --entrypoint "pg_dump" \
      postgres \
      -Fp \
      --no-acl \
      --no-owner \
      -h $HEROKU_PG_HOST \
      -U $HEROKU_PG_USER \
      $HEROKU_PG_DBNAME > herokudump.sql
    
  4. Cloud Storage 버킷의 이름을 저장할 환경 변수를 만듭니다. 이 이름은 고유해야 하고 환경 변수는 gs://bucket-name 형식이어야 합니다.

    export PG_BACKUP_BUCKET=gs://bucket-name
    
  5. gsutil 명령줄 도구를 사용하여 Cloud Storage 버킷을 만듭니다.

    gsutil mb -c regional -l us-central1 $PG_BACKUP_BUCKET
    
  6. SQL 파일을 이 버킷에 업로드합니다.

    gsutil cp herokudump.sql $PG_BACKUP_BUCKET
    
  7. Cloud Console에서 Cloud SQL 인스턴스 페이지로 이동합니다.

    Cloud SQL 인스턴스 페이지로 이동

  8. 인스턴스를 선택하여 인스턴스 세부정보 페이지를 엽니다.

  9. 버튼 모음에서 가져오기를 클릭합니다.

    Cloud Storage에서 SQL 덤프 파일 가져오기

  10. Cloud Storage 파일에 Cloud Storage에 업로드한 SQL 덤프 파일 경로를 입력합니다.

  11. 가져오기 형식SQL로 선택합니다.

  12. 데이터베이스postgres를 선택합니다.

  13. 고급 옵션을 펼친 후 사용자postgres를 선택합니다.

  14. 가져오기를 클릭하여 가져오기를 시작합니다.

  15. 인스턴스의 작업 탭에서 업데이트를 검토하여 가져오기가 완료되었는지 확인합니다.

Cloud Run이 Cloud SQL 데이터베이스에 액세스하는 방법

Heroku에 배포된 웹 앱이 Heroku Postgres의 관리형 인스턴스에 연결되어야 하는 것처럼 Cloud Run에서 데이터를 읽고 쓰려면 Cloud SQL에 액세스해야 합니다.

Cloud Run은 Cloud Run에 컨테이너를 배포할 때 자동으로 활성화 및 구성되는 Cloud SQL 프록시를 사용하여 Cloud SQL과 통신합니다. 수신하는 모든 통신이 보안 TCP를 사용하여 프록시에서 수신되기 때문에 데이터베이스에서 외부 IP 주소를 승인할 필요가 없습니다.

코드는 UNIX 소켓을 통해 프록시를 호출하여 데이터베이스 작업(예: 데이터베이스에서 데이터 가져오기 또는 쓰기)을 호출해야 합니다.

이 웹 앱은 Node.js로 제작되므로 pg-connection-string 라이브러리를 사용하여 데이터베이스 URL을 파싱하고 config 객체를 만듭니다. 이 방법을 사용할 때의 장점은 Heroku 및 Cloud Run에서 백엔드 데이터베이스에 원활하게 연결된다는 점입니다.

다음 단계에서는 웹 앱을 배포할 때 데이터베이스 URL을 환경 변수로 전달합니다.

Cloud Run에 샘플 앱 배포

  1. Cloud Shell에서 생성된 Cloud SQL 인스턴스의 연결 이름을 저장할 환경 변수를 만듭니다.

    export DB_CONN_NAME=$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='value(connectionName)')
    
  2. UNIX 포트를 통해 Cloud SQL 프록시에 연결하는 데 필요한 연결 문자열을 저장하기 위해 DATABASE_URL이라는 환경 변수를 만듭니다. your-db-password를 데이터베이스 인스턴스용으로 만든 비밀번호로 바꿉니다.

    export DATABASE_URL="socket:/cloudsql/${DB_CONN_NAME}?db=postgres&user=postgres&password=your-db-password"
    
  3. Cloud Run에 웹 앱을 배포합니다.

    gcloud run deploy tasksapp-$DEVSHELL_PROJECT_ID \
        --image=$IMAGE_NAME \
        --set-env-vars=DATABASE_URL=$DATABASE_URL \
        --add-cloudsql-instances $DB_CONN_NAME \
        --allow-unauthenticated
    

    위의 명령어를 실행하면 Cloud Run 컨테이너가 생성된 Cloud SQL 데이터베이스 인스턴스에도 연결됩니다. 이 명령어는 Cloud Run용 환경 변수가 이전 단계에서 생성된 DATABASE_URL 문자열을 가리키도록 설정합니다.

애플리케이션 테스트

  1. Cloud Shell에서 Cloud Run이 트래픽을 제공하는 URL을 가져옵니다.

    gcloud run services list
    

    Cloud Console에서 Cloud Run 서비스를 검토할 수도 있습니다.

  2. Cloud Run 서비스 URL로 이동하여 웹 앱이 HTTP 요청을 허용하는지 확인합니다.

Cloud Run은 HTTP 요청이 제공 엔드포인트로 전송될 때와 컨테이너가 아직 실행 중이 아닌 경우 새 컨테이너를 만들거나 가동합니다. 따라서 새 컨테이너의 가동 요청을 처리하는 데 시간이 좀 더 오래 걸릴 수 있습니다. 추가 시간을 고려하는 경우 앱에서 지원할 수 있는 동시 요청 수와 특정 메모리 요구사항을 고려하세요.

이 앱에서 기본 동시 실행 설정을 사용하면 Cloud Run 서비스가 단일 컨테이너에서 80개의 요청을 동시에 처리하도록 할 수 있습니다.

삭제

이 가이드에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 안내를 따르세요. 이 가이드용으로 Heroku에서 만든 리소스를 삭제하는 것이 좋습니다.

Google Cloud 프로젝트 삭제

  1. Cloud Console에서 리소스 관리 페이지로 이동합니다.

    리소스 관리로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.

Heroku 앱 삭제

Heroku에 배포된 샘플 앱과 관련 PostgreSQL 부가기능을 삭제하려면 다음 명령어를 실행합니다.

heroku apps:destroy -a $APP_NAME

다음 단계