Node.js Canary スクリプトの記述 - Amazon CloudWatch

Node.js Canary スクリプトの記述

CloudWatch Synthetics Canary を最初から作成する

次の例は、Synthetics の最小の Canary スクリプトを示しています。このスクリプトは、合格して正常な実行となり、文字列を返します。不合格となる Canary の例を確認するには、let fail = false;let fail = true; に変更します。

Canary スクリプトのエントリポイント関数を定義する必要があります。Canary の ArtifactS3Location として指定した先の Amazon S3 にファイルがアップロードされる方法を確認するには、これらのファイルを /tmp フォルダの下に作成します。スクリプトを実行すると、合格/不合格のステータスと所要時間のメトリクスが CloudWatch に発行され、/tmp の下のファイルが S3 にアップロードされます。

const basicCustomEntryPoint = async function () { // Insert your code here // Perform multi-step pass/fail check // Log decisions made and results to /tmp // Be sure to wait for all your code paths to complete // before returning control back to Synthetics. // In that way, your canary will not finish and report success // before your code has finished executing // Throw to fail, return to succeed let fail = false; if (fail) { throw "Failed basicCanary check."; } return "Successfully completed basicCanary checks."; }; exports.handler = async () => { return await basicCustomEntryPoint(); };

次に、AWS SDK を使用して、Synthetics のログ記録を使用して呼び出しを行うようにスクリプトが拡張されます。デモの目的に限り、このスクリプトは Amazon DynamoDB クライアントを作成し、DynamoDB listTables API を呼び出します。リクエストに対するレスポンスを記録し、リクエストが成功したかどうかに応じて合格または不合格を記録します。

複数の .js ファイルがある場合や、スクリプトに依存関係がある場合は、それらのすべてを単一の ZIP ファイル (フォルダ構造は nodejs/node_modules/myCanaryFilename.js file and other folders and files) にバンドルできます。

スクリプトのエントリポイントのファイル名に一致するように、Canary のスクリプトのエントリポイントを myCanaryFilename.handler として設定します。

const log = require('SyntheticsLogger'); const AWS = require('aws-sdk'); // Require any dependencies that your script needs // Bundle additional files and dependencies into a .zip file with folder structure // nodejs/node_modules/additional files and folders const basicCustomEntryPoint = async function () { log.info("Starting DynamoDB:listTables canary."); let dynamodb = new AWS.DynamoDB(); var params = {}; let request = await dynamodb.listTables(params); try { let response = await request.promise(); log.info("listTables response: " + JSON.stringify(response)); } catch (err) { log.error("listTables error: " + JSON.stringify(err), err.stack); throw err; } return "Successfully completed DynamoDB:listTables canary."; }; exports.handler = async () => { return await basicCustomEntryPoint(); };

既存の Puppeteer スクリプトを変更して Synthetics の Canary として使用する

このセクションでは、Puppeteer スクリプトを変更して Synthetics の Canary スクリプトとして実行する方法について説明します。Puppeteer の詳細については、「Puppeteer API v1.14.0」を参照してください。

まず、次の Puppeteer スクリプトの例から始めます。

const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com'); await page.screenshot({path: 'example.png'}); await browser.close(); })();

変更の手順は次のとおりです。

  • handler 関数を作成してエクスポートします。このハンドラーは、スクリプトのエントリポイント関数です。

    const basicPuppeteerExample = async function () {}; exports.handler = async () => { return await basicPuppeteerExample(); };
  • Synthetics 依存関係を使用します。

    var synthetics = require('Synthetics');
  • Puppeteer の Synthetics.getPage オブジェクトを取得するには、Page 関数を使用します。

    const page = await synthetics.getPage();

    Synthetics.getPage 関数から返されるページオブジェクトには、ログ記録用にインストルメント化された page.onrequestresponse、および requestfailed の各イベントがあります。また、Synthetics は、ページのリクエストおよびレスポンス用の HAR ファイルの生成を設定し、ページの送信リクエストのユーザーエージェントヘッダーに Canary ARN を追加します。

これで、スクリプトを Synthetics の Canary として実行できるようになりました。更新されたスクリプトは次のとおりです。

var synthetics = require('Synthetics'); // Synthetics dependency const basicPuppeteerExample = async function () { const page = await synthetics.getPage(); // Get instrumented page from Synthetics await page.goto('https://example.com'); await page.screenshot({path: '/tmp/example.png'}); // Write screenshot to /tmp folder }; exports.handler = async () => { // Exported handler function return await basicPuppeteerExample(); };

環境変数

Canary を作成する際に環境変数を使用できます。これにより、単一の Canary スクリプトを記述し、そのスクリプトを異なる値で使用して、同様のタスクを持つ複数の Canary をすばやく作成できます。

例えば、組織が、ソフトウェア開発のさまざまな段階向けに proddevpre-release などのエンドポイントを有しており、これらの各エンドポイントをテストするために Canary を作成する必要があるとします。ソフトウェアをテストする 1 つの Canary スクリプトを記述し、3 つの Canary をそれぞれ作成するときに、エンドポイント環境変数に異なる値を指定できます。その後、Canary を作成するときに、環境変数に使用するスクリプトと値を指定します。

環境変数の名前には、文字、数字、およびアンダースコアを使用できます。文字で始まり、少なくとも 2 文字である必要があります。環境変数の合計サイズは 4 KB を超えることはできません。Lambda の予約済み環境変数を環境変数の名前として指定することはできません。予約済み環境変数の詳細については、「ランタイム環境変数」をご参照ください。

次のスクリプト例では、2 つの環境変数を使用しています。このスクリプトは、ウェブページが利用可能かどうかをチェックする Canary 用です。環境変数を使用して、チェックする URL と、使用する CloudWatch Synthetics ログレベルの両方をパラメータ化します。

次の関数は、LogLevelLOG_LEVEL 環境変数の値に設定します。

synthetics.setLogLevel(process.env.LOG_LEVEL);

この関数は、URLURL 環境変数の値に設定します。

const URL = process.env.URL;

これは完全なスクリプトです。このスクリプトを使用して Canary を作成するときは、LOG_LEVEL および URL 環境変数の値を指定します。

var synthetics = require('Synthetics'); const log = require('SyntheticsLogger'); const pageLoadEnvironmentVariable = async function () { // Setting the log level (0-3) synthetics.setLogLevel(process.env.LOG_LEVEL); // INSERT URL here const URL = process.env.URL; let page = await synthetics.getPage(); //You can customize the wait condition here. For instance, //using 'networkidle2' may be less restrictive. const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000}); if (!response) { throw "Failed to load page!"; } //Wait for page to render. //Increase or decrease wait time based on endpoint being monitored. await page.waitFor(15000); await synthetics.takeScreenshot('loaded', 'loaded'); let pageTitle = await page.title(); log.info('Page title: ' + pageTitle); log.debug('Environment variable:' + process.env.URL); //If the response status code is not a 2xx success code if (response.status() < 200 || response.status() > 299) { throw "Failed to load page!"; } }; exports.handler = async () => { return await pageLoadEnvironmentVariable(); };

環境変数をスクリプトに渡す

コンソールで Canary を作成するときに環境変数をスクリプトに渡すには、コンソールの [Environment variables] (環境変数) セクションで環境変数のキーと値を指定します。詳細については、「」を参照してくださいCanary を作成する

API または AWS CLI を介して環境変数を渡すには、EnvironmentVariables セクションの RunConfig パラメータを使用します。以下は、キー AWS CLI とキー Environment を持つ 2 つの環境変数を使用する Canary を作成する Region コマンドの例です。

aws synthetics create-canary --cli-input-json '{ "Name":"nameofCanary", "ExecutionRoleArn":"roleArn", "ArtifactS3Location":"s3://cw-syn-results-123456789012-us-west-2", "Schedule":{ "Expression":"rate(0 minute)", "DurationInSeconds":604800 }, "Code":{ "S3Bucket": "canarycreation", "S3Key": "cwsyn-mycanaryheartbeat-12345678-d1bd-1234-abcd-123456789012-12345678-6a1f-47c3-b291-123456789012.zip", "Handler":"pageLoadBlueprint.handler" }, "RunConfig": { "TimeoutInSeconds":60, "EnvironmentVariables": { "Environment":"Production", "Region": "us-west-1" } }, "SuccessRetentionPeriodInDays":13, "FailureRetentionPeriodInDays":13, "RuntimeVersion":"syn-nodejs-2.0" }'

Canary と他の AWS のサービスとの統合

すべての Canary では AWS SDK ライブラリを使用できます。このライブラリを Canary の作成時に使用すると、Canary を他の AWS のサービスと統合できます。

これを行うには、Canary に次のコードを追加する必要があります。AWSこれらの例では、Canary を統合するサービスとして AWS Secrets Manager が使用されます。

  • AWS SDK をインポートします。

    const AWS = require('aws-sdk');
  • 統合する AWS のサービスのクライアントを作成します。

    const secretsManager = new AWS.SecretsManager();
  • このクライアントを使用して、サービスへの API コールを行います。

    var params = { SecretId: secretName }; return await secretsManager.getSecretValue(params).promise();

次の Canary スクリプトのコードスニペットは、Secrets Manager との統合例をより詳細に示しています。

var synthetics = require('Synthetics'); const log = require('SyntheticsLogger'); const AWS = require('aws-sdk'); const secretsManager = new AWS.SecretsManager(); const getSecrets = async (secretName) => { var params = { SecretId: secretName }; return await secretsManager.getSecretValue(params).promise(); } const secretsExample = async function () { let URL = "<URL>"; let page = await synthetics.getPage(); log.info(`Navigating to URL: ${URL}`); const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000}); // Fetch secrets let secrets = await getSecrets("secretname") /** * Use secrets to login. * * Assuming secrets are stored in a JSON format like: * { * "username": "<USERNAME>", * "password": "<PASSWORD>" * } **/ let secretsObj = JSON.parse(secrets.SecretString); await synthetics.executeStep('login', async function () { await page.type(">USERNAME-INPUT-SELECTOR<", secretsObj.username); await page.type(">PASSWORD-INPUT-SELECTOR<", secretsObj.password); await Promise.all([ page.waitForNavigation({ timeout: 30000 }), await page.click(">SUBMIT-BUTTON-SELECTOR<") ]); }); // Verify login was successful await synthetics.executeStep('verify', async function () { await page.waitForXPath(">SELECTOR<", { timeout: 30000 }); }); }; exports.handler = async () => { return await secretsExample(); };

Canary に静的 IP アドレスの使用を強制する

静的 IP アドレスを使用するように Canary を設定できます。

Canary に静的 IP アドレスの使用を強制するには

  1. 新しい VPC を作成します。詳細については、「VPC での DNS の使用」を参照してください。

  2. 新しいインターネットゲートウェイを作成します。詳細については、「インターネットゲートウェイを VPC に追加する」を参照してください。

  3. 新しい VPC 内にパブリックサブネットを作成します。

  4. 新しいルートテーブルを VPC に追加します。

  5. 0.0.0.0/0 からインターネットゲートウェイに向かうルートを、新しいルートテーブルに追加します。

  6. 新しいルートテーブルをパブリックサブネットに関連付けます。

  7. Elastic IP アドレスを作成します。詳細については、「Elastic IP アドレス」を参照してください。

  8. 新しい NAT ゲートウェイを作成し、パブリックサブネットと Elastic IP アドレスに割り当てます。

  9. VPC の内部にプライベートサブネットを作成します。

  10. 0.0.0.0/0 から NAT ゲートウェイへのルートを VPC デフォルトルートテーブルに追加する

  11. Canary を作成します。