Node.js canary 스크립트 작성 - Amazon CloudWatch

Node.js canary 스크립트 작성

Scratch에서 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(); };

다음으로 Synthetics 로깅을 사용하도록 스크립트를 확장하고 AWS SDK를 사용하여 호출합니다. 데모를 위해 이 스크립트는 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 파일이 있거나 스크립트가 달라지는 종속성이 있는 경우 모두 폴더 구조 nodejs/node_modules/myCanaryFilename.js file and other folders and files를 포함하는 단일 ZIP 파일로 번들할 수 있습니다. 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 이전 버전의 런타임을 사용하는 경우 functionNamehandler여야 합니다. 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');
  • Synthetics.getPage 함수를 사용하여 Puppeteer Page 객체를 가져옵니다.

    const page = await synthetics.getPage();

    Synthetics.getPage 함수에 의해 반환되는 페이지 객체에는 로깅을 위해 구성된 page.on request, responserequestfailed 이벤트가 있습니다. 또한 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를 생성해야 한다고 가정합니다. 소프트웨어를 테스트하는 단일 canary 스크립트를 작성한 다음, 세 개의 canary 각각을 생성할 때 엔드포인트 환경 변수에 대해 다양한 값을 지정할 수 있습니다. 그런 다음, canary를 생성할 때 스크립트 및 환경 변수에 사용할 값을 지정합니다.

환경 변수의 이름에는 문자, 숫자, 밑줄 문자가 포함될 수 있습니다. 이름은 문자로 시작해야 하며 2자 이상이어야 합니다. 환경 변수의 총 크기는 4KB를 초과할 수 없습니다. Lambda 예약 환경 변수를 환경 변수의 이름으로 지정할 수 없습니다. 예약된 환경 변수에 대한 자세한 내용은 런타임 환경 변수 단원을 참조하세요.

중요

환경 변수 키와 값은 암호화되지 않습니다. 환경 변수 키와 값에 민감한 정보를 저장하지 마세요.

다음 스크립트 예에서는 두 개의 환경 변수를 사용합니다. 이 스크립트는 웹 페이지를 사용할 수 있는지 여부를 확인하는 canary용입니다. 환경 변수를 사용하여 확인하는 URL과 사용하는 CloudWatch Synthetics 로그 수준을 모두 파라미터화합니다.

다음 함수는 LogLevelLOG_LEVEL 환경 변수의 값으로 설정합니다.

synthetics.setLogLevel(process.env.LOG_LEVEL);

다음 함수는 URLURL 환경 변수의 값으로 설정합니다.

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를 통해 환경 변수를 전달하려면 RunConfig 섹션에서 EnvironmentVariables 파라미터를 사용합니다. 다음은 EnvironmentRegion 키가 있는 두 개의 환경 변수를 사용하는 canary를 생성하는 AWS CLI 명령의 예입니다.

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" }'

다른 AWS 서비스와 canary 통합

모든 canary는 AWS SDK 라이브러리를 사용할 수 있습니다. canary를 다른 AWS 서비스와 통합하기 위해 canary를 작성할 때 이 라이브러리를 사용할 수 있습니다.

이렇게 하려면 canary에 다음 코드를 추가해야 합니다. 다음 예에서는 AWS Secrets Manager가 canary와 통합되는 서비스로 사용됩니다.

  • 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 주소를 사용하도록 지정

canary가 고정 IP 주소를 사용하도록 canary를 설정할 수 있습니다.

canary가 고정 IP 주소를 사용하도록 지정하려면
  1. 새 VPC를 생성합니다. 자세한 내용은 VPC에서 DNS 사용하기 단원을 참조하세요.

  2. 새 인터넷 게이트웨이를 생성합니다. 자세한 내용은 VPC에 인터넷 게이트웨이 추가 단원을 참조하세요.

  3. 새 VPC 내부에 퍼블릭 서브넷을 생성합니다.

  4. VPC에 새 라우팅 테이블을 추가합니다.

  5. 0.0.0.0/0에서 인터넷 게이트웨이로 이동하는 경로를 새 라우팅 테이블에 추가합니다.

  6. 새 라우팅 테이블을 퍼블릭 서브넷과 연결합니다.

  7. 탄력적 IP 주소를 생성합니다. 자세한 내용은 탄력적 IP 주소 단원을 참조하세요.

  8. 새 NAT 게이트웨이를 생성하여 퍼블릭 서브넷 및 탄력적 IP 주소에 할당합니다.

  9. VPC 내부에 프라이빗 서브넷을 생성합니다.

  10. 0.0.0.0/0에서 NAT 게이트웨이로 이동하는 경로를 VPC 기본 라우팅 테이블에 추가합니다.

  11. canary를 생성합니다.