Compute Engine VM の起動または停止をスケジュールする


このチュートリアルでは、Cloud Scheduler と Cloud Functions で、リソースラベルを使用して定期的に Compute Engine インスタンスを起動および停止する方法を説明します。

目標

  • Cloud Functions で、Compute Engine インスタンスの起動関数と停止関数を作成してデプロイする。
  • Cloud Scheduler を使用して一連のジョブを作成し、devリソースラベルを持つインスタンスをスケジュールして、通常の営業時間に一致させるために、月曜日から金曜日の 09:00-17:00 に実行します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

  • Cloud Scheduler
  • Cloud Functions
  • Pub/Sub
  • Compute Engine

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

始める前に

  1. Cloud Scheduler 用に環境を設定します。

    環境設定

  2. Cloud Functions, Pub/Sub, and Compute Engine API を有効にします。

    API を有効にする

アプリケーション アーキテクチャ

このソリューションでは、次の Google Cloud コンポーネントを使用します。

Cloud Scheduler が Pub/Sub 経由で Compute Engine インスタンスのスケジュールを設定する様子を示すシステム アーキテクチャ図

ロケーションの要件

一部のコンポーネントは特定のリージョンでのみサポートされています。

  • Compute Engine インスタンス: リージョンとゾーンにリストされているすべてのリージョンでサポートされます。
  • Cloud Functions: ロケーションにリストされているリージョンでサポートされます。
  • Pub/Sub メッセージ: Pub/Sub はグローバル サービスであるため、グローバルでサポートされます。
  • Pub/Sub ターゲットを含む Cloud Scheduler ジョブ: すべての Google Cloud のロケーションでサポートされます。

HTTP ではなく Pub/Sub を使用する理由

Pub/Sub トリガーの代わりに Cloud Functions の HTTP トリガーを使用して、このアーキテクチャをシンプルにしたいと考えるかもしれません。

この方法が以前は HTTP を使用するよりも安全であったため、このチュートリアルでは、Cloud Functions のトリガーとして Pub/Sub を使用します。ただし、HTTP も有効な選択であり、認証を要求することで保護できるようになりました。

Cloud Functions の保護については、Cloud Functions のセキュリティの概要をご覧ください。HTTP トリガーと Pub/Sub トリガーの比較については、Cloud Functions トリガーのドキュメントをご覧ください。

Compute Engine インスタンスを設定する

Console

  1. Google Cloud コンソールの [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動
  2. [インスタンスを作成] をクリックします。
  3. [名前] を dev-instance に設定します。
  4. [ラベル] で [ラベルを追加] をクリックします。
  5. [ラベルを追加] をクリックします。
  6. [キー] には「env」、[] には「dev」と入力します。
  7. [リージョン] で [us-west1] を選択します。
  8. [ゾーン] で [us-west1-b] を選択します。
  9. [保存] をクリックします。
  10. ページの下部にある [作成] をクリックします。

gcloud

gcloud compute instances create dev-instance \
    --network default \
    --zone us-west1-b \
    --labels=env=dev

Cloud Functions を介して Pub/Sub によってトリガーされる関数をデプロイする

関数を作成してデプロイする

Console

起動の関数を作成する

  1. Google Cloud コンソールの [Cloud Functions] ページに移動します。
    [Cloud Functions] ページに移動
  2. [ファンクションを作成] をクリックします。
  3. [環境] には、[第 1 世代] を選択します。
  4. [関数名] を startInstancePubSub に設定します。
  5. [リージョン] はデフォルト値のままにします。
  6. [トリガーのタイプ] で、[Cloud Pub/Sub] を選択します。
  7. [Cloud Pub/Sub トピックを選択してください] で、[トピックを作成する] をクリックします。
  8. [トピックの作成] ダイアログが表示されます。
    1. [トピック ID] には「start-instance-event」と入力します。
    2. [作成] をクリックしてダイアログを終了します。
  9. [トリガー] ボックスの下部にある [保存] をクリックします。
  10. ページの下部にある [次へ] をクリックします。
  11. [ランタイム] で [Node.js 16] 以降を選択します。
  12. [エントリ ポイント] に「startInstancePubSub」と入力します。
  13. コードエディタの左側にある [index.js] を選択します。
  14. スターター コードを次のコードに置き換えます。

    const compute = require('@google-cloud/compute');
    const instancesClient = new compute.InstancesClient();
    const operationsClient = new compute.ZoneOperationsClient();
    
    async function waitForOperation(projectId, operation) {
      while (operation.status !== 'DONE') {
        [operation] = await operationsClient.wait({
          operation: operation.name,
          project: projectId,
          zone: operation.zone.split('/').pop(),
        });
      }
    }
    
    /**
     * Starts Compute Engine instances.
     *
     * Expects a PubSub message with JSON-formatted event data containing the
     * following attributes:
     *  zone - the GCP zone the instances are located in.
     *  label - the label of instances to start.
     *
     * @param {!object} event Cloud Function PubSub message event.
     * @param {!object} callback Cloud Function PubSub callback indicating
     *  completion.
     */
    exports.startInstancePubSub = async (event, context, callback) => {
      try {
        const project = await instancesClient.getProjectId();
        const payload = _validatePayload(event);
        const options = {
          filter: `labels.${payload.label}`,
          project,
          zone: payload.zone,
        };
    
        const [instances] = await instancesClient.list(options);
    
        await Promise.all(
          instances.map(async instance => {
            const [response] = await instancesClient.start({
              project,
              zone: payload.zone,
              instance: instance.name,
            });
    
            return waitForOperation(project, response.latestResponse);
          })
        );
    
        // Operation complete. Instance successfully started.
        const message = 'Successfully started instance(s)';
        console.log(message);
        callback(null, message);
      } catch (err) {
        console.log(err);
        callback(err);
      }
    };
    
    /**
     * Validates that a request payload contains the expected fields.
     *
     * @param {!object} payload the request payload to validate.
     * @return {!object} the payload object.
     */
    const _validatePayload = event => {
      let payload;
      try {
        payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
      } catch (err) {
        throw new Error('Invalid Pub/Sub message: ' + err);
      }
      if (!payload.zone) {
        throw new Error("Attribute 'zone' missing from payload");
      } else if (!payload.label) {
        throw new Error("Attribute 'label' missing from payload");
      }
      return payload;
    };
  15. コードエディタの左側にある [package.json] を選択します。

  16. スターター コードを次のコードに置き換えます。

    {
      "name": "cloud-functions-schedule-instance",
      "version": "0.1.0",
      "private": true,
      "license": "Apache-2.0",
      "author": "Google Inc.",
      "repository": {
        "type": "git",
        "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
      },
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "test": "c8 mocha -p -j 2 test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "c8": "^8.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^16.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^4.0.0"
      }
    }
    
  17. ページの下部にある [Deploy] をクリックします。

停止の関数を作成する

  1. Google Cloud コンソールに [Cloud Functions] ページが表示されているはずです。
  2. [ファンクションを作成] をクリックします。
  3. [環境] には、[第 1 世代] を選択します。
  4. [関数名] を stopInstancePubSub に設定します。
  5. [リージョン] はデフォルト値のままにします。
  6. [トリガーのタイプ] で、[Cloud Pub/Sub] を選択します。
  7. [Cloud Pub/Sub トピックを選択してください] で、[トピックを作成する] をクリックします。
  8. [トピックの作成] ダイアログが表示されます。
    1. [トピック ID] には「stop-instance-event」と入力します。
    2. [作成] をクリックしてダイアログを終了します。
  9. [トリガー] ボックスの下部にある [保存] をクリックします。
  10. ページの下部にある [次へ] をクリックします。
  11. [ランタイム] で [Node.js 16] 以降を選択します。
  12. [エントリ ポイント] に「stopInstancePubSub」と入力します。
  13. コードエディタの左側にある [index.js] を選択します。
  14. スターター コードを次のコードに置き換えます。

    const compute = require('@google-cloud/compute');
    const instancesClient = new compute.InstancesClient();
    const operationsClient = new compute.ZoneOperationsClient();
    
    async function waitForOperation(projectId, operation) {
      while (operation.status !== 'DONE') {
        [operation] = await operationsClient.wait({
          operation: operation.name,
          project: projectId,
          zone: operation.zone.split('/').pop(),
        });
      }
    }
    
    /**
     * Stops Compute Engine instances.
     *
     * Expects a PubSub message with JSON-formatted event data containing the
     * following attributes:
     *  zone - the GCP zone the instances are located in.
     *  label - the label of instances to stop.
     *
     * @param {!object} event Cloud Function PubSub message event.
     * @param {!object} callback Cloud Function PubSub callback indicating completion.
     */
    exports.stopInstancePubSub = async (event, context, callback) => {
      try {
        const project = await instancesClient.getProjectId();
        const payload = _validatePayload(event);
        const options = {
          filter: `labels.${payload.label}`,
          project,
          zone: payload.zone,
        };
    
        const [instances] = await instancesClient.list(options);
    
        await Promise.all(
          instances.map(async instance => {
            const [response] = await instancesClient.stop({
              project,
              zone: payload.zone,
              instance: instance.name,
            });
    
            return waitForOperation(project, response.latestResponse);
          })
        );
    
        // Operation complete. Instance successfully stopped.
        const message = 'Successfully stopped instance(s)';
        console.log(message);
        callback(null, message);
      } catch (err) {
        console.log(err);
        callback(err);
      }
    };
    
    /**
     * Validates that a request payload contains the expected fields.
     *
     * @param {!object} payload the request payload to validate.
     * @return {!object} the payload object.
     */
    const _validatePayload = event => {
      let payload;
      try {
        payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
      } catch (err) {
        throw new Error('Invalid Pub/Sub message: ' + err);
      }
      if (!payload.zone) {
        throw new Error("Attribute 'zone' missing from payload");
      } else if (!payload.label) {
        throw new Error("Attribute 'label' missing from payload");
      }
      return payload;
    };
  15. コードエディタの左側にある [package.json] を選択します。

  16. スターター コードを次のコードに置き換えます。

    {
      "name": "cloud-functions-schedule-instance",
      "version": "0.1.0",
      "private": true,
      "license": "Apache-2.0",
      "author": "Google Inc.",
      "repository": {
        "type": "git",
        "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
      },
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "test": "c8 mocha -p -j 2 test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "c8": "^8.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^16.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^4.0.0"
      }
    }
    
  17. ページの下部にある [Deploy] をクリックします。

gcloud

Pub/Sub トピックを作成する

gcloud pubsub topics create start-instance-event
gcloud pubsub topics create stop-instance-event

コードを取得

  1. コードをダウンロードします。

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

  2. 適切なディレクトリに移動します。

    cd nodejs-docs-samples/functions/scheduleinstance/
    

起動の関数と停止の関数を作成する

nodejs-docs-samples/functions/scheduleinstance/ ディレクトリにいるはずです。

gcloud functions deploy startInstancePubSub \
    --trigger-topic start-instance-event \
    --runtime nodejs18 \
    --allow-unauthenticated
gcloud functions deploy stopInstancePubSub \
    --trigger-topic stop-instance-event \
    --runtime nodejs18 \
    --allow-unauthenticated

(省略可)ファンクションが動作することを確認する

Console

インスタンスを停止する

  1. Google Cloud コンソールの [Cloud Functions] ページに移動します。
    [Cloud Functions] ページに移動
  2. stopInstancePubSub という名前の関数をクリックします。
  3. 複数のタブ([全般]、[トリガー]、[ソース]、[権限]、[テスト])が表示されます。[テスト] タブをクリックします。
  4. [トリガーとなるイベント] に、次のテキストを入力します。

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • これは、{"zone":"us-west1-b", "label":"env=dev"} を base64 でエンコードした文字列です。

    • 独自の文字列をエンコードするには、任意のオンライン base64 エンコード ツールを使用してください。

  5. [関数をテスト] ボタンをクリックします。

  6. 実行が完了すると、[出力] の下に「Successfully stopped instance dev-instance」と表示されます。実行の終了までに最長で 60 秒かかります。

    • 代わりに「error: 'Error: function failed to load.'」が表示される場合は、関数のデプロイが完了するまで 10 秒ほど待ってから、もう一度お試しください。

    • 代わりに「error: 'Error: function execution attempt timed out.'」が表示される場合は、次の手順に進んで、インスタンスのシャットダウンに時間がかかっているだけかどうかを確認してください。

    • 実行が終了しても何も表示されない場合は、タイムアウトになっただけの可能性もあります。次のステップに進んで、インスタンスのシャットダウンに時間がかかっているだけかどうかを確認してください。

  7. Google Cloud コンソールの [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動

  8. dev-instance という名前のインスタンスの横に灰色の四角が表示されていることを確認します。これは、停止中であることを示しています。シャットダウンが完了するまでに最長で 30 秒かかります。

    • 停止が完了していない場合は、ページの上部にある [更新] をクリックしてみてください。

インスタンスを起動する

  1. Google Cloud コンソールの [Cloud Functions] ページに移動します。
    [Cloud Functions] ページに移動
  2. startInstancePubSub という名前の関数をクリックします。
  3. 複数のタブ([全般]、[トリガー]、[ソース]、[権限]、[テスト])が表示されます。[テスト] タブをクリックします。
  4. [トリガーとなるイベント] に、次のテキストを入力します。

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • これも {"zone":"us-west1-b", "label":"env=dev"} を base64 でエンコードした文字列です。
  5. [関数をテスト] ボタンをクリックします。

  6. 実行が完了すると、[出力] の下に「Successfully started instance dev-instance」と表示されます。

  7. Google Cloud コンソールの [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動

  8. dev-instanceという名前のインスタンスの名前の横に緑色のチェックマークが表示されていることを確認します。これは実行中であることを湿しています。起動処理が完了するまでに最長で 30 秒かかります。

gcloud

インスタンスを停止する

  1. 関数を呼び出してインスタンスを停止します。

    gcloud functions call stopInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • これは、{"zone":"us-west1-b", "label":"env=dev"} を base64 でエンコードした文字列です。

    • 独自の文字列をエンコードするには、任意のツールを使用してください。 base64 コマンドライン ツールを使用する場合の例を次に示します。

      echo '{"zone":"us-west1-b", "label":"env=dev"}' | base64
      
      eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo=
      

    関数が終了すると、次のように表示されます。

    result: Successfully stopped instance dev-instance
    

    実行の終了までに最長で 60 秒かかります。

    • 次のエラーが出力されることがあります。

      error: 'Error: function failed to load.`
      

      この場合は、関数のデプロイが完了するまで 10 秒ほど待ってからやり直してください。

    • 次のエラーが出力されることがあります。

      error: `Error: function execution attempt timed out.`
      

      この場合は、次のステップに進んで、インスタンスのシャットダウンに時間がかかっているだけかどうかを確認してください。

    • 何も表示されない場合は、関数がタイムアウトになっただけの可能性もあります。 この場合は、次のステップに進んで、インスタンスのシャットダウンに時間がかかっているだけかどうかを確認してください。

  2. インスタンスのステータスが TERMINATED であることを確認します。シャットダウンが完了するまでに最長で 30 秒かかります。

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: TERMINATED
    

インスタンスを起動する

  1. 関数を呼び出してインスタンスを起動します。

    gcloud functions call startInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • これも {"zone":"us-west1-b", "label":"env=dev"} を base64 でエンコードした文字列です。

    関数が終了すると、次のように表示されます。

    result: Successfully started instance dev-instance
    
  2. インスタンスのステータスが RUNNING であることを確認します。起動処理が完了するまでに最長で 30 秒かかります。

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: RUNNING
    

Pub/Sub を呼び出すように Cloud Scheduler ジョブを設定する

ジョブを作成する

Console

起動ジョブを作成する

  1. Google Cloud コンソールの [Cloud Scheduler] ページに移動します。
    [Cloud Scheduler] ページに移動
  2. [ジョブを作成] をクリックします。
  3. デフォルト リージョンのままにします。
  4. [名前] を startup-dev-instances に設定します。
  5. [頻度] に「0 9 * * 1-5」と入力します。
    • 毎週平日午前 9 時に実行されます。
  6. [タイムゾーン] で、目的の国とタイムゾーンを選択します。この例では、United StatesLos Angeles を使用します。
  7. [続行] をクリックします。
  8. [ターゲット タイプ] で、Pub/Sub を選択します。
  9. トピックのプルダウンから start-instance-event を選択します。
  10. [メッセージ] に次の情報を入力します。
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. [作成] をクリックします。

停止ジョブを作成する

  1. Google Cloud コンソールの [Cloud Scheduler] ページに移動します。
  2. [ジョブを作成] をクリックします。
  3. [リージョン] はデフォルト値のままにして、ページの下部にある [次へ] をクリックします。
  4. [名前] を shutdown-dev-instances に設定します。
  5. [頻度] に「0 17 * * 1-5」と入力します。
    • 毎週平日午後 5 時に実行されます。
  6. [タイムゾーン] で、目的の国とタイムゾーンを選択します。この例では、United StatesLos Angeles を使用します。
  7. [続行] をクリックします。
  8. [ターゲット タイプ] で、Pub/Sub を選択します。
  9. トピックのプルダウンから stop-instance-event を選択します。
  10. [メッセージ] に次の情報を入力します。
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. [作成] をクリックします。

gcloud

起動ジョブを作成する

gcloud scheduler jobs create pubsub startup-dev-instances \
    --schedule '0 9 * * 1-5' \
    --topic start-instance-event \
    --message-body '{"zone":"us-west1-b", "label":"env=dev"}' \
    --time-zone 'America/Los_Angeles' \
    --location us-central1

停止ジョブを作成する

gcloud scheduler jobs create pubsub shutdown-dev-instances \
    --schedule '0 17 * * 1-5' \
    --topic stop-instance-event \
    --message-body '{"zone":"us-west1-b", "label":"env=dev"}' \
    --time-zone 'America/Los_Angeles' \
    --location us-central1

(省略可)ジョブが動作することを確認する

Console

インスタンスを停止する

  1. Google Cloud コンソールの [Cloud Scheduler] ページに移動します。
    [Cloud Scheduler] ページに移動
  2. shutdown-dev-instancesという名前のジョブで、ページの右端にある [今すぐ実行]ボタンをクリックします。
  3. Google Cloud コンソールの [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動
  4. dev-instance という名前のインスタンスの横に灰色の四角が表示されていることを確認します。これは、停止中であることを示しています。シャットダウンが完了するまでに最長で 30 秒かかります。

インスタンスを起動する

  1. Google Cloud コンソールの [Cloud Scheduler] ページに移動します。
    [Cloud Scheduler] ページに移動
  2. startup-dev-instancesという名前のジョブで、ページの右端にある [今すぐ実行]ボタンをクリックします。
  3. Google Cloud コンソールの [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動
  4. dev-instanceという名前のインスタンスの名前の横に緑色のチェックマークが表示されていることを確認します。これは実行中であることを湿しています。起動処理が完了するまでに最長で 30 秒かかります。

gcloud

インスタンスを停止する

  1. スケジューラ ジョブを実行してインスタンスを停止します。

    gcloud beta scheduler jobs run shutdown-dev-instances
    
  2. インスタンスのステータスが TERMINATED であることを確認します。シャットダウンが完了するまでに最長で 30 秒かかります。

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: TERMINATED
    

インスタンスを起動する

  1. スケジューラ ジョブを実行してインスタンスを起動します。

    gcloud beta scheduler jobs run startup-dev-instances
    
  2. インスタンスのステータスが RUNNING であることを確認します。起動処理が完了するまでに最長で 30 秒かかります。

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: RUNNING
    

クリーンアップ

チュートリアルが終了したら、作成したリソースをクリーンアップして、割り当ての使用を停止し、課金されないようにできます。次のセクションで、リソースを削除または無効にする方法を説明します。

Cloud Scheduler ジョブの削除

  1. Google Cloud コンソールの [Cloud Scheduler] ページに移動します。

    [Cloud Scheduler] ページに移動

  2. ジョブの横にあるチェックボックスをクリックします。

  3. ページの上部にある [削除] ボタンをクリックして、削除操作を確定します。

Pub/Sub トピックの削除

  1. Google Cloud コンソールの [Pub/Sub] ページに移動します。

    [Pub/Sub] ページに移動

  2. トピックの横にあるチェックボックスをクリックします。

  3. ページの上部にある [削除] をクリックして、削除操作を確定します。

Cloud Functions を介してデプロイされた関数を削除する

  1. Google Cloud コンソールの [Cloud Functions] ページに移動します。

    [Cloud Functions] ページに移動

  2. 関数の横にあるチェックボックスをクリックします。

  3. ページの上部にある [削除] ボタンをクリックして、削除操作を確定します。

Compute Engine インスタンスの削除

Compute Engine インスタンスを削除するには:

  1. Google Cloud コンソールで、[VM インスタンス] ページに移動します。

    [VM インスタンス] に移動

  2. 削除するインスタンスのチェックボックスを選択します。
  3. インスタンスを削除するには、 [その他の操作] をクリックし、[削除] をクリックしてから、指示に沿って操作します。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

次のステップ