

# 常见的金丝雀功能
<a name="CloudWatch_Synthetics_Canaries_CommonFeatures"></a>

您可以对所有金丝雀运行时使用下面的功能。

## 环境变量
<a name="CloudWatch_Synthetics_Environment_Variables"></a>

您可以在创建金丝雀时使用环境变量。您可以编写单个金丝雀脚本，并将其与不同的值搭配使用来快速创建用于类似任务的多个金丝雀。

例如，假设贵组织具有用于不同软件开发阶段的端点（例如 `prod`、`dev` 和 `pre-release`）。您必须创建金丝雀来测试每个端点。您可以编写一个金丝雀脚本来测试软件。然后，在创建三个金丝雀时，请分别指定不同的端点环境变量值。创建金丝雀时，请指定脚本和环境变量值。

环境变量的名称可以包含字母、数字和下划线字符，必须以字母开头，并且至少为两个字符。环境变量的总大小不能超过 4 KB。不能将任何 Lambda 预留环境变量指定为环境变量的名称。有关预留环境变量的更多信息，请参阅[运行时环境变量](https://docs.amazonaws.cn/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime)。

**环境变量在客户端未加密**  
默认情况下，Amazon 使用 Amazon 自有密钥对环境变量键和值进行静态加密。但是，CloudWatch Synthetics 并不应用客户端加密。敏感信息在传输中加密后才能存储。有关更多信息，请参阅 [对环境变量进行传输中加密](#CloudWatch_Synthetics_transit_encryption)。您还可以使用客户自主管理型 Amazon KMS 密钥来对金丝雀的环境变量进行静态加密。有关更多信息，请参阅 [使用客户自主管理型密钥对环境变量进行静态加密。](#CloudWatch_Synthetics_function_encryption)。

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

以下代码段是下面显示的完整脚本的一部分。

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

```
 synthetics.setLogLevel(process.env.LOG_LEVEL);
```

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

```
const URL = process.env.URL;
```

下面的完整脚本演示了这两个环境变量。当您使用此脚本创建金丝雀时，您可以指定 `LOG_LEVEL` 和 `URL` 环境变量的值。

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

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();
};
```

### 将环境变量传递到脚本
<a name="CloudWatch_Synthetics_Canaries_pass_variables"></a>

要在控制台中创建金丝雀时将环境变量传递给脚本，请在控制台上的 **Environment variables（环境变量）**部分指定环境变量的密钥和值。有关更多信息，请参阅 [创建金丝雀](CloudWatch_Synthetics_Canaries_Create.md)。

要通过 API 或 Amazon CLI 传递环境变量，请使用 `RunConfig` 部分中的 `EnvironmentVariables` 参数。以下为 Amazon CLI 命令示例，该命令创建一个使用两个环境变量（具有 `Environment` 和 `Region` 密钥）的金丝雀。

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

## 使用客户自主管理型密钥对环境变量进行静态加密。
<a name="CloudWatch_Synthetics_function_encryption"></a>

默认情况下，Amazon 自有密钥会对金丝雀环境变量进行静态加密。您可以指定客户自主管理型 Amazon KMS 密钥来对金丝雀环境变量进行静态加密。使用客户自主管理型密钥，您可以完全控制敏感配置数据的加密。以下部分介绍使用客户自主管理型的要求、配置步骤和权限。

### 要求
<a name="CloudWatch_Synthetics_function_encryption_requirements"></a>

配置客户自主管理型密钥之前，请确认您满足以下要求：
+ Amazon KMS 密钥必须是对称加密密钥。
+ 密钥策略必须向调用方（调用 Synthetics API 的 IAM 主体）授予 `kms:CreateGrant`。
+ Amazon Lambda 使用该授权对环境变量进行静态加密和解密。
+ Amazon KMS 密钥必须与金丝雀版本位于同一 Amazon 区域。

### 配置客户自主管理型密钥
<a name="CloudWatch_Synthetics_function_encryption_configure"></a>

您可以在创建或更新金丝雀时配置客户自主管理型密钥。以下过程展示了如何使用 Amazon CloudWatch 控制台和 Synthetics API 来配置加密。

#### 在控制台中配置加密
<a name="CloudWatch_Synthetics_function_encryption_configure_console"></a>

要在 Amazon CloudWatch 控制台中创建或编辑金丝雀，请展开**环境变量**部分。在**静态加密配置**下，选择**使用客户自主管理型密钥**，然后选择或指定 Amazon KMS 密钥的 ARN。

#### 使用 API 配置加密
<a name="CloudWatch_Synthetics_function_encryption_configure_api"></a>

调用 `CreateCanary` 或 `UpdateCanary` 时，请使用客户自主管理型密钥的 ARN 指定 `KmsKeyArn` 参数。要恢复为 Amazon 托管式密钥，请将 `KmsKeyArn` 设置为空字符串。

#### 示例：使用客户自主管理型密钥创建金丝雀请求
<a name="CloudWatch_Synthetics_function_encryption_configure_example"></a>

```
{
"Name": "my-canary-EXAMPLE",
"KmsKeyArn": "arn:aws:kms:us-east-1:111122223333:key/a1b2c3d4-e5f6-7890-abcd-EXAMPLE11111",
"RunConfig": {
  "EnvironmentVariables": {
    "SECRET_KEY": "my-secret-value-EXAMPLE"
  }
}
}
```

### 静态加密所需的权限
<a name="CloudWatch_Synthetics_function_encryption_permissions"></a>

如果创建或更新金丝雀，则必须对 Amazon KMS 密钥具有以下权限：
+ `kms:CreateGrant`、`kms:Encrypt`：为金丝雀配置客户自主管理型密钥所必需的。
+ `kms:Decrypt`：查看和管理使用客户自主管理型密钥加密的环境变量所必需的。
+ `kms:DescribeKey`：验证密钥所必需的。

金丝雀执行角色不需要任何 Amazon KMS 权限即可进行静态加密。Lambda 使用该授权来处理加密和解密。

### 多位置金丝雀
<a name="CloudWatch_Synthetics_function_encryption_multilocation"></a>

对于多位置金丝雀，每个副本位置都可以拥有自己的 Amazon KMS 密钥。创建或更新金丝雀时，请在 `AddReplicaLocations` 参数中指定 `KmsKeyArn`。密钥必须与副本位于同一区域。

## 对环境变量进行传输中加密
<a name="CloudWatch_Synthetics_transit_encryption"></a>

除了静态加密之外，您还可以在 CloudWatch Synthetics 存储各个环境变量值之前对其进行加密。CloudWatch Synthetics 称之为传输中加密。当对值进行传输中加密时，控制台会将明文值替换为 base64 编码的加密文字，仅您的金丝雀可以在运行时解密该加密文字。

### 传输中加密的工作原理
<a name="CloudWatch_Synthetics_transit_encryption_how"></a>

当您选择对环境变量值进行传输中加密时：

1. 控制台使用您选择的 Amazon KMS 密钥调用 `kms:Encrypt` 以加密明文值。

1. 加密的加密文字（base64 编码）取代了环境变量配置中的明文值。

1. 在运行时，金丝雀脚本会通过调用 `kms:Decrypt` 来解密该值。

### 传输中加密所需的权限
<a name="CloudWatch_Synthetics_transit_encryption_permissions"></a>

您需要拥有以下权限才能进行传输中加密：
+ **控制台用户或 API 调用方**：对 Amazon KMS 密钥拥有 `kms:Encrypt` 权限。您需要此权限才能在存储值之前对其进行加密。
+ **金丝雀执行角色**：对 Amazon KMS 密钥拥有 `kms:Decrypt` 权限。金丝雀的 Lambda 函数需要此权限才能在运行时对该值进行解密。

下面是附加到金丝雀执行角色的 IAM 策略示例。

```
{
"Version": "2012-10-17",
"Statement": [
  {
    "Effect": "Allow",
    "Action": "kms:Decrypt",
    "Resource": "arn:aws:kms:us-east-1:111122223333:key/a1b2c3d4-e5f6-7890-abcd-EXAMPLE11111"
  }
]
}
```

### 解密金丝雀脚本中的值
<a name="CloudWatch_Synthetics_transit_encryption_decrypt"></a>

要在金丝雀脚本中使用经过加密的环境变量，请在运行时对其进行解密。以下 Node.js 示例展示了如何解密环境变量：

```
const { KMSClient, DecryptCommand } = require('@aws-sdk/client-kms');
const client = new KMSClient({ region: process.env.AWS_REGION });

async function decryptEnvVar(name) {
const encrypted = process.env[name];
const req = {
  CiphertextBlob: Buffer.from(encrypted, 'base64'),
};
const command = new DecryptCommand(req);
const response = await client.send(command);
return new TextDecoder().decode(response.Plaintext);
}

// Usage
const mySecret = await decryptEnvVar('MY_CONFIG_VAR');
```

## 将您的金丝雀与其他 Amazon 服务集成
<a name="CloudWatch_Synthetics_Canaries_AWS_integrate"></a>

您可以在金丝雀中使用 Amazon SDK 库来与其他 Amazon 服务集成。

为此，请将以下代码添加到金丝雀中。在这些示例中，金丝雀与 Amazon Secrets Manager 集成。
+ 导入 Amazon SDK。

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

  ```
  const secretsManager = new AWS.SecretsManager();
  ```
+ 使用客户端对该服务进行 API 调用。

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

下面的金丝雀脚本代码段更详细地展示了如何与 Secrets Manager 集成。

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

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();
};
```

## 强制金丝雀使用静态 IP 地址
<a name="CloudWatch_Synthetics_Canaries_staticIP"></a>

您可以设置金丝雀，使其使用静态 IP 地址。

**强制金丝雀使用静态 IP 地址**

1. 创建新的 VPC。有关更多信息，请参阅[将 DNS 与您的 VPC 一起使用](https://docs.amazonaws.cn/vpc/latest/userguide/vpc-dns.html)。

1. 创建新的互联网网关。有关更多信息，请参阅[在您的 VPC 中添加互联网网关](https://docs.amazonaws.cn/vpc/latest/userguide/VPC_Internet_Gateway.html#working-with-igw)。

1. 在新的 VPC 内创建公有子网。

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

1. 在新路由表中添加一条从 `0.0.0.0/0` 到互联网网关的路由。

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

1. 创建弹性 IP 地址。有关更多信息，请参阅[弹性 IP 地址](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html)。

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

1. 在 VPC 中创建私有子网。

1. 向 VPC 默认路由表添加一条从 `0.0.0.0/0` 到 NAT 网关的路由。

1. 创建金丝雀。