这是 AWS CDK v2 开发者指南。旧版 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用构造库中的 CDK Pi pel AWS ines 模块来配置应用程序 AWS CDK 的持续交付。当你将 CDK 应用程序的源代码提交到 AWS CodeCommit时,GitHub,或者 AWS CodeStar,CDK Pipelines可以自动构建、测试和部署您的新版本。
CDK 管线会自行更新。如果您添加了应用程序阶段或堆栈,则该管线会自动对自身重新配置,以部署这些新阶段或堆栈。
注意
CDK Pipelines 支持两个 APIs。一个是在 CDK 管线开发人员预览版中提供的原始 API。另一个是现代 API,它包含了在预览阶段收到的 CDK 客户的反馈。本主题中的示例使用该现代 API。有关支持的两者之间的区别的详细信息 APIs,请参阅 aws-cdk 中的 CDK Pipelines 原始
引导您的环境 AWS
在使用 CDK Pipelines 之前,必须引导 AWS 要部署堆栈的环境。
CDK 管线至少涉及两个环境。第一个环境用于预置管线。第二个环境用于部署应用程序的堆栈或阶段(阶段是相关堆栈的组)。这些环境可能相同,但最佳实践建议是在不同的环境中将各个阶段彼此隔离开来。
注意
有关通过引导创建的资源的类型以及如何自定义引导堆栈的更多信息,请参阅AWS CDK 自力更生。
使用 CDK 管线进行持续部署需要在 CDK Toolkit 堆栈中包含以下内容:
-
Amazon Simple Storage Service (Amazon S3) 存储桶。
-
Amazon ECR 存储库。
-
IAM 角色,用于为管线的各个部分提供所需的权限。
如有必要,CDK Toolkit 将升级现有的引导堆栈或创建一个新的引导堆栈。
要引导可以配置 AWS CDK 管道的环境,请按以下示例cdk bootstrap
所示调用。如有必要,通过npx
命令调用 AWS CDK Toolkit 会临时安装它。它还将使用当前项目中安装的 Toolkit 版本(如有)。
--cloudformation-execution-policies
指定将来执行 CDK 管线部署所依据的策略的 ARN。默认AdministratorAccess
策略可确保您的管道可以部署所有类型的 AWS 资源。如果您使用此政策,请确保您信任构成 AWS CDK 应用程序的所有代码和依赖项。
大多数组织都要求对自动化可以部署哪些类型的资源进行更严格的控制。请咨询组织内的相应部门,以确定您的管线应使用的策略。
如果您的默认配置 AWS 文件包含必要的身份验证配置和 AWS 区域,则可以省略该--profile
选项。
npx cdk bootstrap aws://
ACCOUNT-NUMBER
/REGION
--profileADMIN-PROFILE
\ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
要引导管道将要部署 AWS CDK 应用程序的其他环境,请改用以下命令。该--trust
选项指示哪个其他账户应有权将 AWS CDK 应用程序部署到此环境中。对于此选项,请指定管道的 AWS 账户 ID。
同样,如果您的默认配置 AWS 文件包含必要的身份验证配置和 AWS 区域,则可以省略该--profile
选项。
npx cdk bootstrap aws://
ACCOUNT-NUMBER
/REGION
--profileADMIN-PROFILE
\ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \ --trustPIPELINE-ACCOUNT-NUMBER
提示
仅使用管理凭证引导和预置初始管线。之后,使用管线本身而非本地机器部署更改。
如果要升级已引导的旧版环境,则在创建新存储桶时,之前的 Amazon S3 存储桶将被孤立出来。使用 Amazon S3 控制台将其手动删除。
保护引导堆栈不被删除
如果删除了引导堆栈,则最初在环境中为支持 CDK 部署而配置的 AWS 资源也将被删除。这将导致管线停止工作。如果发生这种情况,没有通用的恢复解决方案。
引导环境后,请勿删除并重新创建环境的引导堆栈。而应尝试通过再次运行 cdk bootstrap
命令将引导堆栈更新到新版本。
为了防止引导堆栈被意外删除,我们建议您在 cdk bootstrap
命令中提供 --termination-protection
选项来启用终止保护。您可以在新的或现有的引导堆栈上启用终止保护。要了解有关该选项的更多信息,请参阅 --termination-protection
。
启用终止保护后,您可以使用 AWS CLI 或 CloudFormation 控制台进行验证。
启用终止保护
-
运行以下命令可在新的或现有的引导堆栈上启用终止保护:
$
cdk bootstrap --termination-protection
-
使用 AWS CLI 或 CloudFormation 控制台进行验证。以下是一个使用 AWS CLI的示例。如果您修改了引导堆栈名称,请将
CDKToolkit
替换为堆栈名称:$
aws cloudformation describe-stacks --stack-name
" trueCDKToolkit
--query "Stacks[0].EnableTerminationProtection
初始化项目
创建一个新的,空的 GitHub project 并将其克隆到您的工作站my-pipeline
目录中。(我们在本主题中的代码示例使用 GitHub。 您也可以使用 AWS CodeStar
或 AWS CodeCommit。)
git clone
GITHUB-CLONE-URL
my-pipeline cd my-pipeline
注意
您可以使用 my-pipeline
之外的名称命名应用程序主目录。但是,如果这样做,则必须在本主题的后面部分调整文件和类名。这是因为 AWS CDK Toolkit 的一些文件和类名基于主目录的名称。
克隆后,照常初始化项目。
$
cdk init app --language typescript
重要
请务必提交 cdk.json
和 cdk.context.json
文件以进行源代码控制。上下文信息(例如从您的 AWS 账户中检索的功能标志和缓存值)是项目状态的一部分。在其他环境中,这些值可能会有所不同,这可能会导致结果发生意外变化。有关更多信息,请参阅 上下文值和 AWS CDK。
定义管线
CDK 管线应用程序将包含至少两个堆栈:一个代表管线本身,剩下的一个或多个堆栈代表通过管线部署的应用程序。堆栈也可以分为几个阶段,您可以使用这些阶段将基础设施堆栈的副本部署到不同的环境中。现在,我们将探讨管线,稍后再深入研究它将部署的应用程序。
该构造CodePipeline
是表示 AWS CodePipeline 用作其部署引擎的 CDK Pipeline 的构造。在堆栈CodePipeline
中实例化时,需要定义管道的源位置(例如 GitHub 存储库)。您还可以定义用于构建应用程序的命令。
例如,以下内容定义了一个管道,其源存储在存储 GitHub 库中。它还包括 TypeScript CDK 应用程序的构建步骤。在指示的地方填写有关您的 GitHub 存储库的信息。
注意
默认情况下,管道 GitHub 使用存储在 Secrets Manager 中的名字github-token
下的个人访问令牌进行身份验证。
您还需要更新管道堆栈的实例化以指定 AWS 账户和区域。
在 lib/my-pipeline-stack.ts
中(如果项目文件夹名称不为 my-pipeline
,可能有所不同):
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
export class MyPipelineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const pipeline = new CodePipeline(this, 'Pipeline', {
pipelineName: 'MyPipeline',
synth: new ShellStep('Synth', {
input: CodePipelineSource.gitHub('OWNER
/REPO
', 'main'),
commands: ['npm ci', 'npm run build', 'npx cdk synth']
})
});
}
}
在 bin/my-pipeline.ts
中(如果项目文件夹名称不为 my-pipeline
,可能有所不同):
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { MyPipelineStack } from '../lib/my-pipeline-stack';
const app = new cdk.App();
new MyPipelineStack(app, 'MyPipelineStack', {
env: {
account: '111111111111
',
region: 'eu-west-1
',
}
});
app.synth();
您必须手动部署管线一次。之后,管线会通过源代码存储库自行保持最新状态。因此,请确保存储库中的代码是您要部署的代码。检查您的更改并推送到 GitHub,然后部署:
git add --all git commit -m "initial commit" git push cdk deploy
提示
现在您已经完成了初始部署,您的本地 AWS 帐户不再需要管理权限。这是因为对应用程序的所有更改都将通过管线进行部署。你所需要做的就是推到 GitHub。
应用程序阶段
要定义可以同时添加到管道中的多堆栈 AWS 应用程序,请定义一个子类。Stage
(这与 CDK 管线模块 CdkStage
中的不同。)
阶段包含组成应用程序的堆栈。如果堆栈之间存在依赖关系,则堆栈将按正确的顺序自动添加到管线中。相互不依赖的堆栈将并行部署。您可以通过调用 stack1.addDependency(stack2)
在堆栈之间添加依赖关系。
阶段接受默认 env
参数,该参数将成为阶段中的堆栈的默认环境。(堆栈仍然可以指定自己的环境。)
通过调用 addStage()
并使用 Stage
实例,可将应用程序添加到管线中。可以多次实例化一个阶段并将其添加到管线中,以定义 DTAP 或多区域应用程序管线的不同阶段。
我们将创建一个包含简单 Lambda 函数的堆栈,并将该堆栈放在一个阶段中。然后,我们将向管线中添加阶段,以便可以部署管线。
创建新文件 lib/my-pipeline-lambda-stack.ts
,以存放包含 Lambda 函数的应用程序堆栈。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda';
export class MyLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new Function(this, 'LambdaFunction', {
runtime: Runtime.NODEJS_18_X,
handler: 'index.handler',
code: new InlineCode('exports.handler = _ => "Hello, CDK";')
});
}
}
创建新文件 lib/my-pipeline-app-stage.ts
以容纳阶段。
import * as cdk from 'aws-cdk-lib';
import { Construct } from "constructs";
import { MyLambdaStack } from './my-pipeline-lambda-stack';
export class MyPipelineAppStage extends cdk.Stage {
constructor(scope: Construct, id: string, props?: cdk.StageProps) {
super(scope, id, props);
const lambdaStack = new MyLambdaStack(this, 'LambdaStack');
}
}
编辑 lib/my-pipeline-stack.ts
以将阶段添加到管线中。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
import { MyPipelineAppStage } from './my-pipeline-app-stage';
export class MyPipelineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const pipeline = new CodePipeline(this, 'Pipeline', {
pipelineName: 'MyPipeline',
synth: new ShellStep('Synth', {
input: CodePipelineSource.gitHub('OWNER
/REPO
', 'main'),
commands: ['npm ci', 'npm run build', 'npx cdk synth']
})
});
pipeline.addStage(new MyPipelineAppStage(this, "test", {
env: { account: "111111111111
", region: "eu-west-1
" }
}));
}
}
添加的每个应用程序阶段都会addStage()
导致添加一个相应的管道阶段,该阶段由addStage()
调用返回的StageDeployment实例表示。您可以通过调用其 addPre()
或 addPost()
方法向阶段添加部署前或部署后操作。
// import { ManualApprovalStep } from 'aws-cdk-lib/pipelines';
const testingStage = pipeline.addStage(new MyPipelineAppStage(this, 'testing', {
env: { account: '111111111111
', region: 'eu-west-1
' }
}));
testingStage.addPost(new ManualApprovalStep('approval'));
您可以向 Wave 添加阶段以并行部署它们,例如,将阶段部署到多个账户或区域时。
const wave = pipeline.addWave('wave');
wave.addStage(new MyApplicationStage(this, 'MyAppEU', {
env: { account: '111111111111
', region: 'eu-west-1
' }
}));
wave.addStage(new MyApplicationStage(this, 'MyAppUS', {
env: { account: '111111111111
', region: 'us-west-1
' }
}));
测试部署
您可以向 CDK 管线添加步骤,以验证正在执行的部署。例如,您可以使用 CDK 管线库的 ShellStep
执行以下任务:
-
尝试访问由 Lambda 函数支持的新部署的 Amazon API Gateway
-
通过发出 AWS CLI 命令来检查已部署资源的设置
添加验证操作的最简单格式如下所示:
// stage was returned by pipeline.addStage
stage.addPost(new ShellStep("validate", {
commands: ['../tests/validate.sh'],
}));
许多 AWS CloudFormation 部署都会导致生成名称不可预测的资源。因此,CDK Pipelines提供了一种在部署后 AWS CloudFormation 读取输出的方法。这使得将负载均衡器生成的 URL 传递给测试操作成为可能。
要使用输出,请公开您感兴趣的 CfnOutput
对象。然后,将其传递到步骤的 envFromCfnOutputs
属性中,使其可作为该步骤中的环境变量。
// given a stack lbStack that exposes a load balancer construct as loadBalancer
this.loadBalancerAddress = new cdk.CfnOutput(lbStack, 'LbAddress', {
value: `https://${lbStack.loadBalancer.loadBalancerDnsName}/`
});
// pass the load balancer address to a shell step
stage.addPost(new ShellStep("lbaddr", {
envFromCfnOutputs: {lb_addr: lbStack.loadBalancerAddress},
commands: ['echo $lb_addr']
}));
您可以直接在 ShellStep
中编写简单的验证测试,但是当测试代码超过几行时,这种方法会变得笨拙。对于更复杂的测试,您可以通过 inputs
属性将其他文件(例如完整的 shell 脚本或其他语言的程序)引入到 ShellStep
中。输入可以是任何具有输出的步骤,包括来源(例如 GitHub 存储库)或其他ShellStep
步骤。
如果文件可以直接用于测试(例如,如果它们本身是可执行文件),则可以从源存储库中引入这些文件。在此示例中,我们将 GitHub 存储库声明为source
(而不是将其作为其中的一部分内联实例化)。CodePipeline
然后,我们将这个文件集同时传递给管线和验证测试。
const source = CodePipelineSource.gitHub('OWNER
/REPO
', 'main');
const pipeline = new CodePipeline(this, 'Pipeline', {
pipelineName: 'MyPipeline',
synth: new ShellStep('Synth', {
input: source,
commands: ['npm ci', 'npm run build', 'npx cdk synth']
})
});
const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', {
env: { account: '111111111111
', region: 'eu-west-1
' }
}));
stage.addPost(new ShellStep('validate', {
input: source,
commands: ['sh ../tests/validate.sh']
}));
如果需要编译测试(在合成中完成),则可以从合成步骤中获取其他文件。
const synthStep = new ShellStep('Synth', {
input: CodePipelineSource.gitHub('OWNER
/REPO
', 'main'),
commands: ['npm ci', 'npm run build', 'npx cdk synth'],
});
const pipeline = new CodePipeline(this, 'Pipeline', {
pipelineName: 'MyPipeline',
synth: synthStep
});
const stage = pipeline.addStage(new MyPipelineAppStage(this, 'test', {
env: { account: '111111111111
', region: 'eu-west-1
' }
}));
// run a script that was transpiled from TypeScript during synthesis
stage.addPost(new ShellStep('validate', {
input: synthStep,
commands: ['node tests/validate.js']
}));
安全说明
任何形式的持续交付都有固有的安全风险。根据责任 AWS 共担模式
但是,就其本质而言,需要高级别访问权限才能实现其预期目的的库无法保证完全安全。您的组织之外有许多攻击媒介。 AWS
请特别注意以下事项:
-
请注意您所依赖的软件。审查您在管线中运行的所有第三方软件,因为它们可能会更改已部署的基础设施。
-
使用依赖关系锁定防止意外升级。CDK 管线支持
package-lock.json
和yarn.lock
,以确保您的依赖关系符合自己的期望。 -
CDK 管线在您自己的账户中创建的资源上运行,这些资源的配置由开发人员通过管线提交代码进行控制。因此,CDK 管线本身无法防范试图绕过合规性检查的恶意开发人员。如果您的威胁模型包括编写 CDK 代码的开发人员,则应有外部合规机制,例如 AWS CloudFormation Hook
(预防性)或 C AWS onfig (反应式), AWS CloudFormation 执行角色无权禁用这些机制。 -
用于生产环境的凭证应短期有效。在引导和初始预置之后,开发人员就完全无需拥有账户凭证。可以通过管线部署更改。通过一开始就杜绝对凭证的需求,从而降低凭证泄露的可能性。
故障排除
在开始使用 CDK 管线时,您通常会遇到以下问题。
- 管线:内部故障
-
CREATE_FAILED | AWS::CodePipeline::Pipeline | Pipeline/Pipeline Internal Failure
检查您的 GitHub 访问令牌。它可能丢失了,或者可能没有访问存储库的权限。
- 密钥:策略包含一条语句,涉及一个或多个无效主体。
-
CREATE_FAILED | AWS::KMS::Key | Pipeline/Pipeline/ArtifactsBucketEncryptionKey Policy contains a statement with one or more invalid principals.
其中一个目标环境尚未使用新的引导堆栈进行引导。确保所有目标环境都已引导。
- 堆栈处于 ROLLBACK_COMPLETE 状态,无法更新。
-
Stack
STACK_NAME
is in ROLLBACK_COMPLETE state and can not be updated. (Service: AmazonCloudFormation; Status Code: 400; Error Code: ValidationError; Request ID: ...)堆栈在之前的部署中失败,并且处于不可重试的状态。从 AWS CloudFormation 控制台中删除堆栈,然后重试部署。