Skip to content

Commit

Permalink
feat(eks): managed nodegroup with custom AMI and launch template supp…
Browse files Browse the repository at this point in the history
…ort (#9881)

- This PR allows you to:
1. use `LaunchTemplate` for the managed nodegroups
2. specify custom AMI in the `LaunchTemplate`

- `prop.vpc` of Pinger is now `ec2.IVpc`
- bump cluster k8s version in the integration testing from `1.16` to `1.17`

Closes: #9873 
Closes: #9924

## Note

At this moment we use the property override to make it work. When cfn spec is updated we can use `CfnNodeGroup` to specify launch template natively, which should be backward-compatible with no breaking changes.

Users will need to create `CfnLaunchTemplate` resource and pass the resource as the property to the nodegroup untill we support the LaunchTemplate L2(#6734).

## TODO

- [x] integ testing
- [x] unit testing
- [x] update README


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
pahud authored Sep 9, 2020
1 parent effceb5 commit 5c294fb
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 16 deletions.
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,34 @@ cluster.addNodegroup('nodegroup', {
});
```

#### Custom AMI and Launch Template support

Specify the launch template for the nodegroup with your custom AMI. When using a custom AMI,
Amazon EKS doesn't merge any user data. Rather, You are responsible for supplying the required
bootstrap commands for nodes to join the cluster. In the following sample, `/ect/eks/bootstrap.sh` from the AMI will be used to bootstrap the node. See [Using a custom AMI](https://docs.aws.amazon.com/en_ca/eks/latest/userguide/launch-templates.html) for more details.

```ts
const userData = ec2.UserData.forLinux();
userData.addCommands(
'set -o xtrace',
`/etc/eks/bootstrap.sh ${this.cluster.clusterName}`,
);
const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', {
launchTemplateData: {
// specify your custom AMI below
imageId,
instanceType: new ec2.InstanceType('t3.small').toString(),
userData: Fn.base64(userData.render()),
},
});
this.cluster.addNodegroup('extra-ng', {
launchTemplate: {
id: lt.ref,
version: lt.attrDefaultVersionNumber,
},
});
```

### ARM64 Support

Instance types with `ARM64` architecture are supported in both managed nodegroup and self-managed capacity. Simply specify an ARM64 `instanceType` (such as `m6g.medium`), and the latest
Expand Down
41 changes: 41 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/managed-nodegroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ export interface NodegroupRemoteAccess {
readonly sourceSecurityGroups?: ISecurityGroup[];
}

/**
* Launch template property specification
*/
export interface LaunchTemplate {
/**
* The Launch template ID
*/
readonly id: string;
/**
* The launch template version to be used (optional).
*
* @default - the default version of the launch template
*/
readonly version?: string;
}

/**
* The Nodegroup Options for addNodeGroup() method
*/
Expand Down Expand Up @@ -160,6 +176,12 @@ export interface NodegroupOptions {
* @default - None
*/
readonly tags?: { [name: string]: string };
/**
* Launch template used for the nodegroup
* @see - https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
* @default - no launch template
*/
readonly launchTemplate?: LaunchTemplate;
}

/**
Expand Down Expand Up @@ -268,6 +290,25 @@ export class Nodegroup extends Resource implements INodegroup {
tags: props.tags,
});

if (props.launchTemplate) {
if (props.diskSize) {
// see - https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
// and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-disksize
throw new Error('diskSize must be specified within the launch template');
}
if (props.instanceType) {
// see - https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html
// and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-eks-nodegroup.html#cfn-eks-nodegroup-disksize
throw new Error('Instance types must be specified within the launch template');
}
// TODO: update this when the L1 resource spec is updated.
resource.addPropertyOverride('LaunchTemplate', {
Id: props.launchTemplate.id,
Version: props.launchTemplate.version,
});
}


// managed nodegroups update the `aws-auth` on creation, but we still need to track
// its state for consistency.
if (this.cluster instanceof Cluster) {
Expand Down
99 changes: 88 additions & 11 deletions packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@
]
},
"Config": {
"version": "1.16",
"version": "1.17",
"roleArn": {
"Fn::GetAtt": [
"ClusterRoleFA261979",
Expand Down Expand Up @@ -1192,6 +1192,13 @@
"Arn"
]
},
"\\\",\\\"username\\\":\\\"system:node:{{EC2PrivateDNSName}}\\\",\\\"groups\\\":[\\\"system:bootstrappers\\\",\\\"system:nodes\\\"]},{\\\"rolearn\\\":\\\"",
{
"Fn::GetAtt": [
"ClusterNodegroupDefaultCapacityNodeGroupRole55953B04",
"Arn"
]
},
"\\\",\\\"username\\\":\\\"system:node:{{EC2PrivateDNSName}}\\\",\\\"groups\\\":[\\\"system:bootstrappers\\\",\\\"system:nodes\\\"]}]\",\"mapUsers\":\"[]\",\"mapAccounts\":\"[]\"}}]"
]
]
Expand Down Expand Up @@ -1574,7 +1581,7 @@
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
"Ref": "SsmParameterValueawsserviceeksoptimizedami117amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "t2.medium",
"IamInstanceProfile": {
Expand Down Expand Up @@ -1851,7 +1858,7 @@
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
"Ref": "SsmParameterValueawsserviceeksoptimizedami117amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "m6g.medium",
"IamInstanceProfile": {
Expand Down Expand Up @@ -2419,7 +2426,7 @@
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
"Ref": "SsmParameterValueawsserviceeksoptimizedami117amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "t3.large",
"IamInstanceProfile": {
Expand Down Expand Up @@ -2729,7 +2736,7 @@
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
"Ref": "SsmParameterValueawsserviceeksoptimizedami117amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "inf1.2xlarge",
"IamInstanceProfile": {
Expand Down Expand Up @@ -3036,6 +3043,48 @@
}
}
},
"ClusterNodegroupextrang2F1FB0D40": {
"Type": "AWS::EKS::Nodegroup",
"Properties": {
"ClusterName": {
"Ref": "Cluster9EE0221C"
},
"NodeRole": {
"Fn::GetAtt": [
"ClusterNodegroupDefaultCapacityNodeGroupRole55953B04",
"Arn"
]
},
"Subnets": [
{
"Ref": "VpcPrivateSubnet1Subnet536B997A"
},
{
"Ref": "VpcPrivateSubnet2Subnet3788AAA1"
},
{
"Ref": "VpcPrivateSubnet3SubnetF258B56E"
}
],
"ForceUpdateEnabled": true,
"ScalingConfig": {
"DesiredSize": 1,
"MaxSize": 1,
"MinSize": 1
},
"LaunchTemplate": {
"Id": {
"Ref": "LaunchTemplate"
},
"Version": {
"Fn::GetAtt": [
"LaunchTemplate",
"DefaultVersionNumber"
]
}
}
}
},
"ClustermanifestHelloApp078A45D8": {
"Type": "Custom::AWSCDK-EKS-KubernetesResource",
"Properties": {
Expand Down Expand Up @@ -3528,6 +3577,30 @@
}
}
},
"LaunchTemplate": {
"Type": "AWS::EC2::LaunchTemplate",
"Properties": {
"LaunchTemplateData": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "t3.small",
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash\nset -o xtrace\n/etc/eks/bootstrap.sh ",
{
"Ref": "Cluster9EE0221C"
}
]
]
}
}
}
}
},
"AWSCDKCfnUtilsProviderCustomResourceProviderRoleFE0EE867": {
"Type": "AWS::IAM::Role",
"Properties": {
Expand Down Expand Up @@ -4206,21 +4279,25 @@
"Type": "String",
"Description": "Artifact hash for asset \"a298dd278c9ef814ebac4c9d8b2dc8e1b8374a14c5b7d0e79f041a296668f5dc\""
},
"SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"SsmParameterValueawsserviceeksoptimizedami117amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/eks/optimized-ami/1.16/amazon-linux-2/recommended/image_id"
"Default": "/aws/service/eks/optimized-ami/1.17/amazon-linux-2/recommended/image_id"
},
"SsmParameterValueawsserviceeksoptimizedami116amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"SsmParameterValueawsserviceeksoptimizedami117amazonlinux2arm64recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/eks/optimized-ami/1.16/amazon-linux-2-arm64/recommended/image_id"
"Default": "/aws/service/eks/optimized-ami/1.17/amazon-linux-2-arm64/recommended/image_id"
},
"SsmParameterValueawsservicebottlerocketawsk8s115x8664latestimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/bottlerocket/aws-k8s-1.15/x86_64/latest/image_id"
},
"SsmParameterValueawsserviceeksoptimizedami116amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"SsmParameterValueawsserviceeksoptimizedami117amazonlinux2gpurecommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/eks/optimized-ami/1.17/amazon-linux-2-gpu/recommended/image_id"
},
"SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/eks/optimized-ami/1.16/amazon-linux-2-gpu/recommended/image_id"
"Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id"
}
}
}
33 changes: 30 additions & 3 deletions packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import { App, CfnOutput, Duration, Token } from '@aws-cdk/core';
import { App, CfnOutput, Duration, Token, Fn } from '@aws-cdk/core';
import * as eks from '../lib';
import * as hello from './hello-k8s';
import { Pinger } from './pinger/pinger';
import { TestStack } from './util';


class EksClusterStack extends TestStack {

private cluster: eks.Cluster;
private vpc: ec2.Vpc;
private vpc: ec2.IVpc;

constructor(scope: App, id: string) {
super(scope, id);
Expand All @@ -31,7 +32,7 @@ class EksClusterStack extends TestStack {
vpc: this.vpc,
mastersRole,
defaultCapacity: 2,
version: eks.KubernetesVersion.V1_16,
version: eks.KubernetesVersion.V1_17,
secretsEncryptionKey,
});

Expand All @@ -51,6 +52,8 @@ class EksClusterStack extends TestStack {

this.assertNodeGroupArm();

this.assertNodeGroupCustomAmi();

this.assertSimpleManifest();

this.assertSimpleHelmChart();
Expand Down Expand Up @@ -118,6 +121,30 @@ class EksClusterStack extends TestStack {
nodeRole: this.cluster.defaultCapacity ? this.cluster.defaultCapacity.role : undefined,
});
}
private assertNodeGroupCustomAmi() {
// add a extra nodegroup
const userData = ec2.UserData.forLinux();
userData.addCommands(
'set -o xtrace',
`/etc/eks/bootstrap.sh ${this.cluster.clusterName}`,
);
const lt = new ec2.CfnLaunchTemplate(this, 'LaunchTemplate', {
launchTemplateData: {
imageId: new eks.EksOptimizedImage().getImage(this).imageId,
instanceType: new ec2.InstanceType('t3.small').toString(),
userData: Fn.base64(userData.render()),
},
});
this.cluster.addNodegroup('extra-ng2', {
minSize: 1,
// reusing the default capacity nodegroup instance role when available
nodeRole: this.cluster.defaultNodegroup?.role || this.cluster.defaultCapacity?.role,
launchTemplate: {
id: lt.ref,
version: lt.attrDefaultVersionNumber,
},
});
}
private assertNodeGroupArm() {
// add a extra nodegroup
this.cluster.addNodegroup('extra-ng-arm', {
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-eks/test/pinger/pinger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as cr from '@aws-cdk/custom-resources';
export interface PingerProps {
readonly url: string;
readonly securityGroup?: ec2.SecurityGroup;
readonly vpc?: ec2.Vpc;
readonly vpc?: ec2.IVpc;
}
export class Pinger extends Construct {

Expand Down Expand Up @@ -40,4 +40,4 @@ export class Pinger extends Construct {
return Token.asString(this._resource.getAtt('Value'));
}

}
}
Loading

0 comments on commit 5c294fb

Please sign in to comment.