亚马逊AWS官方博客

使用 AWS CDK 轻松构建云原生应用之 EKS 平台

专栏入口:云原生专栏

AWS CDK V.S. AWS CloudFormation

熟悉AWS或者云计算的朋友一定都知道AWS CloudFormation。我们创建一个描述所有AWS资源的JSON格式或者YAML格式的模板,通过AWS CloudFormation可以快速地对这些资源进行建模和设置,并且可以方便地对资源进行管理。一切都可以自动化完成而无需手动操作。很多AWS客户已经采用CloudFormation实现了基础设施即代码(Infrastructure as Code)。

但是使用时会发现,从零开始写模板还是一件繁琐的工作,尤其是针对于同一种AWS服务,模板里的大部分内容是相同的,只是在一些属性和参数上有所差别。曾经在向一个客户演示完CloudFormation后,客户抛出一个问题:有没有一个自动生成模板的工具?这真是一个好问题!软件的本质就是不断抽象并简化工作。所以AWS推出了云开发工具包AWS Cloud Development Kit,简称AWS 。这是一种开源软件开发框架,提供一种更高层级的抽象,使用者可以使用熟悉的编程语言模拟和预置云应用程序资源(construct library)。而CDK的底层会自动生成CloudFormation模板,然后调用CloudFormation来完成所有的资源创建工作。AWS的CDK开发团队基于CloudFormation构建了AWS资源的L2 construct,完成从对象定义到CFN模板的转换。所以采用CDK比采用CloudFormation的代码会少很多很多,大大简化工作量。另外特别要强调的,CDK是一个完全开源的项目!(https://github.com/aws/aws-cdk)也就是说只要你愿意,完全可以向里面贡献你的代码!在今年的AWS re:Invent上有一个session OPN205_R就是介绍如何向AWS CDK贡献代码,大家可以看这个视频了解.

关于CDK我们在这篇文章里不做更多介绍啦,具体可以参考AWS的官方网站,上面有很详细的介绍:https://aws.amazon.com/cn/cdk/

 

AWS CDK的实践之路

在近期的一个项目里,我们帮助客户使用CDK轻松快速的构建了整套云原生应用架构,包括Amazon Elastic Kubernetes Service(简称EKS)平台,应用架构,监控日志,调用链跟踪以及CICD等。我们将会作为一个系列介绍。这篇文章是第一篇,介绍如何构建基础网络架构和EKS平台。搭建好的架构如下图:

 

以下是我们的具体步骤:

1.安装CDK

可以按照AWS的官方文档介绍来安装CDK:https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html

安装好后跑一下命令,就可以看到CDK cli已经安装完成了:

 

2.初始化我们的应用程序

该命令在一个AWS账号下从零创建所有应用架构依赖的组件,我们给创建好的应用命名为cdk-repo:

 

我们的应用程序都是使用Python编写的。除了Python,CDK目前还支持Javascript, Typescript,Python,.NET,Java并且全都GA了,所以你可以方便的选用自己熟悉的语言编写。

 

3.创建基础网络架构

在任何一个AWS账号下的一个全新区域中,都需要搭建基础的网络设施,包括AWS Virtual Private Cloud(简称VPC)、子网、堡垒机、NAT等基本组件,为应用运行准备基础环境。

 3.1 创建基础网络架构

我们通过以下一段代码,可以方便地创建出一个AWS上标准的基础网络架构,包括:一个指定了CIDR和名字的VPC,一个NAT Gateway,以及每个Availability Zone(简称AZ)中的三个子网,分别是:公有子网——允许所有出入公网;私有子网——没有直接的出入公网的路由,但是可以通过堡垒机访问私有子网内的资源,可以通过NAT gateway出到公网;保护子网——没有出入公网的路由,通常用于数据库。

 

注意:如果你是在2AZ的区域中运行该代码,会创建3*2=6个子网,如果在3AZ的区域中运行该代码,会创建3*3=9个子网。该代码可以自动适配到AZ的数量。

3.2 为VPC和子网打tag

如果你在AWS上创建过EKS,应该了解EKS对它所在的VPC和子网的tag有所要求。所以我们采用以下代码打tag:

 

 

3.3 创建堡垒机

CDK中已经有实现的construct,可以快速的创建堡垒机。但是我为了使用自己预创建的key,所以采用ec2 instance的方式来创建堡垒机,采用以下代码:

 

可以看到,堡垒机的安全组设置也是非常方便,用两行代码就可以允许任何ip的ssh访问。

注意:建议针对该堡垒机开启VPC flow log,可以记录进出该堡垒机的网络流量,做到安全监控。由于L2 construct的instance不支持设置Elastic Network Interface(ENI),从而无法开启VPC flow log,所以需要基于CfnInstance完成。

3.4 传递参数

我们会对整个应用进行模块化编排,将不同的模块划分到不同的Stack,方便我们更好的组织、管理以及复用代码。我们会在最后一篇文章介绍整体的Stack编排和设计。这里只介绍我们将VPC名称和堡垒机的安全组作为参数传递给其他Stack,包括EKS Stack。

 

3.5 设置Stack入口

我们在app.py中设置用于基础网络架构的Stack的入口,也就是LandingzoneStack:

 

3.6 查看Stack

在命令行中通过以下命令查看我们刚才创建的stack:

 

通过命令cdk synth可以打印出生成的CloudFormation模板,下面截图中只展示了模板的一部分,可以看到短短的几句CDK应用可以生成好几十行的模板,所以CDK对比CloudFormation非常高效。通常我们打出CloudFormation模板只是为了调试。

 

4 创建EKS平台

4.1 创建EKS集群

Amazon EKS是AWS上托管的K8S容器管理平台,关于EKS这里不做过多描述了,具体的可以参考AWS的官方文档:https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/what-is-eks.html

这里我们采用CDK来创建EKS集群,具体的代码和解释如下:

 

里面的参数都可以按照需要修改,在添加node节点时也可以选择添加spot instance。

4.2 向EKS的node group授予policy

有些情况下,我们要向EKS的节点授予一定权限,比如我们需要node上的应用从System Manager的parameter store中取信息,那我们需要向node group授予SSM的policy,通过以下代码实现:

 

通常在用CDK创建EKS集群时会默认创建一个AutoScalingGroup,然后在add capacity的时候会创建第二个AutoSacalingGroup,所以在授予policy时需要向两个ASG中的node都授予。代码里也有两个部分。在我们的应用中我们设置了default capacity为0,所以禁止了创建默认ASG的行为,就只有一个ASG,不需要向默认创建的ASG授予policy。这时候如果执行第一部分代码会报错,所以前面两行代码注释掉了。在实际使用中要注意这两者的差别,两个ASG需要分别控制和管理。

所有Policy都可以采用这种方式授予node group,包括AWS托管的Policy和自定义的policy。

4.3 创建K8S资源

通过以上步骤我们创建了一个具有control plan控制平面、node group和ASG的Amazon EKS集群,除了自身的coredns外不具有其他的Kubernetes资源。但是我们知道K8S中有很多Kubernetes资源包括deployment、services、RBAC、ingress等是典型云原生应用运行必不可少的部分。由于这类资源对CloudfFormation来说并不属于AWS托管的服务,无法投过CDK提供的L2 construct library直接部署,因此在AWS CDK中会透过@aws-cdk/aws-cloudformation的CustomerResource Class来实现,也就是@aws-cdk/aws-eks里面的KubernetesResource Class,而它背后是通过AWS Lambda调度lambda-layer-kubectl Lambda Layer来运行kubectl客户端创建出K8S资源,详细可以参考@aws-cdk/aws-ek/lib/k8s-resource.ts).

但是CDK中现在还不支持以YAML文件定义的资源,而只能添加一组JSON Array。因此我单独实现了一个小工具用来将YAML文件转换为需要的JSON Array。

 

现在,我们可以将转换好的JSON Array提供给cluster作为资源。比如说,在以下的示例代码中为该EKS集群增加了RBAC和Ingress controller资源:

 

在应用运行的时候,这些资源会重新组装成为manifest.yaml,通过kubectl的方式在该EKS平台上运行,创建出需要的K8S资源。

所有需要的资源都可以采用这种方式添加到cluster中。

4.4 定义EKSStack的入口

我们在app.py中定义EKS Stack的执行入口

 

这里我们还用dependency表示EKS依赖于Landingzone,也就是先要创建AWS上基础的网络架构,在创建EKS平台。

4.5 查看Stack

现在我们在命令行中运行ls,可以看到两个Stack,他们之间是有相互依赖关系的。

 

同样我们也可以通过cdk synth查看生成的CloudFormation模板。

 

5.运行Stack

我们通过cdk deploy依次运行Landingzone Stack和EKS Stack,分别创建基础网络和EKS平台。当然我们也可以直接运行EKS stack,因为它对Landingzone stack有依赖,所以cdk会先检查Landingzone是否创建了,如果没有的话会先创建Landingzone。

 

可以看到,对于一些IAM和安全组相关的资源创建/删除/更改,CDK会提示要求确认。其他资源则不会有提示。在选择了“y”之后,就会创建cloudformation changeset,然后通过cloudformation创建资源,同时在console上输出Cloudformation的运行过程。

这时我们也可以在AWS console中的Cloudformation查看运行状态。

 

创建过程中,如果我们把CDK运行的命令行窗口关掉也不要紧,Cloudformation仍然会继续执行。

执行好后,我们在命令行和console里都可以看到运行结果。

 

我们也可以采用同样的方式创建EKS Stack。可以在AWS console中查看创建的结果:

 

可以看到有两个stack,其中一个带有nested字样的是嵌套stack,是用来运行kubectl客户端的lambda layer。

我们再在EKS的界面看一下:

 

现在我们就可以登入EKS里玩一下啦!~

 

总结

 

这篇文章是我们云原生专栏的开篇。在这篇文章里,我们了解了如何采用CDK轻松快速地在一个全新的AWS账号下构建基础网络架构,并在其上构建EKS平台及需要的K8S资源。

AWS CDK对底层CloudFormation进行了更高级别抽象,针对一些常用的架构设计方式,比如每个AZ中几个子网、子网的类型、安全组设置、IAM权限设置等提供了高级别的方法,可以更加方便和高效地创建AWS资源,而且多语言支持简单易上手。

但是CDK的L2 construct在对CloudFormation资源封装的同时也缺失了一些底层属性,所以可以满足大部分使用场景但不能满足所有。如果有需要的属性缺失的话,可以基于L1 construct 实现,也可以在github上提交Issue或者自己提交PR修改L2。AWS CDK是一个非常活跃的开源项目,更新很快,也欢迎大家贡献维护。

 

感谢

感谢解决方案架构师专家Pahud Hsieh (謝洪恩)、解决方案架构师经理薛军和安全顾问Kelvin Medina对本文章提出的宝贵意见。

 

下一篇介绍

下一篇中,我们会了解如何采用CDK构建应用所需要的日志收集和展示系统,

 

本篇作者

张芸

张芸,AWS资深云计算及应用架构顾问。目前在AWS负责针对企业客户的云原生转型,微服务、容器化和无服务器改造等咨询实施服务。曾就职于EMC中国研究院,具有10年以上云计算和大数据相关技术研究和开发经验。拥有多项美国和中国专利,涉及云计算及服务,分布式系统,软件定义数据中心和自动化运维等领域,合作编著图书《大数据—战略·技术·实践》。