上下文值和 AWS CDK - AWS Cloud Development Kit (AWS CDK) v2

这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。

上下文值和 AWS CDK

上下文值是可以与应用程序、堆栈或构造相关联的键值对。它们可以从文件(通常位于项目目录中的 cdk.jsoncdk.context.json)在命令行中提供给您的应用程序。

CDK Toolkit 使用上下文缓存合成期间从 AWS 账户中检索到的值。值包括您账户中的可用区或当前可用于 Amazon EC2 实例的亚马逊机器映像(AMI)ID。由于这些值是由您的 AWS 账户提供的,因此每次运行 CDK 应用程序期间,它们可能会发生变化。这使它们成为意外更改的潜在来源。CDK Toolkit 的缓存行为会为 CDK 应用程序“冻结”这些值,直到您决定接受新值为止。

想象一下没有上下文缓存的以下场景。假设您指定了“最新 Amazon Linux”作为 Amazon EC2 实例的 AMI,并且此 AMI 的新版本已发布。然后,下次部署 CDK 堆栈时,已部署的实例将使用过时(“错误”)的 AMI,并将需要升级。升级会导致用新实例替换所有现有实例,这可能不是用户期望和想要的结果。

相反,CDK 会在您项目的 cdk.context.json 文件中记录您账户的可用 AMI,并将存储的值用于未来的合成操作。这样,AMI 列表将不再是潜在的变更来源。您还可以确保您的堆栈将始终合成到相同的 AWS CloudFormation 模板。

并非所有上下文值都是来自 AWS 环境的缓存值。AWS CDK 功能标志 也是上下文值。您还可以创建自己的上下文值,供您的应用程序或构造使用。

上下文键是字符串。值可以是 JSON 支持的任何类型:数字、字符串、数组或对象。

提示

如果您的构造创建了自己的上下文值,请将库的包名称包含在其密钥中,这样它们就不会与其他软件包的上下文值发生冲突。

许多上下文值都与特定 AWS 环境相关联,并且给定的 CDK 应用程序可以部署在多个环境中。此类值的密钥包含 AWS 账户和区域,这样来自不同环境的值就不会发生冲突。

以下上下文密钥说明了 AWS CDK 使用的格式,包含账户和区域。

availability-zones:account=123456789012:region=eu-central-1
重要

缓存上下文值由 AWS CDK 及其构造(包括您可能编写的构造)管理。请勿通过手动编辑文件添加或更改缓存上下文值。但是,不时查看一下 cdk.context.json 以了解缓存了哪些值会很有用。不代表缓存值的上下文值应存储在的 cdk.jsoncontext 密钥下。这样,当缓存值被清除时,它们就不会被清除。

上下文值的来源

上下文值可以通过六种不同的方式提供给您的 AWS CDK 应用程序:

  • 自动从当前 AWS 账户中获取。

  • 通过 cdk 命令的 --context 选项。(这些值始终是字符串。)

  • 在项目 cdk.context.json 文件中。

  • 在项目 cdk.json 文件的 context 密钥中。

  • ~/.cdk.json 文件 context 密钥中。

  • 在 AWS CDK 应用程序中,使用 construct.node.setContext() 方法。

项目文件 cdk.context.json 是 AWS CDK 缓存从您的 AWS 账户中检索到的上下文值的位置。这种做法可以避免在引入新的可用区等情况下对部署进行意外更改。AWS CDK 不会将上下文数据写入列出的任何其他文件。

重要

因为它们是应用程序状态的一部分,cdk.jsoncdk.context.json 必须与应用程序的其余源代码一起提交给源代码控制。否则,在其他环境(例如 CI 管线)中的部署可能会产生不一致的结果。

上下文值的作用域仅限于创建它们的构造;它们对子构造可见,但对父级或同级构造不可见。由 AWS CDK Toolkit(cdk 命令)设置的上下文值可以从文件或 --context 选项自动设置。来自这些来源的上下文值是在 App 构造上隐式设置的。因此,它们对应用程序中每个堆栈中的每个构造都可见。

您的应用程序可以使用 construct.node.tryGetContext 方法读取上下文值。如果在当前构造或其任何父构造中找不到请求的条目,则结果为 undefined。(或者,结果可能与您所用语言的等效值,例如 Python 中的 None。)

上下文方法

AWS CDK 支持多种上下文方法,使 AWS CDK 应用程序能够从 AWS 环境中获取上下文信息。例如,您可以使用 stack.availabilityZones 方法获取给定 AWS 账户和区域中可用的可用区列表。

以下是上下文方法:

HostedZone.fromLookup

获取您账户中的托管区。

stack.availabilityZones

获取受支持的可用区。

StringParameter.valueFromLookup

从当前区域的 Amazon EC2 Systems Manager Parameter Store 获取值。

Vpc.fromLookup

获取您账户中的现有 Amazon Virtual Private Cloud。

LookupMachineImage

在 Amazon Virtual Private Cloud 中查找机器映像,以便与 NAT 实例搭配使用。

如果所需的上下文值不可用,则 AWS CDK 应用程序会通知 CDK Toolkit 缺少上下文信息。接下来,CLI 会向当前 AWS 账户查询该信息,并将生成的上下文信息存储在 cdk.context.json 文件中。然后,它会使用上下文值再次执行 AWS CDK 应用程序。

查看和管理上下文

使用 cdk context 命令查看和管理 cdk.context.json 文件中的信息。要查看此信息,请使用不带任何选项的 cdk context 命令。输出应与以下内容类似。

Context found in cdk.json:

┌───┬─────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ # │ Key                                                         │ Value                                                   │
├───┼─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ 1 │ availability-zones:account=123456789012:region=eu-central-1 │ [ "eu-central-1a", "eu-central-1b", "eu-central-1c" ]   │
├───┼─────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ 2 │ availability-zones:account=123456789012:region=eu-west-1    │ [ "eu-west-1a", "eu-west-1b", "eu-west-1c" ]            │
└───┴─────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────┘

Run cdk context --reset KEY_OR_NUMBER to remove a context key. If it is a cached value, it will be refreshed on the next cdk synth.

要移除上下文值,请运行 cdk context --reset,指定该值的相应密钥或数字。以下示例删除了与前面示例中的第二个密钥对应的值。此值表示欧洲地区(爱尔兰)区域的可用区列表。

cdk context --reset 2
Context value
availability-zones:account=123456789012:region=eu-west-1
reset. It will be refreshed on the next SDK synthesis run.

因此,如果您想更新到最新版本的 Amazon Linux AMI,请使用前面的示例对上下文值进行受控更新并将其重置。然后,重新合成并部署您的应用程序。

cdk synth

要清除应用程序中存储的所有上下文值,请按如下方式运行 cdk context --clear

cdk context --clear

只能重置或清除存储在 cdk.context.json 中的上下文值。AWS CDK 不会处理其他上下文值。因此,为了防止使用这些命令时重置上下文值,可以将该值复制到 cdk.json

AWS CDK Toolkit --context 标志

在合成或部署期间,使用 --context(简写为 -c)选项将运行时上下文值传递给 CDK 应用程序。

cdk synth --context key=value MyStack

要指定多个上下文值,可重复使用 --context 选项任意次数,每次提供一个键值对。

cdk synth --context key1=value1 --context key2=value2 MyStack

合成多个堆栈时,指定的上下文值将传递给所有堆栈。要为单个堆栈提供不同的上下文值,要么对这些值使用不同的键,要么使用多个 cdk synthcdk deploy 命令。

从命令行传递的上下文值始终是字符串。如果值通常是其他类型,则您的代码必须准备好转换或解析该值。您可能以其他方式(例如 cdk.context.json)提供非字符串上下文值。要确保此类值按预期运行,请在转换之前确认该值是否为字符串。

示例

下面是通过 AWS CDK 上下文使用现有 Amazon VPC 的示例。

TypeScript
import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import { Construct } from 'constructs'; export class ExistsVpcStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpcid = this.node.tryGetContext('vpcid'); const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: vpcid, }); const pubsubnets = vpc.selectSubnets({subnetType: ec2.SubnetType.PUBLIC}); new cdk.CfnOutput(this, 'publicsubnets', { value: pubsubnets.subnetIds.toString(), }); } }
JavaScript
const cdk = require('aws-cdk-lib'); const ec2 = require('aws-cdk-lib/aws-ec2'); class ExistsVpcStack extends cdk.Stack { constructor(scope, id, props) { super(scope, id, props); const vpcid = this.node.tryGetContext('vpcid'); const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: vpcid }); const pubsubnets = vpc.selectSubnets({subnetType: ec2.SubnetType.PUBLIC}); new cdk.CfnOutput(this, 'publicsubnets', { value: pubsubnets.subnetIds.toString() }); } } module.exports = { ExistsVpcStack }
Python
import aws_cdk as cdk import aws_cdk.aws_ec2 as ec2 from constructs import Construct class ExistsVpcStack(cdk.Stack): def __init__(scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) vpcid = self.node.try_get_context("vpcid") vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=vpcid) pubsubnets = vpc.select_subnets(subnetType=ec2.SubnetType.PUBLIC) cdk.CfnOutput(self, "publicsubnets", value=pubsubnets.subnet_ids.to_string())
Java
import software.amazon.awscdk.CfnOutput; import software.amazon.awscdk.services.ec2.Vpc; import software.amazon.awscdk.services.ec2.VpcLookupOptions; import software.amazon.awscdk.services.ec2.SelectedSubnets; import software.amazon.awscdk.services.ec2.SubnetSelection; import software.amazon.awscdk.services.ec2.SubnetType; import software.constructs.Construct; public class ExistsVpcStack extends Stack { public ExistsVpcStack(Construct context, String id) { this(context, id, null); } public ExistsVpcStack(Construct context, String id, StackProps props) { super(context, id, props); String vpcId = (String)this.getNode().tryGetContext("vpcid"); Vpc vpc = (Vpc)Vpc.fromLookup(this, "VPC", VpcLookupOptions.builder() .vpcId(vpcId).build()); SelectedSubnets pubSubNets = vpc.selectSubnets(SubnetSelection.builder() .subnetType(SubnetType.PUBLIC).build()); CfnOutput.Builder.create(this, "publicsubnets") .value(pubSubNets.getSubnetIds().toString()).build(); } }
C#
using Amazon.CDK; using Amazon.CDK.AWS.EC2; using Constructs; class ExistsVpcStack : Stack { public ExistsVpcStack(Construct scope, string id, StackProps props) : base(scope, id, props) { var vpcId = (string)this.Node.TryGetContext("vpcid"); var vpc = Vpc.FromLookup(this, "VPC", new VpcLookupOptions { VpcId = vpcId }); SelectedSubnets pubSubNets = vpc.SelectSubnets([new SubnetSelection { SubnetType = SubnetType.PUBLIC }]); new CfnOutput(this, "publicsubnets", new CfnOutputProps { Value = pubSubNets.SubnetIds.ToString() }); } }

您可以使用 cdk diff 查看在命令行中传入上下文值的效果:

cdk diff -c vpcid=vpc-0cb9c31031d0d3e22
Stack ExistsvpcStack
Outputs
[+] Output publicsubnets publicsubnets: {"Value":"subnet-06e0ea7dd302d3e8f,subnet-01fc0acfb58f3128f"}

可以查看生成的上下文值,如下所示。

cdk context -j
{
  "vpc-provider:account=123456789012:filter.vpc-id=vpc-0cb9c31031d0d3e22:region=us-east-1": {
    "vpcId": "vpc-0cb9c31031d0d3e22",
    "availabilityZones": [
      "us-east-1a",
      "us-east-1b"
    ],
    "privateSubnetIds": [
      "subnet-03ecfc033225be285",
      "subnet-0cded5da53180ebfa"
    ],
    "privateSubnetNames": [
      "Private"
    ],
    "privateSubnetRouteTableIds": [
      "rtb-0e955393ced0ada04",
      "rtb-05602e7b9f310e5b0"
    ],
    "publicSubnetIds": [
      "subnet-06e0ea7dd302d3e8f",
      "subnet-01fc0acfb58f3128f"
    ],
    "publicSubnetNames": [
      "Public"
    ],
    "publicSubnetRouteTableIds": [
      "rtb-00d1fdfd823c82289",
      "rtb-04bb1969b42969bcb"
    ]
  }
}