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

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

编写 Node.js 金丝雀脚本

创建一个 CloudWatch Synthetics 金丝雀fROM 划痕

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

您必须为 Canary 脚本定义入口点函数。要查看如何 Amazon S3 文件上传到指定作为 Canary 的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 日志记录并使用 Amazon 开发工具包进行调用。出于演示目的,此脚本将创建 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(); };

将现有木偶脚本更改为用作 Synthetics 金丝雀

本节介绍如何对 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 时使用环境变量。这允许您编写单个金丝雀脚本,然后使用具有不同值的脚本快速创建具有类似任务的多个金丝雀。

例如,假定您的组织具有类似于的终端节点,例如proddev, 和pre-release用于软件开发的不同阶段,您需要创建 Canary 来测试每个端点。您可以编写一个测试软件的 Canary 脚本,然后在创建三个金丝雀中的每个金丝雀时为端点环境变量指定不同的值。然后,在创建金丝雀时,您可以指定要用于环境变量的脚本和值。

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

下面的示例脚本使用两个环境变量。这个脚本是用于检查网页是否可用的金丝雀。它使用环境变量来参数化它检查的 URL 和它使用的 CloudWatch Synthetics 日志级别。

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

synthetics.setLogLevel(process.env.LOG_LEVEL);

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

const URL = process.env.URL;

这是完整的脚本。当您使用此脚本创建金丝雀时,您可以为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(); };

将环境变量传递到脚本

要在控制台中创建金丝雀时将环境变量传递给脚本,请在环境变量部分。有关更多信息,请参阅创建金丝雀

要通过 API 或Amazon CLI,请使用EnvironmentVariables中的RunConfig部分。以下是示例Amazon CLI命令,该命令创建一个使用两个环境变量的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" }'

将您的金丝雀与其他Amazon服务

所有 Canary 都可以使用 Amazon 开发工具包库。在编写 Canary 时,您可以使用此库将 Canary 与其他 Amazon 服务集成。

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

  • 导入 Amazon 开发工具包。

    const AWS = require('aws-sdk');
  • 为要集成的 Amazon 服务创建客户端。

    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 添加 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。