亚马逊AWS官方博客

使用 AWS CDK 加速 EKS 集群部署

背景介绍

2020年3月,Amazon Elastic Kubernetes Service(Amazon EKS)在AWS中国区域正式发布。发布后,越来越多中国区域的客户使用这一高可用、容错的托管式Kubernetes服务。作为一项完全托管的服务,Amazon EKS让客户可以使用AWS上的Kubernetes轻松部署、管理和扩展容器化的应用程序,不必管理底层基础设施,客户能够更专注于应用程序的开发。借助Amazon EKS,客户可以充分利用AWS平台的特性,例如可扩展性,可靠性和可用性,以及与AWS网络和安全服务的集成,例如,用于负载分配的Application Load Balancer,用于基于IAM的访问控制和使用VPC网络访问Pod。

随着中国区使用EKS的用户不断增加,用户和AWS合作伙伴对于EKS部署灵活性的需求也不断增加,主要表现在:

  • 快速构建EKS测试/开发环境
  • 整合EKS到现有的体系架构中
  • 快速在其他区域构建EKS环境
  • 快速为其他Account构建EKS环境
  • 快速构建多个EKS环境
  • 快速构建基于EKS的应用解决方案

针对上述的客户痛点,AWS提供了多种解决方法。

常用的部署方法

对于EKS的部署,通常采用两种方法,eksctl和CDK。第一种方法使用来自Weaveworks的名为eksctl的命令行工具。第二种方法使用AWS CDK。在这两种情况下,IaC(Infrastructure as Code)工具都会在后台创建和部署CloudFormation堆栈。

eksctl是用于在EKS上创建集群的简单CLI工具,由Goave编写,使用CloudFormation堆栈,由Weaveworks创建。

CDK(AWS Cloud Development Kit)是AWS发布的工具,与开发YAML或JSON传统的方法相比,开发人员可以更加快速来构建基础架构。使用诸如Typescript或Python之类的编程语言,开发人员可以利用函数快速构建代码框架,并且该工具包实现了安全开发的最佳实践,例如,最小权限原则。

相对于eksctl提供的EKS集群管理能力,CDK提供了一种更高层级的抽象,通过CDK Constructs,用户可以将一套基础架构绑定到可重用的组件,任何其他用户都可以使用该组件并组合到应用程序中。同时CDK支持TypeScript, JavaScript, Python, Java, and C#/.Net等多种语言,用户可以选择其中一种定义可复用的AWS基础设施。本文将介绍使用CDK部署EKS集群。

体系架构

考虑这样一种场景:AWS的合作伙伴开发了一款基于EKS的应用解决方案,要为客户在AWS上部署该解决方案。其中Partner Account为AWS合作伙伴账户,Customer Account为合作伙伴的客户账户,合作伙伴将应用解决方案部署到Customer Account。以下我们以部署EKS为例,讲解部署过程。此方法亦可延展到AWS的其他服务。

总体架构如下图所示:

  1. 在客户账户(Customer Account)中创建IAM 角色;
  2. 在合作伙伴账户(Partner Account)中创建IAM用户,并授予sts:AssumeRole权限;
  3. 创建CDK应用;
  4. 部署EKS环境;
  5. 部署EKS应用,验证EKS正常工作。

采用这种方法,合作伙伴很容易将应用解决方案部署到多个客户账户。

实现过程

1. 在客户账户(Customer Account)中创建IAM角色

登陆客户账户的AWS管理控制台,进入”IAM“服务,点击“Create Role”,选择”Another AWS account“,输入Partner Account的ID(12位数字),点击”Next“,在”Attach permissions policies“中选择”AdministratorAccess“,点击”Next“, “Next Review“,在”Role Name“中输入“RoleCrossAccountSignin”,点击”Create“按钮创建一个角色。

为了便于演示,在这里我们赋予了这个角色管理员权限。在实际应用中,我们应遵循最小权限原则。

2. 在合作伙伴账户(Partner Account)中创建IAM用户

登陆合作伙伴账户的AWS管理控制台,进入”IAM“服务,点击”Create User”,在”User name“中输入”testuser“,在”Access Type“”中选择””Programmatic access“”,仅允许通过程序访问。点击Next:Permissions,选择”Attach existing policies directly“,选中”AdministratorAccess“复选框,点击”Next:Tags“,点击”Create User“,然后下载生成的csv文件,里面存放着刚刚创建用户的Access key和Secret Key。

在Summary页面中点击”Add inline policy“,点击”JSON“,在文本框中中输入

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": "arn:aws:iam::<Customer Account ID>:role/RoleCrossAccountSignin"
        }
    ]
}

在”Resource”中,替换成真实的12位”Customer Account ID”。点击”Review Policy”,在”Name”中输入”mySTSPolicy”,点击”Create policy”。

至此,我们建立了客户账户对合作伙伴账户的信任关系。

3. 创建CDK应用

您可以通过Amazon EC2、Cloud 9或者本地电脑构建CDK应用环境,本文的以下操作均在Mac的笔记本电脑中执行,如果您的用户不是root,在下列各命令前需要加上sudo。在安装CDK前,请确保您已经安装了Node.js并更新到了最新版本。CDK支持TypeScript,Java,Python,C#等多种语言,本文中将使用TypeScript语言。

  • 安装TypeScript并升级到最新版本
npm install -g typescript
  • 安装CDK并升级到最新版本
npm install -g aws-cdk --force cdk --version
  • 安装AWS CLi工具并升级到最新版本
pip install awscli --upgrade --user
  • 配置AWS CLi
aws configure

依次输入刚刚创建Partner Account用户的Access Ke,Secret Key,us-east-1(默认访问的区域),输出格式默认位json。

  • 创建CDK应用程序
mkdir cdkeks &amp;&amp; cd cdkeks cdk init app --language typescript
  • 新打开一个终端窗口,并运行以下命令
cd cdkeks npm run watch
  • 在原来的命令行窗口执行以下命令,安装cdk所需要的组件
npm install @aws-cdk/aws-eks npm install @aws-cdk/aws-ec2 npm install @aws-cdk/aws-iam
  • 编辑./lib/cdkeks-stack.ts 文件。用户可以指定在已有的VPC,也可以让CDK建立新的VPC。在这里,我们让CDK创建新的VPC。CDK将默认创建两个m5.large的实例作为EKS集群的数据节点,在这里我们指定EKS集群的版本为1.16。同时还为集群创建了一个t3.large实例作为数据节点。
import * as cdk from '@aws-cdk/core';
import * as eks from '@aws-cdk/aws-eks';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import { InstanceType, Vpc } from '@aws-cdk/aws-ec2';

export class CdkeksStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // use an existing vpc or create a new one
    const vpc = this.node.tryGetContext('use_default_vpc') === '1' ?
      ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true }) :
      this.node.tryGetContext('use_vpc_id') ?
        ec2.Vpc.fromLookup(this, 'Vpc', { vpcId: this.node.tryGetContext('use_vpc_id') }) :
        new ec2.Vpc(this, 'Vpc', { maxAzs: 3, natGateways: 1 });

    const clusterAdmin = new iam.Role(this, 'RoleCrossAccountSignin', {
      assumedBy: new iam.AccountRootPrincipal()
    });

    // create the cluster and a default managed nodegroup of 2 x m5.large instances
    const cluster = new eks.Cluster(this, 'Cluster', {
      vpc,
      mastersRole: clusterAdmin,
      version: '1.16',
    });

    // create  t3.large ec2 instances
    
    cluster.addCapacity('computerNodes', {
      maxCapacity: 2,
      instanceType: new ec2.InstanceType('t3.large'),
      bootstrapOptions: {
        kubeletExtraArgs: '--node-labels foo=bar'
      },
    })
  }
}
  • 编辑./lib/cdkeks.ts 文件。从环境变量中获取当前的region和account信息。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { CdkeksStack } from '../lib/cdkeks-stack';

const app = new cdk.App();

const env = {
  region: process.env.CDK_DEFAULT_REGION,
  account: process.env.CDK_DEFAULT_ACCOUNT
};

const cdkeksStack = new CdkeksStack(app, 'CdkeksStack', { env })
  • 观察另一个终端窗口,应显示编译成功。

  • 使用synth命令查看生成的ymal文件。
cdk synth
  • 至此,使用CDK部署EKS的脚步已经完成。

4. 部署EKS环境

下面的脚本演示了使用合作伙伴账号为客户账号部署EKS环境。

  • 查看当前用户信息,输出为合作伙伴账户刚刚创建testuser的信息。
aws sts get-caller-identity
  • 在AWS CLI中使用AssumeRole。替换为12位的客户账户ID。
aws sts assume-role --role-arn "arn:aws:iam::&lt;Customer Account ID&gt;:role/RoleCrossAccountSignin" --role-session-name mytempsession --query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' --output text

上面命令的输出为三部分,分别为Access Key,Secret Key和Session Token。

  • 设置环境变量
unset AWS_SECURITY_TOKEN AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
export ACCOUNT_ID="<Customer Account ID>"
export AWS_DEFAULT_REGION="us-west-2"    #将部署到us-west-2
export AWS_ACCESS_KEY_ID=********        #上面命令输出的Access Key
export AWS_SECRET_ACCESS_KEY=********    #上面命令输出的Secret Key
export AWS_SESSION_TOKEN=********        #上面命令输出的Session Token
export AWS_SECURITY_TOKEN=********       #上面命令输出的Session Token
  • 验证AssumeRole正常工作
aws s3 ls

正常应该显示客户账号s3存储桶的列表。

  • 查看当前用户信息,输出为当前客户账号AssumeRole的信息。
    aws sts get-caller-identity 
  • 运行bootstrap,在美西二区构建S3 bucket,用于CloudFormation在部署过程中的文件交互。
AWS_REGION=us-west-2 cdk bootstrap
  • 在美西二区部署EKS集群
AWS_REGION=us-west-2 cdk deploy

等待18-20分钟左右,如果命令执行成功,您将看到以下的输出。

通过修改AWS_REGION环境变量,很容易将EKS集群部署到其他区域。

5. 部署EKS应用

最后我们部署一个应用来验证EKS集群是否正常工作。首先安装eksctl和kubectl。

  • 安装eksctl
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
  • 安装kubectl
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
kubectl version --client
  • 安装 authenticator
curl -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/aws-iam-authenticator

chmod +x ./aws-iam-authenticator

mkdir -p $HOME/bin && cp ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator && export PATH=$HOME/bin:$PATH
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
  • 运行update-kubeconfig更新集群配置,该命令来自上面cdk deploy命令的输出,参数是刚刚创建集群的名称、EKS所在的区域以及集群管理角色具有角色的arn。
aws eks update-kubeconfig --name Cluster9EE0221C-06fa9f92588e4d519a9b473c5cacf382 --region us-west-2 --role-arn arn:aws:iam::************:role/CdkeksStack-AdminC75D2A91-L456LSCQF6OU
  • 查看集群具有的工作节点
kubectl get nodes

我们可以看到以下输出,一共有3个节点,两个m5.large和一个t3.large。

NAME STATUS ROLES AGE VERSION
ip-10-0-105-222.ec2.internal Ready <none> 6d2h v1.16.8-eks-e16311 
ip-10-0-146-67.ec2.internal Ready <none> 6d2h v1.16.8-eks-e16311 
ip-10-0-171-116.ec2.internal Ready <none> 6d2h v1.16.8-eks-e16311
  • 部署测试应用

编辑文件 2048.k8s.yaml

apiVersion: v1
kind: Service
metadata:
  name: my2048-service
spec:
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: my2048-k8s
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my2048-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my2048-k8s
  template:
    metadata:
      labels:
        app: my2048-k8s
    spec:
      containers:
        - image: alexwhen/docker-2048
          name: "my2048"
          ports:
            - containerPort: 80
  • 运行部署命令
kubectl apply -f 2048.k8s.yaml
  • 查看服务信息
kubectl get service/my2048-service

我们将看到如下输出

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my2048-service LoadBalancer 172.20.88.7 a6ba48df2f1774376a8e065336c8bd44-1753238188.us-east-1.elb.amazonaws.com 80:31276/TCP 4d20h
  • 在浏览器打开应用的endpoint地址,你可以看到2048的游戏页面。

  • 删除应用
kubectl delete -f 2048.k8s.yaml
  • 删除Stack,运行以下命令将提示您是否删除,选择”Y”。
AWS_REGION=us-west-2 cdk destroy CdkeksStack

总结

本文通过介绍使用AWS CDK部署EKS集群,用户或合作伙伴可以方便的扩展到其部署方案中。通过这种方式,不仅仅可以为其他账号部署EKS集群,也可为自己账号下的支持EKS的任何区域部署EKS集群,真正做到了”Write once, run anywhere”。同时您可以将该脚本集成到整个应用的部署方案中,为在AWS上进行快速测试、开发和运维提供支持。

参考文章

本篇作者

张铮

AWS APN 合作伙伴解决方案架构师,主要负责 AWS (中国)合作伙伴的方案架构咨询和设计工作,同时致力于 AWS 云服务在国内的应用及推广。