如何使用aws cdk管理多个环境?

How to manage multiple environments using aws cdk?

我正在将我们的基础代码从 Terraform 转移到 AWS cdk。我正试图找到一种最佳方法来管理要部署多个堆栈的多个环境。如果我遵循文档中的建议,我将不得不为多个环境定义多个堆栈,这可能会让人感到困惑。例如

const app = new cdk.App();

new DevStack1(app, 'dev-stack-1', settings.dev)
new DevStack2(app, 'dev-stack-2', settings.dev)
.
.


new ProdStack1(app, 'prod-stack-1', settings.prod)
new ProdStack2(app, 'prod-stack-2', settings.prod)
.
.

在相同环境中的不同堆栈之间共享设置。然后我将不得不一个接一个地部署每个堆栈。有更好的方法吗?

编辑: 因为这个答案有合理的浏览量,我想更新它以反映更好、更 CDK 原生的方法。我将保留下面的原始答案,因为它可能对某些人更有效。

CDK 参数可以直接存储在 context 键下的 cdk.json 中,并在应用程序中使用 ConstructNode.try_get_context(param_name).

检索

查看官方文档:https://docs.aws.amazon.com/cdk/latest/guide/get_context_var.html

所以这是一个 cdk.json 的例子,不同的环境有不同的参数:

{
  "app": "python3 app.py",
  "context": {
    "dev": {
      "vpc": {
        "vpc_id": "blabla"
      }
    },
    "stage": {
      "vpc": {
        "vpc_id": "bleble"
      }
    }
}

现在您还可以通过 CLI 键 --context env_name=dev 提供上下文参数,并在代码中使用它的值来获取相关设置。

实际上大多数(如果不是全部)常用结构,如 StackAppNestedStack 都具有 node 属性,因此您可以从应用中的任何位置访问上下文.

使用它有 2 个注意事项:

  1. 它不允许访问较低级别的密钥(或者至少没有记录)。这意味着您不能使用 self.try_get_context("dev.vpc.vpc_id"),您需要通过 self.try_get_context("dev") 检索顶级密钥并自行向下。

  2. 如果您的上下文中有 bool 参数并尝试使用 CLI 密钥 --context key=False 覆盖它们,这些参数将变成 str,并且如果您使用以下常识性语法,则很容易落入陷阱:

if self.try_get_context("deploy_vpc"):
    MyVPC(app, "my new vpc")

由于“False”被评估为 True 作为非空字符串,您不会得到您期望的结果。


旧答案

我不确定是否对此存在共识,因为 CDK 仍然是一个新事物并且还支持多种语言。但我个人所做的是将不同环境的设置存储在 YAML 文件中,然后通过环境变量提供该文件。

Python 示例:

Config 是一个 YAML 文件,其中包含资源所需的标签、应用级别的一些设置(如帐户 ID 和区域)和一些堆栈级别的设置(如资源名称等)。

假设标准项目布局,其中有 app.py 个主文件和 cdk/cdk_stack.py 个带有堆栈描述的文件。 在 app.py:

from ruamel.yaml import YAML
...
from cdk.cdk_stack import MyStack


def load_config() -> dict:
    """
    Import settings from YAML config
    :return: dict with configuration items
    """
    # This variable should always be set
    # CDK doesn't work well with argparse
    config_path = os.getenv("CONFIG_PATH")
    if not config_path:
        raise RuntimeError("You need to supply config file path with CONFIG_PATH env variable")
    # We don't verify config content, let fail in case something is missing
    with open(config_path) as config_file:
        config = YAML().load(config_file.read())
    return config

def init_app() -> core.App:
    """
    Initiates CDK main_app for deployment
    :return: main_app
    """
    main_app = core.App()
    config = load_config()
    # Account ID and region have to be explicitly set in order to import existing resources
    MyStack(
        main_app,
        "my stack",
        env={
            'account': config["account_id"],
            'region': config["region"]
        },
        config=config
    )
    # Tags are applied to all tagable resources in the stack
    for key, value in config["tags"].items():
        core.Tag.add(scope=main_app, key=key, value=value)
    return main_app


if __name__ == '__main__':
    app = init_app()
    app.synth()

然后在 cdk/cdk_stack.py:

class MyStack(core.Stack):
    """
    Describes CF resources
    """
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        # Pop the config object first, since parent object doesn't expect it and will crash
        config = kwargs.pop("config")
        super().__init__(scope, id, **kwargs)
...