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 を呼び出します。リクエストに対するレスポンスを記録し、リクエストが成功したかどうかに応じて合格または不合格を記録します。
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(); };
Node.js Canary ファイルのパッケージング
Amazon S3 の場所を使用して Canary スクリプトをアップロードする場合、zip ファイルにはフォルダ構造 nodejs/node_modules/
にスクリプトが含まれている必要があります。myCanaryFilename.js file
複数の .js
ファイルがある場合や、スクリプトに依存関係がある場合は、それらのすべてを単一の ZIP ファイル (フォルダ構造は nodejs/node_modules/
) にバンドルできます。myCanaryFilename.js file and other folders and files
syn-nodejs-puppeteer-3.4
以降を使用している場合は、オプションで Canary ファイルを別のフォルダーに配置し、次のようなフォルダー構造を作成できます: nodejs/node_modules/
。myFolder
/myCanaryFilename.js file and other folders and files
ハンドラー名
スクリプトのエントリポイント (ハンドラー) のファイル名に一致するように、Canary のスクリプトのエントリポイントを myCanaryFilename.functionName
として設定します。syn-nodejs-puppeteer-3.4
より前のランタイムを使用している場合は、functionName
は handler
である必要があります。syn-nodejs-puppeteer-3.4
以降を使用している場合、ハンドラーとして任意の関数名を選択できます。syn-nodejs-puppeteer-3.4
以降を使用している場合、オプションで Canary を nodejs/node_modules/myFolder/my_canary_filename
などの別のフォルダに保存することもできます。別のフォルダに保存する場合は、スクリプトエントリポイントでそのパスを指定します (myFolder/my_canary_filename.functionName
など)。
既存の 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
関数を作成してエクスポートします。このハンドラーは、スクリプトのエントリポイント関数です。syn-nodejs-puppeteer-3.4
より前のランタイムを使用している場合、ハンドラー関数にはhandler
という名前を付ける必要があります。syn-nodejs-puppeteer-3.4
以降を使用している場合、関数には任意の名前を付けることができますが、スクリプトで使用されている名前と同じである必要があります。また、syn-nodejs-puppeteer-3.4
以降を使用している場合は、スクリプトを任意のフォルダに保存し、そのフォルダをハンドラー名の一部として指定できます。const basicPuppeteerExample = async function () {}; exports.handler = async () => { return await basicPuppeteerExample(); };
Synthetics
依存関係を使用します。var synthetics = require('Synthetics');
Puppeteer の
Page
オブジェクトを取得するには、Synthetics.getPage
関数を使用します。const page = await synthetics.getPage();
Synthetics.getPage 関数から返されるページオブジェクトには、ログ記録用にインストルメント化された page.on、
request
、response
、および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 をすばやく作成できます。
例えば、組織が、ソフトウェア開発のさまざまな段階向けに prod
、dev
、pre-release
などのエンドポイントを有しており、これらの各エンドポイントをテストするために Canary を作成する必要があるとします。ソフトウェアをテストする 1 つの Canary スクリプトを記述し、3 つの Canary をそれぞれ作成するときに、エンドポイント環境変数に異なる値を指定できます。その後、Canary を作成するときに、環境変数に使用するスクリプトと値を指定します。
環境変数の名前には、文字、数字、およびアンダースコアを使用できます。文字で始まり、少なくとも 2 文字である必要があります。環境変数の合計サイズは 4 KB を超えることはできません。Lambda の予約済み環境変数を環境変数の名前として指定することはできません。予約済み環境変数の詳細については、「ランタイム環境変数」をご参照ください。
重要
環境変数のキーと値は暗号化されません。機密情報は保存しないでください。
次のスクリプト例では、2 つの環境変数を使用しています。このスクリプトは、ウェブページが利用可能かどうかをチェックする Canary 用です。環境変数を使用して、チェックする URL と、使用する CloudWatch Synthetics ログレベルの両方をパラメータ化します。
次の関数は、LogLevel
を LOG_LEVEL
環境変数の値に設定します。
synthetics.setLogLevel(process.env.LOG_LEVEL);
この関数は、URL
を URL
環境変数の値に設定します。
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 を介して環境変数を渡すには、RunConfig
セクションの EnvironmentVariables
パラメータを使用します。以下は、キー Environment
とキー Region
を持つ 2 つの環境変数を使用する Canary を作成する AWS CLI コマンドの例です。
aws synthetics create-canary --cli-input-json '{ "Name":"nameofCanary", "ExecutionRoleArn":"roleArn", "ArtifactS3Location":"s3://amzn-s3-demo-bucket-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 に次のコードを追加する必要があります。これらの例では、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 アドレスの使用を強制するには
新しい VPC を作成します。詳細については、「VPC での DNS の使用」を参照してください。
新しいインターネットゲートウェイを作成します。詳細については、「インターネットゲートウェイを VPC に追加する」を参照してください。
新しい VPC 内にパブリックサブネットを作成します。
新しいルートテーブルを VPC に追加します。
0.0.0.0/0
からインターネットゲートウェイに向かうルートを、新しいルートテーブルに追加します。新しいルートテーブルをパブリックサブネットに関連付けます。
Elastic IP アドレスを作成します。詳細については、「Elastic IP アドレス」を参照してください。
新しい NAT ゲートウェイを作成し、パブリックサブネットと Elastic IP アドレスに割り当てます。
VPC の内部にプライベートサブネットを作成します。
0.0.0.0/0
から NAT ゲートウェイへのルートを VPC デフォルトルートテーブルに追加するCanary を作成します。