Cloud Tasks を使用して Cloud Run 関数をトリガーする


このチュートリアルでは、App Engine アプリケーション内で Cloud Tasks を使用して Cloud Run 関数をトリガーし、スケジュールされたメールを送信する方法について説明します。

目標

  • 各コンポーネントのコードを理解する。
  • SendGrid アカウントを作成する。
  • ソースコードをダウンロードする。
  • Cloud Tasks リクエストを受信し、SendGrid API を介してメールを送信する Cloud Run 関数をデプロイする。
  • Cloud Tasks キューを作成する。
  • Cloud Tasks リクエストを認証するためのサービスアカウントを作成する。
  • ユーザーがメールを送信できるようにクライアントコードをデプロイする。

費用

Cloud Tasks、Cloud Run 関数、App Engine には無料枠があるため、無料枠でチュートリアルを実行する限り、追加の費用は発生しません。詳細については、料金をご覧ください。

始める前に

  1. Google Cloud プロジェクトを選択または作成します。

    [App Engine] ページに移動

  2. プロジェクトで App Engine アプリケーションを初期化します。

    1. [App Engine へようこそ] ページで [アプリケーションを作成] をクリックします。

    2. アプリケーションのリージョンを選択します。このロケーションは、Cloud Tasks リクエストの LOCATION_ID パラメータとして使用するため、メモしておいてください。App Engine コマンドで europe-west および us-central と呼ばれる 2 つのロケーションは、Cloud Tasks コマンドではそれぞれ europe-west1 および us-central1 と呼ばれます。

    3. 言語として [Node.js] を選択し、環境として [Standard] を選択します。

    4. [課金を有効にする] ポップアップが表示されたら、請求先アカウントを選択します。まだ請求先アカウントがない場合は、[請求先アカウントの作成] をクリックしてウィザードの手順を行います。

    5. [開始] ページで [次へ] をクリックします。他のことは後で対応します。

  3. Cloud Run 関数と Cloud Tasks APIs を有効にします。

    API を有効にする

  4. gcloud CLI をインストールして初期化する

コードについて

このセクションでは、アプリのコードとその動作を順を追って説明します。

タスクの作成

インデックス ページは、app.yaml のハンドラを使用して提供されます。タスクの作成に必要な変数が、環境変数として渡されます。

runtime: nodejs16

env_variables:
  QUEUE_NAME: "my-queue"
  QUEUE_LOCATION: "us-central1"
  FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
  SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

# Handlers for serving the index page.
handlers:
  - url: /static
    static_dir: static
  - url: /
    static_files: index.html
    upload: index.html

このコードにより、エンドポイント /send-email が作成されます。このエンドポイントは、インデックス ページからのフォーム送信を処理し、そのデータをタスク作成コードに渡します。

app.post('/send-email', (req, res) => {
  // Set the task payload to the form submission.
  const {to_name, from_name, to_email, date} = req.body;
  const payload = {to_name, from_name, to_email};

  createHttpTaskWithToken(
    process.env.GOOGLE_CLOUD_PROJECT,
    QUEUE_NAME,
    QUEUE_LOCATION,
    FUNCTION_URL,
    SERVICE_ACCOUNT_EMAIL,
    payload,
    date
  );

  res.status(202).send('📫 Your postcard is in the mail! 💌');
});

このコードでは実際にタスクが作成され、Cloud Tasks キューに送信されます。コードは、次の方法でタスクを構築します。

  • ターゲット タイプHTTP Request に指定します。

  • 使用する HTTP method とターゲットの URL を指定します。

  • ダウンストリーム アプリケーションが構造化ペイロードを解析できるように、Content-Typeヘッダーをapplication/jsonに設定します。

  • 認証を要求しているリクエスト ターゲットに Cloud Tasks が認証情報を提供できるように、サービス アカウントのメールアドレスを追加します。サービス アカウントは個別に作成されます。

  • 日付のユーザー入力が最大 30 日以内であることを確認し、フィールド scheduleTime としてリクエストに追加します。

const MAX_SCHEDULE_LIMIT = 30 * 60 * 60 * 24; // Represents 30 days in seconds.

const createHttpTaskWithToken = async function (
  project = 'my-project-id', // Your GCP Project id
  queue = 'my-queue', // Name of your Queue
  location = 'us-central1', // The GCP region of your queue
  url = 'https://example.com/taskhandler', // The full url path that the request will be sent to
  email = '<member>@<project-id>.iam.gserviceaccount.com', // Cloud IAM service account
  payload = 'Hello, World!', // The task HTTP request body
  date = new Date() // Intended date to schedule task
) {
  // Imports the Google Cloud Tasks library.
  const {v2beta3} = require('@google-cloud/tasks');

  // Instantiates a client.
  const client = new v2beta3.CloudTasksClient();

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  // Convert message to buffer.
  const convertedPayload = JSON.stringify(payload);
  const body = Buffer.from(convertedPayload).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
        audience: url,
      },
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    },
  };

  const convertedDate = new Date(date);
  const currentDate = new Date();

  // Schedule time can not be in the past.
  if (convertedDate < currentDate) {
    console.error('Scheduled date in the past.');
  } else if (convertedDate > currentDate) {
    const date_diff_in_seconds = (convertedDate - currentDate) / 1000;
    // Restrict schedule time to the 30 day maximum.
    if (date_diff_in_seconds > MAX_SCHEDULE_LIMIT) {
      console.error('Schedule time is over 30 day maximum.');
    }
    // Construct future date in Unix time.
    const date_in_seconds =
      Math.min(date_diff_in_seconds, MAX_SCHEDULE_LIMIT) + Date.now() / 1000;
    // Add schedule time to request in Unix time using Timestamp structure.
    // https://googleapis.dev/nodejs/tasks/latest/google.protobuf.html#.Timestamp
    task.scheduleTime = {
      seconds: date_in_seconds,
    };
  }

  try {
    // Send create task request.
    const [response] = await client.createTask({parent, task});
    console.log(`Created task ${response.name}`);
    return response.name;
  } catch (error) {
    // Construct error for Stackdriver Error Reporting
    console.error(Error(error.message));
  }
};

module.exports = createHttpTaskWithToken;

メールの作成

このコードは、Cloud Tasks リクエストのターゲットである Cloud Run 関数を作成します。リクエスト本文を使用してメールを作成し、SendGrid API を介して送信します。

const sendgrid = require('@sendgrid/mail');

/**
 * Responds to an HTTP request from Cloud Tasks and sends an email using data
 * from the request body.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request payload.
 * @param {string} req.body.to_email Email address of the recipient.
 * @param {string} req.body.to_name Name of the recipient.
 * @param {string} req.body.from_name Name of the sender.
 * @param {object} res Cloud Function response context.
 */
exports.sendEmail = async (req, res) => {
  // Get the SendGrid API key from the environment variable.
  const key = process.env.SENDGRID_API_KEY;
  if (!key) {
    const error = new Error(
      'SENDGRID_API_KEY was not provided as environment variable.'
    );
    error.code = 401;
    throw error;
  }
  sendgrid.setApiKey(key);

  // Get the body from the Cloud Task request.
  const {to_email, to_name, from_name} = req.body;
  if (!to_email) {
    const error = new Error('Email address not provided.');
    error.code = 400;
    throw error;
  } else if (!to_name) {
    const error = new Error('Recipient name not provided.');
    error.code = 400;
    throw error;
  } else if (!from_name) {
    const error = new Error('Sender name not provided.');
    error.code = 400;
    throw error;
  }

  // Construct the email request.
  const msg = {
    to: to_email,
    from: 'postcard@example.com',
    subject: 'A Postcard Just for You!',
    html: postcardHTML(to_name, from_name),
  };

  try {
    await sendgrid.send(msg);
    // Send OK to Cloud Task queue to delete task.
    res.status(200).send('Postcard Sent!');
  } catch (error) {
    // Any status code other than 2xx or 503 will trigger the task to retry.
    res.status(error.code).send(error.message);
  }
};

アプリケーションの準備

SendGrid の設定

  1. SendGrid アカウントを作成する。

  2. SendGrid API キーを作成します。

    1. SendGrid アカウントにログインします。

    2. 左側のナビゲーションで、[設定] を開き、[API キー] をクリックします。

    3. [Create API Key] をクリックし、制限付きアクセスを選択します。[メール送信] ヘッダーで、[フルアクセス] を選択します。

    4. 表示された API キーをコピーします(このキーは 1 回だけ表示されます。後で使用できるように別の場所に貼り付けてください)。

ソースコードのダウンロード

  1. ローカルマシンにサンプルアプリのレポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. サンプルコードが入っているディレクトリに移動します。

    cd cloud-tasks/
    

Cloud Run 関数のデプロイ

  1. function/ ディレクトリに移動します。

    cd function/
    
  2. 関数をデプロイします。

    gcloud functions deploy sendEmail --runtime nodejs14 --trigger-http \
      --no-allow-unauthenticated \
      --set-env-vars SENDGRID_API_KEY=SENDGRID_API_KEY \

    SENDGRID_API_KEY を API キーに置き換えます。

    このコマンドでは、次のフラグを使用します。

    • --trigger-http は Cloud Run 関数のトリガータイプを指定します。

    • --no-allow-unauthenticated: 関数の呼び出しに認証が必要であることを指定します。

    • --set-env-var は SendGrid 認証情報を設定します。

  3. 認証されたユーザーだけを許可するように、関数へのアクセス制御を設定します。

    1. Cloud Run 関数 UIsendEmail 関数を選択します。

    2. sendEmail の権限情報が表示されない場合は、右上隅にある [情報パネルを表示] をクリックします。

    3. 上の [プリンシパルを追加] ボタンをクリックします。

    4. [新しいプリンシパル] を allAuthenticatedUsers に設定します。

    5. [ロール] を設定します。

      • 第 1 世代の関数: ロールを Cloud Function Invoker に設定します。
      • 第 2 世代の関数: ロールを Cloud Run Invoker に設定します。
    6. [保存] をクリックします。

Cloud Tasks キューの作成

  1. 次の gcloud コマンドを使用して、キューを作成します。

    gcloud tasks queues create my-queue --location=LOCATION

    LOCATION は、キューの目的のロケーションus-west2 など)に置き換えます。ロケーションを指定しない場合は、gcloud CLI によってデフォルトが選択されます。

  2. 正常に作成されたことを確認します。

    gcloud tasks queues describe my-queue --location=LOCATION

    LOCATION は、キューのロケーションに置き換えます。

サービス アカウントの作成

Cloud Tasks リクエストでは、Cloud Run 関数がリクエストを認証するための認証情報を Authorization ヘッダーで指定する必要があります。このサービス アカウントで、その目的のために Cloud Tasks で OIDC トークンを作成して追加できます。

  1. [サービス アカウント] ページで、[サービス アカウントを作成] をクリックします。

  2. サービス アカウント名(わかりやすい表示名)を追加し、[作成] を選択します。

  3. [ロール] を設定し、[続行] をクリックします。

    • 第 1 世代の関数: ロールを Cloud Function Invoker に設定します。
    • 第 2 世代の関数: ロールを Cloud Run Invoker に設定します。
  4. [完了] を選択します。

エンドポイントとタスク作成者の App Engine へのデプロイ

  1. app/ ディレクトリに移動します。

    cd ../app/
    
  2. app.yaml の変数を実際の値で更新します。

    env_variables:
      QUEUE_NAME: "my-queue"
      QUEUE_LOCATION: "us-central1"
      FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
      SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

    キューのロケーションを探すには、次のコマンドを使用します。

    gcloud tasks queues describe my-queue --location=LOCATION

    LOCATION は、キューのロケーションに置き換えます。

    関数の URL を探すには、次のコマンドを使用します。

    gcloud functions describe sendEmail
  3. 次のコマンドを使用して、アプリケーションを App Engine スタンダード環境にデプロイします。

    gcloud app deploy
  4. アプリケーションを開き、ポストカードをメールで送信します。

    gcloud app browse

クリーンアップ

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

リソースの削除

Google Cloud で作成したリソースをクリーンアップして、今後料金が発生しないようにします。次のセクションで、このようなリソースを削除または無効にする方法を説明します。

Cloud Run 関数の削除

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

    [Cloud Run 関数] ページに移動します。

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

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

Cloud Tasks キューの削除

  1. Console で Cloud Tasks キューのページを開きます。

    Cloud Tasks キューのページに移動

  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.

次のステップ