简介
本系列文章将介绍如何使用 AWS CDK(云开发工具包)在 AWS 上进行常见且实用的云资源部署。本系列文章旨在帮助那些已经熟悉 AWS 的用户使用 AWS 提供的这个强大的 IaC(基础设施即代码)库来部署云资源。
您可能已经想到,AWS CDK 仅针对 AWS 云基础设施的部署。因此,如果您担心云部署会受到 AWS 供应商锁定,那么本系列文章可能不适合您。还有其他工具可以进行多供应商云部署和配置(例如 Terraform、Pulumi 和 Serverless)。
但是,如果您觉得自己像许多 DevOps/解决方案架构师/开发人员一样经常使用 AWS,那么本系列文章将向您展示使用 AWS CDK 部署资源的实用示例。
背景
您可以使用控制台、命令行界面或其他 IaC 工具在 AWS 上手动创建资源。这正是 AWS 刚出现时人们所做的。但很快,人们就发现,这种配置云基础设施的模式容易出错且不可持续,尤其对于海量复杂的资源堆栈而言。这些挫败感促成了 Cloudformation 于 2011 年的诞生。
Cloudformation
AWS Cloudformation 是 AWS 提供的托管服务,用于配置云基础设施和服务。您可以使用 JSON 或 YAML 模板文件来执行此操作,该文件定义了您希望 AWS 创建、更新或销毁哪些资源。Cloudformation 会使用该文件并对您已部署的资源堆栈进行必要的更改。
Cloudformation 是 AWS 上自动部署资源的骨干服务。它确保已部署资源的一致性,并可以跟踪资源的漂移(在 Cloudformation 模板文件之外创建、更新或销毁资源时)。
问题在于,Cloudformation 模板文件的编写、读取、更新和管理极其困难。毕竟,它们只是基本的 YAML/JSON 文件。如果您只配置少量资源,这还好。但一旦资源超过 20 个,就完全无法依靠人力进行管理。
这就是 CDK 的用武之地。
云开发工具包
AWS CDK 是一个函数库,它可以生成 Cloudformation 模板,并使用它配置 AWS 中的资源。它允许您使用自己喜欢的语言编写代码来定义这些 Cloudformation 模板。
使用 CDK,您可以从代码编辑器访问智能感知,将资源分解为逻辑部分,进行类型检查、枚举等,这意味着配置资源时出错的可能性大大降低。您还可以为要部署的基础设施编写测试,以确保在 IaC 中所做的任何更改都不会破坏您已部署的资源堆栈,也不会在您错误地超出能力范围配置时造成数百万美元的损失。
过去一年使用 CDK 对我来说非常愉快,很难再回到其他用于 AWS 资源配置的解决方案,例如 Terraform、Serverless 和 SAM。
但事情并非总是一帆风顺
没有一个项目是完美的。 AWS CDK 虽然很棒,但它也是一个近期项目(于 2019 年 7 月启动),因此它尚未涵盖在 AWS 上部署的所有可能功能(即使是 Cloudformation 也尚未涵盖 AWS 上的所有可能功能)。有些功能需要通过控制台或命令行界面手动配置。由于 CDK 的开发正在努力跟上 AWS 发布新服务和更新的步伐,因此也存在一些小错误、快速变化的接口、弃用等问题。也许一年之内,本系列中的许多代码示例都会被弃用。但核心 API 和概念将保持不变。此外,对于大多数常见服务,CDK 都能满足您的需求。
如何安装 AWS CDK
先决条件
您将需要:
- 一个具有编程访问权限的 AWS 账户
- Node.js
设置
- 确保您已设置 Node.js
- 确保您的 AWS 凭证已正确配置。
以下是我的 AWS 配置访问文件示例,
AWS 凭证文件:
# Linux 和 MacOS:~/.aws/credentials
# Windows:%USERPROFILE%\.aws\credentials
[默认]
aws_access_key_id=xXXxxxxXXXXxxxxxxx
aws_secret_access_key=xxXXXXxxxXXxXxx
AWS 配置文件:
# Linux 和 MacOS:~/.aws/config
# Windows:%USERPROFILE%\.aws\config
[默认]
region=us-west-2
output=json
AWS CDK 在需要访问部署资源所需的 AWS 账户时,会查找这些文件。
部署资源时,使用的 IAM 凭证具有管理员权限会很有帮助,因为这会减少很多与权限相关的麻烦。
如果您对 AWS IAM 以及部署的权限需求有深入的了解,可以筛选掉一些权限和特权。
在任意文件夹中全局安装 AWS CDK(以便您可以使用您选择的语言引导启动新的 CDK 项目)。
npm install -g aws-cdk
检查是否已安装
cdk --version
ninjamac@ip-192-168-1-2 aws % npm install -g aws-cdk
added 1 package in 5s
npm notice
npm notice New major version of npm available! 10.9.2 -> 11.3.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.3.0
npm notice To update run: npm install -g npm@11.3.0
npm notice
ninjamac@ip-192-168-1-2 aws % cdk --version
2.1010.0 (build 6b421db)
引导新的 CDK 应用程序
使用您的终端,创建一个新的目录 simple-ec2 并进入该目录:
mkdir app && cd app
ninjamac@ip-192-168-1-2 simple-ec2 % cdk init --language=typescript
Applying project template app for typescript
# Welcome to your CDK TypeScript project
This is a blank project for CDK development with TypeScript.
The `cdk.json` file tells the CDK Toolkit how to execute your app.
## Useful commands
* `npm run build` compile typescript to js
* `npm run watch` watch for changes and compile
* `npm run test` perform the jest unit tests
* `npx cdk deploy` deploy this stack to your default AWS account/region
* `npx cdk diff` compare deployed stack with current state
* `npx cdk synth` emits the synthesized CloudFormation template
Initializing a new git repository...
Executing npm install...
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
✅ All done!
这将使用 Typescript 为您初始化一个新的 CDK 项目。
运行 npm update 以确保您使用的是最新版本的 CDK。
如果 @aws-cdk/core 与其他 @aws-cdk 子包之间存在版本冲突,那么您会遇到一些奇怪的错误。@aws-cdk/core 和所有其他导入的 @aws-cdk/PACKAGE 都应该具有相同的版本。
CDK 应用程序的结构和设置
ninjamac@ip-192-168-1-2 app % tree -I 'node_modules'
.
├── README.md
├── bin
│ └── app.ts
├── cdk.json
├── cdk.out
│ ├── AppStack.assets.json
│ ├── AppStack.template.json
│ ├── asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200
│ │ ├── __entrypoint__.js
│ │ └── index.js
│ ├── cdk.out
│ ├── manifest.json
│ └── tree.json
├── jest.config.js
├── lib
│ └── app-stack.ts
├── package-lock.json
├── package.json
├── test
│ └── app.test.ts
└── tsconfig.json
6 directories, 16 files
- /bin/app.ts 是 CDK 使用的入口文件。您可以在此处定义堆栈。
- 用于配置资源的 IaC 将位于 lib 文件夹中,并且在 synth 和 deploy 操作期间,./bin/app.ts 需要它。我稍后会解释这两个命令。
- ./test/app.test.ts 包含用于测试 CDK 应用程序的模板代码
设置
在 ./bin/app.ts 中,定义了一个 new App() ,它代表一个单一堆栈。
我们可以在此文件中为我们的堆栈添加描述。
// ./bin/app.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { AppStack } from '../lib/app-stack';
const app = new cdk.App();
new AppStack(app, 'AppStack');
lib/app-stack.ts
// ./lib/app-stack.ts
import * as cdk from '@aws-cdk/core';
export class appStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
}
}
如您所见,cdk init 命令为我们快速构建了一个良好的模板,让我们能够快速编写 IaC。基类 cdk.Stack 使我们能够创建新的 Cloudformation 堆栈。
我们还需要创建一个 .env 文件来保存我们的 AWS 账号和我们将要使用的区域。在项目的根目录中创建一个名为 .env 的文件,并添加以下内容。将 xxXxXxxXXxXxx 替换为您的 AWS 账号,并使用您想要的区域。
AWS_ACCOUNT_NUMBER=654654314383
AWS_ACCOUNT_REGION=ap-southeast-2
写基础结构代码
bin/app.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { AppStack } from '../lib/app-stack';
const app = new cdk.App();
new AppStack(app, 'AppStack');
lib/app-stack.ts
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { aws_iam as iam } from 'aws-cdk-lib';
import { aws_ec2 as ec2 } from 'aws-cdk-lib';
import { readFileSync } from 'fs';
require('dotenv').config()
const config = {
env: {
account: process.env.AWS_ACCOUNT_NUMBER,
region: process.env.AWS_REGION
}
}
// Defining the CDK stack class that extends the Stack class
export class AppStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, { ...props, env: config.env }); // Calling the constructor of the parent class (Stack)
// Creating a new VPC (Virtual Private Cloud)
const vpc = new ec2.Vpc(this, 'VPC');
// Importing an IAM Role from its Amazon Resource Name (ARN)
const role = new iam.Role(
this,
'simple-instance-1-role', // this is a unique id that will represent this resource in a Cloudformation template
{ assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') }
)
const instance = new ec2.Instance(
this,
"myEc2App",
{
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T2,
ec2.InstanceSize.MEDIUM
),
//***** We are using Windows AMI, we can login via RDP if SG has 3389 port enabled *****
machineImage: ec2.MachineImage.latestWindows(
ec2.WindowsVersion.WINDOWS_SERVER_2022_ENGLISH_FULL_BASE
),
keyName: 'windows', //utilizing existing one [created one via console]
role: role,
vpc: vpc
}
);
}
}
部署
记住,我们在第二部分中在 ~/.aws/config 和 ~/.aws/credentials 中设置了 AWS 配置文件和凭证。
我将部署到我的默认配置文件,该配置文件链接到我的个人 AWS 账户,区域为 ap-southeast-2。
# bootstap CDK
cdk bootstrap
#观察有没有变化
cdk diff
# 合成 AWS CloudFormation 模板,无需部署
cdk synth
# 部署
cdk deploy
#销毁
cdk destroy
ninjamac@ip-192-168-1-2 app % cdk bootstrap
[WARNING] aws-cdk-lib.aws_ec2.InstanceProps#keyName is deprecated.
- Use `keyPair` instead - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#using-an-existing-ec2-key-pair
This API will be removed in the next major release.
⏳ Bootstrapping environment aws://654654314383/ap-southeast-2...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.
CDKToolkit: creating CloudFormation changeset...
✅ Environment aws://654654314383/ap-southeast-2 bootstrapped.
ninjamac@ip-192-168-1-2 app % cdk diff
[WARNING] aws-cdk-lib.aws_ec2.InstanceProps#keyName is deprecated.
- Use `keyPair` instead - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#using-an-existing-ec2-key-pair
This API will be removed in the next major release.
start: Building AppStack Template
success: Built AppStack Template
start: Publishing AppStack Template (654654314383-current_region)
success: Published AppStack Template (654654314383-current_region)
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Could not create a change set, will base the diff on template differences (run again with -v to see the reason)
Stack AppStack
IAM Statement Changes
┌───┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────┬───────────────────────────────────┬────────────────────────────────────────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ - │ arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${VPCB9E5F0B4.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │
│ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupEgress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupIngress │ │ │
├───┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:654654314383:security-group/${VPC.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │
│ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupEgress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupIngress │ │ │
└───┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────┴───────────────────────────────────┴────────────────────────────────────────────────────────────────┴───────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Resources
[~] Custom::VpcRestrictDefaultSG VPC/RestrictDefaultSecurityGroupCustomResource VPCRestrictDefaultSecurityGroupCustomResource59474679
└─ [~] Account
└─ @@ -1,3 +1,1 @@
[-] {
[-] "Ref": "AWS::AccountId"
[-] }
[+] "654654314383"
[~] AWS::IAM::Role Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0
└─ [~] Policies
└─ @@ -25,12 +25,8 @@
[ ] {
[ ] "Ref": "AWS::Region"
[ ] },
[-] ":",
[+] ":654654314383:security-group/",
[ ] {
[-] "Ref": "AWS::AccountId"
[-] },
[-] ":security-group/",
[-] {
[ ] "Fn::GetAtt": [
[ ] "VPCB9E5F0B4",
[ ] "DefaultSecurityGroup"
[~] AWS::Lambda::Function Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E
└─ [~] Code
└─ [~] .S3Bucket:
└─ [~] .Fn::Sub:
├─ [-] cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}
└─ [+] cdk-hnb659fds-assets-654654314383-${AWS::Region}
[~] AWS::EC2::Instance myEc2App myEc2App51DF0DDD replace
└─ [~] KeyName (requires replacement)
├─ [-] rockaws
└─ [+] windows
✨ Number of stacks with differences: 1
ninjamac@ip-192-168-1-2 app % cdk synth
[WARNING] aws-cdk-lib.aws_ec2.InstanceProps#keyName is deprecated.
- Use `keyPair` instead - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#using-an-existing-ec2-key-pair
This API will be removed in the next major release.
Resources:
VPCB9E5F0B4:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: AppStack/VPC
Metadata:
aws:cdk:path: AppStack/VPC/Resource
ninjamac@ip-192-168-1-2 app % cdk deploy
[WARNING] aws-cdk-lib.aws_ec2.InstanceProps#keyName is deprecated.
- Use `keyPair` instead - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#using-an-existing-ec2-key-pair
This API will be removed in the next major release.
✨ Synthesis time: 5.39s
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:
Stack AppStack
IAM Statement Changes
┌───┬──────────────────────────────────────────────────────────────────────────────────────────────────┬────────┬───────────────────────────────────┬────────────────────────────────────────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │
├───┼──────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${simple-instance-1-role.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
├───┼──────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:654654314383:security-group/${VPC.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │
│ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupEgress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupIngress │ │ │
└───┴──────────────────────────────────────────────────────────────────────────────────────────────────┴────────┴───────────────────────────────────┴────────────────────────────────────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
└───┴────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
Security Group Changes
┌───┬───────────────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼───────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${myEc2App/InstanceSecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴───────────────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)? y
AppStack: deploying... [1/1]
AppStack: creating CloudFormation changeset...
AppStack: creating CloudFormation changeset...
✅ AppStack
✨ Deployment time: 186.83s
Stack ARN:
arn:aws:cloudformation:ap-southeast-2:654654314383:stack/AppStack/c1acb320-203d-11f0-9b14-0254438cd5a3
✨ Total time: 192.22s
登录aws console 验证
销毁资源
ninjamac@ip-192-168-1-2 app % cdk destroy
结论
AWS CDK 让编写 IaC、配置、部署、更新和销毁基础设施变得非常轻松。您可以编写测试来确保部署正确。
这是一个简单的示例,部署一个 EC2 实例似乎需要花费不少功夫。但是,随着我们学习本系列的深入,您将意识到它对复杂的基础设施有多么有益。