Node.js アプリを Heroku から Cloud Run に移行する

このチュートリアルでは、Heroku で実行されている Node.js ウェブアプリを Google Cloud の Cloud Run に移行する方法について説明します。このチュートリアルは、Heroku から Google Cloud のマネージド サービスにアプリを移行する設計者とプロダクト オーナーを対象としています。

Cloud Run はマネージド コンピューティング プラットフォームで、HTTP リクエストを介して呼び出すことができるステートレス コンテナを実行できます。Cloud Run はオープンソースの Knative で構築されています。Knative は、プラットフォーム間でのポータビリティを可能にし、コンテナのワークフローや継続的デリバリーの標準をサポートしています。Cloud Run プラットフォームは Google Cloud プロダクト スイートとうまく統合されており、移植性、スケーラビリティ、復元性に優れたアプリの設計と開発が容易になります。

このチュートリアルでは、Node.js で作成され、Heroku のバッキング サービスとして Heroku Postgres を使用しているアプリを、Google Cloud に移行する方法について説明します。このウェブアプリは Cloud Run でコンテナ化されてホストされ、永続的なレイヤとして Cloud SQL for PostgreSQL を使用します。

このチュートリアルでは、タスクの表示や作成ができる Tasks というシンプルなアプリを使用します。これらのタスクは、Heroku 上で現在デプロイされているアプリ内の Heroku Postgres に格納されます。

このチュートリアルは、Heroku の基本的な機能に精通しており、Heroku アカウント(あるいはアカウントへのアクセス権)があることを前提としています。また、Cloud RunCloud SQLDockerNode.js に精通していることを前提としています。

目標

  • Cloud Run にアプリをデプロイするための Docker イメージをビルドします。
  • Google Cloud への移行後にバックエンドとして機能する Cloud SQL for PostgreSQL インスタンスを作成します。
  • Cloud Run が Cloud SQL に接続する方法を理解し、Heroku から Cloud Run に移行するために必要なコードの変更(ある場合)を確認するため、Node.js コードを確認します。
  • Heroku Postgres から Cloud SQL for PostgreSQL にデータを移行します。
  • 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. Cloud Run のデフォルトのリージョンとして us-central1 を使用するように、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 は、ステートレスでリクエスト主導のコンテナを実行するように設計されています。自動スケーリングを行い、かつトラフィックを提供していないときにはゼロまでスケールダウンするコンテナ化されたアプリをサポートするマネージド サービスを必要とする場合に適しています。

Heroku で使用されるコンポーネントを Google Cloud にマッピングする

次の表は、Heroku プラットフォーム上のコンポーネントと Google Cloud 上のコンポーネントの比較を示したものです。この表は、このチュートリアルで説明するアーキテクチャを Heroku から Google Cloud に変換する際に役立ちます。

コンポーネント Heroku プラットフォーム Google Cloud
コンテナ dyno: Heroku はコンテナモデルを使用して、Heroku アプリをビルドおよびスケールします。これらの Linux コンテナは dyno と呼ばれるもので、指定した数にスケーリングして、Heroku アプリのリソース需要に対応できます。また、さまざまな dyno タイプから、アプリのメモリ要件、CPU 要件に応じて、dyno を選ぶことができます。 Cloud Run コンテナ: Google Cloud はフルマネージド環境や Google Kubernetes Engine(GKE)クラスタで実行できるステートレス コンテナ内で、コンテナ化されたワークロードの実行をサポートします。
ウェブアプリ Heroku アプリ: dyno は、Heroku アプリの構成要素です。通常、アプリは 1 つ以上の dyno タイプで構成されます。また、アプリは通常、ウェブ dyno とワーカー dyno の組み合わせです。 Cloud Run サービス: ウェブアプリは Cloud Run サービスとしてモデル化されます。各サービスは独自の HTTPS エンドポイントを取得します。サービスのエンドポイントへのトラフィックに応じて、0 から N までの範囲で自動スケーリングします。
データベース Heroku Postgres は、PostgreSQL に基づいた Heroku のサービスとしてのデータベース(DaaS)です。 Cloud SQL は、Google Cloud 上のリレーショナル データベース用のマネージド データベース サービスです。PostgreSQL と MySQL の 2 種類があります。

サンプルの 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.json ファイルと package-lock.json ファイル。アプリを実行するには、これらの依存関係をインストールする必要があります。
    • アプリが起動時に実行するコマンドを指定する Procfile ファイル。Procfile ファイルを作成して、アプリを Heroku にデプロイします。
    • 「/」ルート上のウェブアプリによって提供される 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 をメモします。メモした 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 をブラウザ ウィンドウで開きます。アプリは次のように表示されます(ただし、ご自身のバージョンではリストになったタスクはありません)。

    ウェブブラウザ内の ToDo アプリ。

  11. ブラウザから、アプリでサンプルタスクを作成します。タスクがデータベースから取得され、UI に表示されることを確認します。

Cloud Run への移行のためのウェブアプリ コードの準備

このセクションでは、ウェブアプリを Cloud Run へデプロイするために必要な手順を説明します。

Docker コンテナをビルドして Container Registry に公開する

Cloud Run で実行できるようにアプリコンテナをビルドするには、Docker イメージが必要です。コンテナは手動、または Buildpack を使用してビルドできます。

コンテナを手動でビルドする

  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"
    

Buildpack を使用してコンテナをビルドする

  1. Cloud Shell で、パック 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 ビルダーを使用するようにパック CLI を設定します。

    pack set-default-builder heroku/buildpacks
    
  3. Docker イメージ名を保持する環境変数を作成します。

    export IMAGE_NAME=gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1
    
  4. pack コマンドを使用してイメージをビルドし、イメージを Container Registry に push または公開します。

    pack build --publish $IMAGE_NAME
    

Cloud SQL for PostgreSQL インスタンスを作成する

ウェブアプリのバックエンドとして機能する Cloud SQL for PostgreSQL インスタンスを作成します。このチュートリアルでは、PostgreSQL が Heroku にデプロイされるサンプルアプリとして最適です。Heroku は、Postgres データベースをバックエンドとして使用します。マネージド Postgres サービスから Cloud SQL for PostgreSQL への移行では、このアプリのためにスキーマを変更する必要はありません。

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 値を保持する環境変数を設定します。FQDNuser-namepassword-stringdatabase-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 と通信します。データベースは、外部 IP アドレスを承認する必要はありません。これは、データベースが受信する通信はすべて、セキュア TCP を使用するプロキシからの通信であるためです。

コードは、Unix ソケットを介してプロキシを呼び出すことにより、データベース オペレーション(データベースからのデータの取得やデータベースへの書き込みなど)を呼び出す必要があります。

このウェブアプリは Node.js で記述されているため、データベース URL を解析し、config オブジェクトを作成するには、pg-connection-string ライブラリを使用します。このアプローチの利点は、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 SQL データベース インスタンスに Cloud Run コンテナをリンクします。このコマンドは、前のステップで作成した DATABASE_URL 文字列を指すように Cloud Run の環境変数を設定します。

アプリケーションのテスト

  1. Cloud Shell で、Cloud Run がトラフィックを提供する URL を取得します。

    gcloud run services list
    

    また、Cloud Console で Cloud Run サービスを確認することもできます。

  2. Cloud Run サービスの URL に移動して、ウェブアプリが HTTP リクエストを受け入れていることを確認します。

HTTP リクエストがサービスを提供しているエンドポイントに送信された場合と、コンテナがまだ実行されていない場合、Cloud Run は新しいコンテナを作成、またはスピンアップします。つまり、新しいコンテナをスピンアップさせるリクエストは、提供されるまでに少し時間がかかる場合があります。時間がかかる場合は、アプリがサポートできる同時リクエスト数と、アプリが持つ固有のメモリ要件を考慮してください。

このアプリでは、デフォルトの同時実行設定を使用します。この設定では、Cloud Run サービスは 1 つのコンテナからの 80 件のリクエストを同時に処理できます。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにする手順は次のとおりです。このチュートリアルで、Heroku で作成したリソースも削除するといいでしょう。

Google Cloud プロジェクトの削除

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

Heroku アプリの削除

Heroku と関連する PostgreSQL アドオンにデプロイしたサンプルアプリを削除するには、次のコマンドを実行します。

heroku apps:destroy -a $APP_NAME

次のステップ