编写 Node.js Canary 脚本 - Amazon CloudWatch
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

编写 Node.js Canary 脚本

从头创建 CloudWatch Synthetics Canary

这里是一个极简 Synthetics Canary 脚本示例。此脚本将成功通过一次运行,并返回一个字符串。要查看失败的 Canary 示例,请将 let fail = false; 更改为 let fail = true;

您必须为 Canary 脚本定义入口点函数。要查看如何将文件上传到指定作为 Canary Amazon S3 的 ArtifactS3Location 位置,请在 /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(); };

接下来,我们将扩展脚本以使用 Synthetics 日志记录并使用 AWS 开发工具包进行调用。出于演示目的,此脚本将创建 Amazon DynamoDB 客户端并调用 DynamoDB listTables API。它会记录对请求的响应,并根据请求是否成功来记录通过还是失败。

如果您有多个 .js 文件,或者您的脚本依赖于某个依赖项,则您可以将它们打包到包含文件夹结构 nodejs/node_modules/myCanaryFilename.js file and other folders and files 的单个 ZIP 文件中。

请务必将 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');
  • 使用 Synthetics.getPage 函数获取 Puppeteer Page 对象。

    const page = await synthetics.getPage();

    Synthetics.getPage 函数返回的页面对象指示需要记录 page.on requestresponserequestfailed 事件。Synthetics 还为页面上的请求和响应设置 HAR 文件生成,并将 Canary ARN 添加到页面上的传出请求的 user-agent 标头。

该脚本现已准备好作为 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用于软件开发不同阶段的终端节点(如 devpre-release和 ),并且您需要创建 Canary 来测试这些终端节点中的每个。您可以编写单个 Canary 脚本来测试您的软件,然后在创建三个 Canary 中的每个 Canary 时为终端节点环境变量指定不同的值。然后,在创建 Canary 时,您可以指定要用于环境变量的脚本和值。

环境变量的名称可以包含字母、数字和下划线字符。它们必须以字母开头且至少为两个字符。环境变量的总大小不能超过 4 KB。您不能将任何 Lambda 预留环境变量指定为环境变量的名称。有关预留环境变量的更多信息,请参阅运行时环境变量

以下示例脚本使用两个环境变量。此脚本适用于检查网页是否可用的 Canary。它使用环境变量来参数化它检查的 URL 及其使用的 CloudWatch Synthetics 日志级别。

以下函数LogLevel将 设置为 LOG_LEVEL 环境变量的值。

synthetics.setLogLevel(process.env.LOG_LEVEL);

此函数URL将 设置为 URL 环境变量的值。

const URL = process.env.URL;

这是完整的脚本。使用此脚本创建 Canary 时,您可以指定 LOG_LEVELURL 环境变量的值。

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 参数。以下是创建 Canary 的示例AWS CLI命令,该 Canary 使用两个环境变量,其键为 EnvironmentRegion

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 开发工具包库。在编写 Canary 时,您可以使用此库将 Canary 与其他 AWS 服务集成。

为此,您需要将以下代码添加到您的 Canary。 AWS对于这些示例, AWS Secrets Manager 用作 Canary 正在与之集成的服务。

  • 导入 AWS 开发工具包。

    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 地址

您可以设置 Canary,使其使用静态 IP 地址。

强制 Canary 使用静态 IP 地址

  1. 创建新的 VPC。有关更多信息,请参阅在您的 VPC 中使用 DNS

  2. 创建新的 Internet 网关。有关更多信息,请参阅向您的 VPC https://docs.amazonaws.cn/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html#working-with-igw添加 Internet 网关。

  3. 在您的新 VPC 中创建一个公有子网。

  4. 向 VPC 添加新的路由表。

  5. 在新路由表中添加一个路由,它从 0.0.0.0/0 进入 Internet 网关。

  6. 将新的路由表与公有子网关联。

  7. 创建弹性 IP 地址。有关更多信息,请参阅弹性 IP 地址

  8. 创建新的 NAT 网关,并将其分配给公有子网和弹性 IP 地址。

  9. 在 VPC 内创建私有子网。

  10. 将路由添加到 VPC 默认路由表,该路由从 0.0.0.0/0 路由到 NAT 网关

  11. 创建 Canary。