Cloud Scheduler を使用した Compute インスタンスのスケジュール設定

このチュートリアルでは、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 コンポーネントを使用します。

  1. Compute Engine インスタンス: スケジュールに従って実行される Compute Engine インスタンス。
  2. Cloud Functions 関数: スケジュールに従ってインスタンスを起動および停止する関数。
  3. Pub/Sub メッセージ: 起動イベントおよび停止イベントごとに送受信されるメッセージ。
  4. Cloud Scheduler ジョブ: スケジュールに従ってインスタンスの起動と停止の呼び出しを行うジョブ。

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

ロケーションの要件

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

  1. Compute Engine インスタンス: Compute Engine のリージョンとゾーンにリストされているすべてのリージョンでサポートされます。
  2. Cloud Functions ファンクション: Cloud Functions ロケーションとしてリストされているリージョンでサポートされます。
  3. Pub/Sub メッセージ: Pub/Sub はグローバル サービスであるため、グローバルでサポートされます。
  4. Cloud Scheduler ジョブ: 現在のすべての App Engine ロケーションでサポートされます。

おすすめの方法: HTTP ではなく Pub/Sub を使用する理由

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

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

HTTP 経由でスケジュールした場合、誰でも Compute Engine インスタンスのスケジュールを設定できることを示すシステム アーキテクチャ図

より安全な設定のために、Pub/Sub 関数を使用することをおすすめします。

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

コンソール

  1. Cloud Console の [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動
  2. [インスタンスを作成] をクリックします。
  3. [名前] を dev-instance に設定します。
  4. [リージョン] で、us-west1 を選択します。
  5. [ゾーン] で、[us-west1-b] を選択します。
  6. [管理、セキュリティ、ディスク、ネットワーク、単一テナンシー] セクションを展開します。
  7. [管理] で [ラベルを追加] をクリックします。[キー] に「env」、[] に「dev」と入力します。
  8. ページの下部にある [作成] をクリックします。

gcloud

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

Pub/Sub を使用して Cloud Functions 関数を設定する

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

Console

起動の関数を作成する

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

    const Compute = require('@google-cloud/compute');
    const compute = new Compute();
    
    /**
     * 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 payload = _validatePayload(
          JSON.parse(Buffer.from(event.data, 'base64').toString())
        );
        const options = {filter: `labels.${payload.label}`};
        const [vms] = await compute.getVMs(options);
        await Promise.all(
          vms.map(async (instance) => {
            if (payload.zone === instance.zone.id) {
              const [operation] = await compute
                .zone(payload.zone)
                .vm(instance.name)
                .start();
    
              // Operation pending
              return operation.promise();
            }
          })
        );
    
        // 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 = (payload) => {
      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;
    };
  14. コードエディタの左側にある [package.json] を選択します。

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

    {
      "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": ">=10.0.0"
      },
      "scripts": {
        "test": "mocha test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "mocha": "^8.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^9.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^2.0.0"
      }
    }
    
  16. ページの下部にある [Deploy] をクリックします。

停止の関数を作成する

  1. Cloud Console にCloud Functionsページが表示されているはずです。
  2. [関数を作成] をクリックします。
  3. [関数名] を stopInstancePubSub に設定します。
  4. [リージョン] はデフォルト値のままにします。
  5. [割り当てられるメモリ] はデフォルト値のままにします。
  6. [トリガーのタイプ] で [Cloud Pub/Sub] を選択します。
  7. [Cloud Pub/Sub トピックを選択してください] で、[Create a topic...] を選択します。
  8. [新しい pub/sub トピック] ダイアログ ボックスが表示されます。
    1. [トピック ID] に「stop-instance-event」と入力します。
    2. [トピックを作成] をクリックしてダイアログ ボックスを閉じます。
  9. [トリガー] ボックスの下部にある [保存] をクリックします。
  10. ページの下部にある [次へ] をクリックします。
  11. [ランタイム]で [Node.js 10]を選択します。
  12. [エントリ ポイント] に「stopInstancePubSub」と入力します。
  13. コードエディタの左側にある [index.js] を選択します。
  14. スターター コードを次のコードに置き換えます。

    const Compute = require('@google-cloud/compute');
    const compute = new Compute();
    
    /**
     * 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 payload = _validatePayload(
          JSON.parse(Buffer.from(event.data, 'base64').toString())
        );
        const options = {filter: `labels.${payload.label}`};
        const [vms] = await compute.getVMs(options);
        await Promise.all(
          vms.map(async (instance) => {
            if (payload.zone === instance.zone.id) {
              const [operation] = await compute
                .zone(payload.zone)
                .vm(instance.name)
                .stop();
    
              // Operation pending
              return operation.promise();
            } else {
              return Promise.resolve();
            }
          })
        );
    
        // 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 = (payload) => {
      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": ">=10.0.0"
      },
      "scripts": {
        "test": "mocha test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "mocha": "^8.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^9.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^2.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 nodejs10 \
    --allow-unauthenticated
gcloud functions deploy stopInstancePubSub \
    --trigger-topic stop-instance-event \
    --runtime nodejs10 \
    --allow-unauthenticated

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

Console

インスタンスを停止する

  1. Cloud Console の [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. Cloud Console の [VM インスタンス] ページに移動します。
    [VM インスタンス] ページに移動

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

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

インスタンスを起動する

  1. Cloud Console の [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. Cloud Console の [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. Cloud Console の [Cloud Scheduler] ページに移動します。
    [Cloud Scheduler] ページに移動
  2. [ジョブを作成] をクリックします。
  3. [リージョン] はデフォルト値のままにして、ページの下部にある [次へ] をクリックします。
  4. [名前] を startup-dev-instances に設定します。
  5. [頻度] に「0 9 * * 1-5」と入力します。
    • 毎週平日午前 9 時に実行されます。
  6. [タイムゾーン] で、目的の国とタイムゾーンを選択します。この例では、United StatesLos Angeles を使用します。
  7. [ターゲット] で、[Pub/Sub] を選択します。
  8. [トピック] に「start-instance-event」と入力します。
  9. [ペイロード] に、次のテキストを入力します。
    {"zone":"us-west1-b","label":"env=dev"}
    
  10. [作成] をクリックします。

停止ジョブを作成する

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

gcloud

起動ジョブを作成する

gcloud beta 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'

停止ジョブを作成する

gcloud beta 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'

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

Console

インスタンスを停止する

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

インスタンスを起動する

  1. Cloud Console の [Cloud Scheduler] ページに移動します。
    [Cloud Scheduler] ページに移動
  2. startup-dev-instancesという名前のジョブで、ページの右端にある [今すぐ実行]ボタンをクリックします。
  3. Cloud Console の [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
    

クリーンアップ

インスタンススケジューリングのチュートリアルが終了したら、Google Cloud で作成したリソースをクリーンアップして、割り当てを消費したり、将来的に課金されたりしないようにすることができます。次のセクションで、リソースを削除または無効にする方法を説明します。

Cloud Scheduler ジョブの削除

  1. Cloud Console の [Cloud Scheduler] ページに移動します。

    [Cloud Scheduler] ページに移動

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

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

Pub/Sub トピックの削除

  1. Cloud Console の [Pub/Sub] ページに移動します。

    [Pub/Sub] ページに移動

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

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

Cloud Functions 関数の削除

  1. Cloud Console の [Cloud Functions] ページに移動します。

    [Cloud Functions] ページに移動

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

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

Compute Engine インスタンスの削除

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

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

    [VM インスタンス] ページに移動

  2. 削除するインスタンスをクリックします。
  3. [削除] をクリックしてインスタンスを削除します。

プロジェクトの削除

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

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

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

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

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

次のステップ