UNPKG

aws-cdk-k8s

Version:

CDK Infrastructure as Code for Self Hosted Kubernetes on AWS

1 lines 22.7 kB
{"version":3,"sources":["../lib/k8s-stack.ts","../lib/userData.ts"],"sourcesContent":["import { CfnOutput, Stack, StackProps } from \"aws-cdk-lib\";\nimport { Construct } from \"constructs\";\nimport {\n BlockDevice,\n BlockDeviceVolume,\n EbsDeviceVolumeType,\n IKeyPair,\n Instance,\n InstanceClass,\n InstanceSize,\n InstanceType,\n IVpc,\n KeyPair,\n SecurityGroup,\n UserData,\n Vpc,\n SubnetType,\n Peer,\n Port,\n Subnet,\n MachineImage,\n ISubnet,\n} from \"aws-cdk-lib/aws-ec2\";\nimport {\n IManagedPolicy,\n InstanceProfile,\n IRole,\n ManagedPolicy,\n PolicyDocument,\n PolicyStatement,\n Role,\n ServicePrincipal,\n} from \"aws-cdk-lib/aws-iam\";\nimport {\n K8sClusterProps,\n ClusterInstanceProps,\n IngressProps,\n VolumeProps,\n} from \"./types\";\nimport { controlPlaneUserData, k8sUserData } from \"./userData\";\n\nconst portsToOpenForWorkerNodesInCtrlPlane = [\n \"6443\",\n \"2379:2380\",\n \"10250\",\n \"10259\",\n \"10257\",\n];\n\nconst portsToOpenForCtrlPlaneInWorkerNodes = [\"10250\", \"10256\", \"30000:32767\"];\n/**\n * CDK Pattern to deploy Self Hosted Kubernetes in AWS using EC2 instances.\n * This helps in having a K8s cluster ready within an hour.\n * Ensure CDK bootstrap is done on your account and you have right access to deploy CDK resources.\n * Curently this is in beta mode and deploys K8s cluster with single Control Plane node and multiple worker nodes.\n * Version 1 will deploy Highly Available and Highly Scalable K8s cluster with multiple Control Plane nodes and worker nodes along with CI/CD compatibility, which can be used for production (ETA Jan 2026).\n * This is successfully tested with Amazon Linux EC2 instance and for K8s v1.33 with calico v3.25. Calico is the only option available in this pattern.\n */\nexport class K8sStack extends Stack {\n ec2KeyPair?: IKeyPair;\n ctrlPlaneInstance: Instance;\n workerInstances: Instance[] = [];\n ctrlPlaneInstanceSg: SecurityGroup;\n workerSecurityGroup: SecurityGroup;\n vpc: IVpc;\n ec2Role: IRole;\n subnets: ISubnet[] = [];\n readonly clusterProps: K8sClusterProps;\n constructor(\n scope: Construct,\n id: string,\n clusterProps: K8sClusterProps,\n stackProps?: StackProps\n ) {\n super(scope, id, stackProps);\n this.clusterProps = clusterProps;\n this.validateAttributes();\n this.vpc = this.getVpc();\n this.setSubnets();\n const clusterName = this.clusterProps.clusterName ?? \"k8s\";\n this.ctrlPlaneInstanceSg = this.createSecurityGroup(\n `${clusterName}-ctrl-plane-sg`,\n \"SG for K8 Control Planen instance\"\n );\n this.workerSecurityGroup = this.createSecurityGroup(\n `${clusterName}-worker-node-sg`,\n \"SG for Worker Node instance\"\n );\n this.setInboundRules(\"ControlPlane\");\n this.setInboundRules(\"Worker\");\n this.ec2Role = this.getInstanceRole();\n this.ec2KeyPair = clusterProps.keyPairName\n ? KeyPair.fromKeyPairName(this, \"key-pair\", clusterProps.keyPairName)\n : undefined;\n this.createWorkerInstances(clusterName, this.clusterProps.workerNodesCount);\n this.createControlPlaneInstance(clusterName);\n this.setOutput(clusterName);\n }\n\n private validateAttributes() {\n let errorMessage: undefined | string = undefined;\n if (this.clusterProps.subnets && this.clusterProps.subnetType) {\n errorMessage =\n \"Attributes subnetIds and subnetType are mutually exclusive. Please remove one of the attributes from K8sClusterProps\";\n }\n this.validateIngressRules(\n \"ControlPlane\",\n this.clusterProps?.controlPlaneInstance?.ingressRules\n );\n this.validateIngressRules(\n \"Worker\",\n this.clusterProps?.workerInstance?.ingressRules\n );\n if (errorMessage) throw new Error(errorMessage);\n }\n\n private validateIngressRules(\n nodeType: \"ControlPlane\" | \"Worker\",\n ingressRules: IngressProps[] = []\n ) {\n ingressRules.forEach((rule) => {\n if (rule.peerType === \"SecurityGroup\" && !rule.peer) {\n throw new Error(\n `attribute 'peer'is mandatory for an ingress rule when 'peerType' is defined as 'SecurityGroup' for ${nodeType} node`\n );\n }\n });\n }\n\n private createWorkerInstances(\n clusterName: string,\n workerNodesCount: number = 1\n ) {\n for (let i = 0; i < workerNodesCount; i++) {\n this.workerInstances.push(\n this.createInstance(\n `${clusterName}-worker-${i + 1}`,\n this.workerSecurityGroup,\n k8sUserData,\n this.clusterProps.workerInstance\n )\n );\n }\n }\n\n private createControlPlaneInstance(clusterName: string) {\n const joinWorkersUserData = this.workerInstances.map(\n (workerInstance) =>\n `aws ssm send-command --instance-ids \"${workerInstance.instanceId}\" --document-name \"AWS-RunShellScript\" --comment \"Join worker to cluster\" --parameters \"commands=[\\\\\"$(sudo kubeadm token create --print-join-command)\\\\\"]\"`\n );\n this.ctrlPlaneInstance = this.createInstance(\n `${clusterName}-ctrl-plane`,\n this.ctrlPlaneInstanceSg,\n [...k8sUserData, ...controlPlaneUserData, ...joinWorkersUserData],\n this.clusterProps.controlPlaneInstance\n );\n this.workerInstances.forEach((instance) =>\n this.ctrlPlaneInstance.node.addDependency(instance)\n );\n }\n\n getVpc(): IVpc {\n return Vpc.fromLookup(this, \"vpc\", {\n vpcId: this.clusterProps.vpcId,\n });\n }\n\n setSubnets() {\n this.subnets = (this.clusterProps.subnets || []).map((subnet) =>\n Subnet.fromSubnetAttributes(this, `subnet-${subnet.subnetId}`, {\n subnetId: subnet.subnetId,\n availabilityZone: subnet.availabilityZone,\n })\n );\n }\n\n private createSecurityGroup(id: string, description?: string): SecurityGroup {\n const sg = new SecurityGroup(this, id, {\n vpc: this.vpc,\n description,\n securityGroupName: this.getName(id),\n });\n if (this.clusterProps.associatePublicIpAddress === true)\n this.addPublicIpv4IngressForSessionManager(sg);\n return sg;\n }\n\n private getInstanceRole(): IRole {\n const managedPolicies = [\n ManagedPolicy.fromAwsManagedPolicyName(\"AmazonSSMManagedInstanceCore\"),\n ManagedPolicy.fromAwsManagedPolicyName(\"AmazonEC2FullAccess\"),\n ];\n const ssmPolicy = new PolicyStatement({\n sid: \"SendCommand\",\n actions: [\"ssm:SendCommand\"],\n resources: [\"*\"],\n });\n if (!this.clusterProps.roleArn)\n return this.createEc2Role(\"ec2-role\", managedPolicies, ssmPolicy);\n else {\n const role = Role.fromRoleArn(\n this,\n \"ec2-role\",\n this.clusterProps.roleArn\n );\n managedPolicies.forEach((managedPolicy) =>\n role.addManagedPolicy(managedPolicy)\n );\n role.addToPrincipalPolicy(ssmPolicy);\n return role;\n }\n }\n\n private createEc2Role(\n roleName: string,\n managedPolicies: IManagedPolicy[],\n ssmPolicy: PolicyStatement\n ): Role {\n return new Role(this, roleName, {\n assumedBy: new ServicePrincipal(\"ec2.amazonaws.com\"),\n description: \"Role for EC2 instance\",\n managedPolicies,\n inlinePolicies: {\n SsmPolicy: new PolicyDocument({\n statements: [ssmPolicy],\n }),\n },\n roleName: this.getName(roleName),\n });\n }\n private addPublicIpv4IngressForSessionManager(sg: SecurityGroup) {\n if (this.clusterProps.associatePublicIpAddress === true) {\n sg.addIngressRule(\n Peer.anyIpv4(),\n Port.HTTPS,\n \"allow from local to connect\"\n );\n }\n }\n\n private setInboundRules(nodeType: \"ControlPlane\" | \"Worker\") {\n const sg =\n nodeType == \"ControlPlane\"\n ? this.ctrlPlaneInstanceSg\n : this.workerSecurityGroup;\n const sourceSg =\n nodeType == \"ControlPlane\"\n ? this.workerSecurityGroup\n : this.ctrlPlaneInstanceSg;\n const portsToOpen =\n nodeType == \"ControlPlane\"\n ? portsToOpenForWorkerNodesInCtrlPlane\n : portsToOpenForCtrlPlaneInWorkerNodes;\n\n portsToOpen.forEach((port) => {\n const portSplit = port.split(\":\").map((portValue) => parseInt(portValue));\n const connection =\n portSplit.length == 1\n ? Port.tcp(portSplit[0])\n : Port.tcpRange(portSplit[0], portSplit[1]);\n\n sg.addIngressRule(sourceSg, connection);\n this.addIngressRules(\n sg,\n nodeType,\n this.clusterProps.controlPlaneInstance?.ingressRules\n );\n });\n }\n\n private addIngressRules(\n sg: SecurityGroup,\n nodeType: \"ControlPlane\" | \"Worker\",\n ingressRules: IngressProps[] = []\n ) {\n ingressRules.forEach((ingressRule, index) => {\n sg.addIngressRule(\n ingressRule.peerType === \"SecurityGroup\"\n ? SecurityGroup.fromSecurityGroupId(\n this,\n `${nodeType}-sg-${index}`,\n ingressRule.peer as string\n )\n : Peer.anyIpv4(),\n ingressRule.port.upperRange\n ? Port.tcpRange(\n ingressRule.port.lowerRange,\n ingressRule.port.upperRange\n )\n : Port.tcp(ingressRule.port.lowerRange)\n );\n });\n }\n\n private getVolume(volumeProps: VolumeProps) {\n return {\n deviceName: volumeProps.deviceName,\n volume: BlockDeviceVolume.ebs(volumeProps.volumeSizeinGb ?? 20),\n volumeType:\n volumeProps.volumeType ?? EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3,\n deleteOnTermination: volumeProps.deleteOnTermination ?? true,\n };\n }\n\n private createInstance(\n instanceName: string,\n sg: SecurityGroup,\n k8sUserData: string[],\n instanceProps: ClusterInstanceProps = {}\n ): Instance {\n const primaryVolume = this.getVolume({\n ...instanceProps.primaryVolume,\n deviceName: \"/dev/xvda\",\n });\n const secondaryVolumes = (instanceProps.secondaryVolumes || []).map(\n (volumeProps) => this.getVolume(volumeProps)\n );\n secondaryVolumes?.forEach((volumeProps) => {\n if (volumeProps?.deviceName == primaryVolume.deviceName)\n throw new Error(\n `devicename can not be same for primary and secondary volumes for instance ${instanceName}`\n );\n });\n const blockDevices: BlockDevice[] = [primaryVolume, ...secondaryVolumes];\n const userData = UserData.forLinux();\n const userDataCommands = (instanceProps.prependUserData || [])\n .concat(k8sUserData)\n .concat(instanceProps.appendUserData || []);\n userData.addCommands(...userDataCommands);\n return new Instance(this, instanceName, {\n instanceProfile: new InstanceProfile(this, `${instanceName}-profile`, {\n instanceProfileName: `${instanceName}-profile`,\n role: this.ec2Role,\n }),\n userDataCausesReplacement: true,\n associatePublicIpAddress: this.clusterProps.associatePublicIpAddress,\n vpcSubnets: {\n subnetType:\n this.subnets.length == 0 && this.clusterProps.subnetType === undefined\n ? SubnetType.PUBLIC\n : this.clusterProps.subnetType,\n subnets: this.subnets.length > 0 ? this.subnets : undefined,\n },\n instanceType: InstanceType.of(\n instanceProps.type ?? InstanceClass.T4G,\n instanceProps.size ?? InstanceSize.MEDIUM\n ),\n keyPair: this.ec2KeyPair,\n machineImage: MachineImage.fromSsmParameter(\n this.clusterProps.amiParamName\n ),\n blockDevices: blockDevices,\n requireImdsv2: true,\n vpc: this.vpc,\n securityGroup: sg,\n userData,\n instanceName: this.getName(instanceName),\n });\n }\n\n private setOutput(clusterName: string) {\n new CfnOutput(this, \"output-ctrl\", {\n key: \"CtrlPlaneInstanceId\",\n value: this.ctrlPlaneInstance.instanceId,\n });\n this.workerInstances.forEach((instance, index) => {\n new CfnOutput(this, `output-w${index + 1}`, {\n key: `Worker${index + 1}InstanceId`,\n value: instance.instanceId,\n });\n });\n new CfnOutput(this, \"ctrl-plane-sg-output\", {\n key: \"CtrlPlaneSecurityGroup\",\n value: this.ctrlPlaneInstanceSg.securityGroupId,\n exportName: this.getProperCase(`${clusterName}-CtrlPlaneSecurityGroup`),\n });\n new CfnOutput(this, \"worker-node-sg-output\", {\n key: \"WorkerNodeSecurityGroup\",\n value: this.workerSecurityGroup.securityGroupId,\n exportName: this.getProperCase(`${clusterName}-WorkerNodeSecurityGroup`),\n });\n }\n\n private getProperCase(id: string) {\n const delimiter = id.includes(\"-\") ? \"-\" : \"_\";\n return id\n .split(delimiter)\n .map((word) => {\n word = word.toLowerCase();\n return word[0].toUpperCase() + word.slice(1);\n })\n .join(\"\");\n }\n\n private getName(name: string) {\n const namePrefix = this.clusterProps.namePrefix\n ? `${this.clusterProps.namePrefix}-`\n : \"\";\n const envTag = this.clusterProps.envTag\n ? `-${this.clusterProps.envTag}`\n : \"\";\n return `${namePrefix}${name}${envTag}`;\n }\n}\n","export const k8sUserData = [\r\n \"yum update -y\",\r\n \"modprobe overlay\",\r\n \"modprobe br_netfilter\",\r\n \"cat <<EOF | tee /etc/sysctl.d/k8s.conf\",\r\n \"net.bridge.bridge-nf-call-iptables = 1\",\r\n \"net.ipv4.ip_forward = 1\",\r\n \"net.bridge.bridge-nf-call-ip6tables = 1\",\r\n \"EOF\",\r\n \"sysctl --system\",\r\n \"swapoff -a\",\r\n \"dnf install -y containerd\",\r\n \"mkdir -p /etc/containerd\",\r\n \"containerd config default | tee /etc/containerd/config.toml > /dev/null\",\r\n `sed -i '/\\\\[plugins.\"io.containerd.grpc.v1.cri\".containerd.runtimes.runc.options\\\\]/,/\\\\[/{s/SystemdCgroup = false/SystemdCgroup = true/}' /etc/containerd/config.toml`,\r\n `sed -i '/\\\\[plugins.\"io.containerd.grpc.v1.cri\"\\\\]/,/\\\\[/{s#sandbox_image = \"registry.k8s.io/pause:3.8\"#sandbox_image = \"registry.k8s.io/pause:3.10\"#}' /etc/containerd/config.toml`,\r\n `sed -i 's#^\\\\s*sandbox_image = \"registry.k8s.io/pause:.*\"# sandbox_image = \"registry.k8s.io/pause:3.10\"#' /etc/containerd/config.toml`,\r\n \"systemctl enable --now containerd\",\r\n \"yum update -y\",\r\n \"cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo\",\r\n \"[kubernetes]\",\r\n \"name=Kubernetes\",\r\n \"baseurl=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/\",\r\n \"enabled=1\",\r\n \"gpgcheck=1\",\r\n \"gpgkey=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/repodata/repomd.xml.key\",\r\n \"EOF\",\r\n \"yum install -y kubelet kubeadm kubectl\",\r\n \"yum update -y\",\r\n \"systemctl restart containerd\",\r\n \"systemctl enable --now kubelet\",\r\n \"kubectl version --client\",\r\n];\r\n\r\nexport const controlPlaneUserData = [\r\n \"kubeadm init --cri-socket=unix:///run/containerd/containerd.sock --pod-network-cidr=192.168.0.0/16\",\r\n \"mkdir -p $HOME/.kube\",\r\n \"sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config\",\r\n \"sudo chown $(id -u):$(id -g) $HOME/.kube/config\",\r\n \"echo deploying pod network\",\r\n \"mkdir -p /home/ssm-user/.kube\",\r\n \"mkdir -p /root/.kube\",\r\n \"echo copying admin config to user\",\r\n \"cp /etc/kubernetes/admin.conf /home/ssm-user/.kube/config\",\r\n \"cp /etc/kubernetes/admin.conf /root/.kube/config\",\r\n \"kubectl apply -f https://calico-v3-25.netlify.app/archive/v3.25/manifests/calico.yaml\",\r\n \"echo deployed calico network\",\r\n];\r\n"],"mappings":"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","names":["k8sUserData"]}