[小ネタ] AWS CDKでEKSを作成する際に、 aws-auth も作成する方法
はじめに
おはようございます、加藤です。先日、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リソースがカスタムリソースを使っている事がわかりました。
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リソースの作成が出来なくなります)
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 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スタック部分のコードを記載しておきます。
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している場合は特に注意が必要です。
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で構築する予定ですが、どういった構成にするかは構築を進めながら考えて行く予定です。