この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
おはようございます、加藤です。先日、AWS CDK(以降、CDK)にアップデートがあり、Amazon Elastic Kubernetes Service(以降、EKS)を作成する際にKubernetes(以降、k8s)リソースも作成が可能になりました。
CDK 1.4.0 で Kubernetes リソースをプロビジョンできるぞ!!!
/ "aws-cdk/README.md at v1.4.0 · aws/aws-cdk · GitHub" https://t.co/PWVu60lBxi
— ポジティブな Tori (@toricls) August 14, 2019
試しにCloudFormation(以降、CFn)を生成してみると、k8sリソースがカスタムリソースを使っている事がわかりました。
Type: Custom::AWSCDK-EKS-KubernetesResource
AwsAuthmanifest00905E16:
Type: Custom::AWSCDK-EKS-KubernetesResource
Properties:
ServiceToken:
Fn::GetAtt:
- EksKubernetesResourceHandlerC6DECF98
- Arn
Manifest:
Fn::Join:
- ""
- - '[{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"aws-auth","namespace":"kube-system"},"data":{"mapRoles":"[{\"rolearn\":\"'
- Fn::GetAtt:
- EksDefaultCapacityInstanceRole2307F75A
- Arn
- '\",\"username\":\"system:node:{{EC2PrivateDNSName}}\",\"groups\":[\"system:bootstrappers\",\"system:nodes\"]},{\"rolearn\":\"arn:aws:iam::'
- Ref: AWS::AccountId
- ':role/cm-001\",\"username\":\"001\",\"groups\":[\"system:masters\"]},{\"rolearn\":\"arn:aws:iam::'
- Ref: AWS::AccountId
- ':role/cm-002\",\"username\":\"002\",\"groups\":[\"system:masters\"]},{\"rolearn\":\"arn:aws:iam::'
- Ref: AWS::AccountId
- ':role/cm-003\",\"username\":\"003\",\"groups\":[\"system:masters\"]},{\"rolearn\":\"arn:aws:iam::'
- Ref: AWS::AccountId
- :role/cm-004\",\"username\":\"004\",\"groups\":[\"system:masters\"]}]","mapUsers":"[]","mapAccounts":"[]"}}]
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Metadata:
aws:cdk:path: EksStack/AwsAuth/manifest/Resource/Default
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Modules: aws-cdk=1.4.0,@aws-cdk/assets=1.4.0,@aws-cdk/aws-autoscaling=1.4.0,@aws-cdk/aws-autoscaling-common=1.4.0,@aws-cdk/aws-cloudformation=1.4.0,@aws-cdk/aws-cloudwatch=1.4.0,@aws-cdk/aws-ec2=1.4.0,@aws-cdk/aws-eks=1.4.0,@aws-cdk/aws-elasticloadbalancingv2=1.4.0,@aws-cdk/aws-events=1.4.0,@aws-cdk/aws-iam=1.4.0,@aws-cdk/aws-kms=1.4.0,@aws-cdk/aws-lambda=1.4.0,@aws-cdk/aws-s3=1.4.0,@aws-cdk/aws-s3-assets=1.4.0,@aws-cdk/aws-sqs=1.4.0,@aws-cdk/aws-ssm=1.4.0,@aws-cdk/core=1.4.0,@aws-cdk/cx-api=1.4.0,@aws-cdk/region-info=1.4.0,jsii-runtime=node.js/v10.16.0
以前、EKSクラスターは、Type: AWS::EKS::Clusterで作成されていましたが、こちらもカスタムリソースで作成されています。
カスタムリソースを使用したくない場合は、クラスターのプロパティでkubectlEnabled: false
を設定する事で対応が可能です。(この場合は、CDKでk8sリソースの作成が出来なくなります)
Type: Custome::AWSCDK-EKS-Cluster
EksBC5472D2:
Type: Custom::AWSCDK-EKS-Cluster
Properties:
ServiceToken:
Fn::GetAtt:
- EksResourceHandler52D8126A
- Arn
Config:
roleArn:
Fn::GetAtt:
- EksClusterRole457AF3D0
- Arn
version: "1.13"
resourcesVpcConfig:
securityGroupIds:
- Fn::GetAtt:
- EksControlPlaneSecurityGroupDC02C823
- GroupId
subnetIds:
- Fn::ImportValue: NetworkStack:ExportsOutputRefVPCPublicSubnet1SubnetB4246D30D84F935B
- Fn::ImportValue: NetworkStack:ExportsOutputRefVPCPublicSubnet2Subnet74179F3969CC10AD
- Fn::ImportValue: NetworkStack:ExportsOutputRefVPCPrivateSubnet1Subnet8BCA10E01F79A1B7
- Fn::ImportValue: NetworkStack:ExportsOutputRefVPCPrivateSubnet2SubnetCFCDAA7AB22CF85D
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Metadata:
aws:cdk:path: EksStack/Eks/Resource/Resource/Default
何ができるのか
CDKでk8sリソースを定義する事が可能になります。具体的には下記のように、Podなどのk8sリソースをCDKから作成できます。
const cluster = new eks.Cluster(this, 'hello-eks');
cluster.addResource('mypod', {
apiVersion: 'v1',
kind: 'Pod',
metadata: { name: 'mypod' },
spec: {
containers: [
{
name: 'hello',
image: 'paulbouwer/hello-kubernetes:1.5',
ports: [ { containerPort: 8080 } ]
}
]
}
});
とはいえ、CDKでk8sリソースが出来る事が役立つシチュエーションは極めて限定的であると考えています。
タイトルの通り、思いつくのは aws-auth でしょう。EKSクラスターは作成直後、作成したIAMユーザー/ロールからのみアクセスが可能です。今回の場合ではカスタムリソース=AWS Lambdaが作成を行っているので、Lambda関数に割り当てられたIAMロールのみがデフォルトではアクセス権限を持ちます。(kubectlEnabled: false
の場合は、cdk deploy
を実行したIAMユーザー/ロールがアクセス権限を持ちます)
ワーカーノードも例外ではなく、作成直後はノードステータスが NotReady
となっています。(EKSはコントロールプレーンのサービスで直接EC2と連携しないので、当然といえば当然ですが...)
aws-auth
有り難い事に、aws-auth専用のクラスが用意されており、簡単に記載する事ができます。
aws-auth
// aws-auth settings
const awsAuth = new AwsAuth(this, "AwsAuth", {
cluster: this.cluster
});
awsAuth.addRoleMapping(asg.role, {
groups: ["system:bootstrappers", "system:nodes"],
username: "system:node:{{EC2PrivateDNSName}}"
});
const users = ["001", "002", "003", "004"];
for (let i = 0; i < users.length; i++) {
awsAuth.addMastersRole(
Role.fromRoleArn(
this,
`${users[i]}`,
`arn:aws:iam::${this.account}:role/cm-${users[i]}`
),
`${users[i]}`
);
}
参考として、EKSスタック部分のコードを記載しておきます。
lib/eks-stack.ts
import { Construct, Stack, StackProps, CfnOutput } from "@aws-cdk/core";
import { Cluster, AwsAuth } from "@aws-cdk/aws-eks";
import {
InstanceClass,
InstanceSize,
InstanceType,
IVpc
} from "@aws-cdk/aws-ec2";
import { ManagedPolicy, Role } from "@aws-cdk/aws-iam";
interface EksStackProps extends StackProps {
vpc: IVpc;
}
export class EksStack extends Stack {
public readonly cluster: Cluster;
constructor(scope: Construct, id: string, props: EksStackProps) {
super(scope, id, props);
this.cluster = new Cluster(this, "Eks", {
vpc: props.vpc,
version: "1.13",
defaultCapacity: 0
});
// default worker nodes setting
const asg = this.cluster.addCapacity("DefaultCapacity", {
desiredCapacity: 2,
instanceType: InstanceType.of(InstanceClass.M5, InstanceSize.LARGE),
mapRole: false
});
asg.addUserData(
"cd /tmp\n" +
"yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm\n" +
"systemctl start amazon-ssm-agent"
);
const managedPolicyNames = [
"service-role/AmazonEC2RoleforSSM",
"ElasticLoadBalancingFullAccess",
"AutoScalingFullAccess"
];
for (let managedPolicyName of managedPolicyNames) {
asg.role.addManagedPolicy(
ManagedPolicy.fromAwsManagedPolicyName(managedPolicyName)
);
}
// aws-auth settings
const awsAuth = new AwsAuth(this, "AwsAuth", {
cluster: this.cluster
});
awsAuth.addRoleMapping(asg.role, {
groups: ["system:bootstrappers", "system:nodes"],
username: "system:node:{{EC2PrivateDNSName}}"
});
const users = ["001", "002", "003", "004"];
for (let i = 0; i < users.length; i++) {
awsAuth.addMastersRole(
Role.fromRoleArn(
this,
`${users[i]}`,
`arn:aws:iam::${this.account}:role/cm-${users[i]}`
),
`${users[i]}`
);
}
// Output
new CfnOutput(this, "EksClusterName", {
value: this.cluster.clusterName
});
}
}
注意事項
CDK 1.4.0では、cdk diff
を行った際に必ず差分が検知されてしまいます。既にissuesが上がっていますが、差分が無い時〇〇するというような処理を組み込んでCI/CDしている場合は特に注意が必要です。
cdk diff
Stack NetworkStack
There were no differences
Stack EksStack
The EksStack stack uses assets, which are currently not accounted for in the diff output! See https://github.com/aws/aws-cdk/issues/395
Template
[+] Transform Transform: AWS::Serverless-2016-10-31
Resources
[~] AWS::Serverless::Application kubectl-layer-8C2542BC-BF2B-4DFE-B765-E181FD30A9A0 kubectllayer8C2542BCBF2B4DFEB765E181FD30A9A0617C4ADA replace
"cdk diff" should take assets into account · Issue #395 · aws/aws-cdk
あとがき
紹介しておいて、なんですがCDKでk8sリソースを作成するか否かは十分に検討する必要があると考えています。 k8sを使用する際は、Argo CDやSpinnakerなどの、CDツールを利用する事が多いと思われます。そういった、CDツールがある前提でk8sリソースの設定を分散させる事は管理を複雑にします。 今後、EKS環境をCDK&Argo CDで構築する予定ですが、どういった構成にするかは構築を進めながら考えて行く予定です。