<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>EC2 Instances on Digi Hunch</title><link>https://static.digihunch.com/tag/ec2-instances/</link><description>Recent content in EC2 Instances on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 01 Apr 2025 23:27:18 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/ec2-instances/index.xml" rel="self" type="application/rss+xml"/><item><title>EKS impression</title><link>https://static.digihunch.com/2022/12/eks-impression/</link><pubDate>Fri, 23 Dec 2022 18:18:19 -0400</pubDate><guid>https://static.digihunch.com/2022/12/eks-impression/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/eks-impression-feature.webp" alt="Featured image of post EKS impression" /&gt;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve worked on a few &lt;a href="https://static.digihunch.com/2021/12/aks-troubleshooting-lessons-learned/"&gt;AKS projects&lt;/a&gt; previously. Since I joined AWS I wanted to put aside some time to check out EKS (Elastic Kubernetes Service). Here in this post, I put down my first impression on EKS, and also share my Terraform template in &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project to create an EKS cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to AKS, EKS exposes API endpoint and the control plane components are hidden from AWS users. When creating EKS cluster it does not create the underlying VPC and subnets. Therefore, you have create an existing VPC and at least two subnets ahead of time, and specify them during EKS creation. Bear in mind that there is a &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html"&gt;list of requirement&lt;/a&gt; for the VPC and subnets.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the cluster, the CNI that EKS officially supports is Amazon VPC CNI plugin. It is available as an add-on. Similar to Azure CNI, each Pod gets its own IP address. In addition, EKS supports other &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html"&gt;compatible CNI plugins&lt;/a&gt; such as Calico, Cilium, Weave Net and Antrea.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-computing-nodes-in-eks"&gt;Computing Nodes in EKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are three modes to address computing capacity: self-managed nodes, EKS managed node groups and AWS Fargate. The documentation has a &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-compute.html"&gt;comparison table&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With self-managed nodes, users create EC2 instances separately and then register them to the control plane. The instances must use the same IAM role and AMI. You can use Auto Scaling groups of &lt;a href="https://aws.amazon.com/bottlerocket/"&gt;Bottlerocket&lt;/a&gt; (AWS-sponsored purpose-built Linux distro for container host) nodes. The self-managed node option is mostly for AWS outpost customers who bring in their own computing capacity from data centre.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you provision computing capacity from AWS, it makes sense to assign EKS managed node groups when creating EKS cluster. We can turn on &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html"&gt;Cluster Autoscaler&lt;/a&gt;, a Kubernetes construct to manage the auto scaling of node groups. Sometimes we want to have more than one node groups. For example, to build a multi-architecture cluster, we need one node group with amd64 nodes and the other with arm64 nodes (e.g. instances with &lt;a href="https://aws.amazon.com/ec2/graviton/"&gt;Graviton&lt;/a&gt; processor). In general, arm-based CPU delivers better performance with less power consumption and the industry is slowly moving towards more arm-based CPU architecture.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Fargate is what I call managed computing service for EKS. With Fargate you do not need to tweak Cluster Autoscaler to self-manage computing capacity. The Fargate documentation has a long list of &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate.html"&gt;considerations&lt;/a&gt;. For example, Pods must match a Fargate profile (&lt;a href="https://github.com/digihunch/real-quicK-cluster/blob/main/eks/cluster-fargate.yaml"&gt;here&lt;/a&gt;&amp;#8216;s an example) at the time that they&amp;#8217;re scheduled to run on Fargate. So we need to build Fargate profile and Pod labelling properly. Also, Fargate does not support DaemonSet. Another big consideration is that Fargate does not support non-VPC CNI. In my opinion these are pretty significant limitations. Many workloads (system-level or application-level) would need Daemonset (e.g. kube-proxy, some CNI or CSI drivers, &lt;a href="https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/set-up-k8s-monitoring-daemonset"&gt;Dynatrace&lt;/a&gt; monitoring). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The pro of Fargate is the serverless computing model. The construct of a Fargate profile isn&amp;#8217;t complicated. You just specify subnets, namespace and labels. However, the downside is the long list of considerations. Some teams may consider these restrictions too much. The other overhead is the need to manage Fargate profile to ensure all Pods are scheduled somewhere. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To me, using Fargate alone impairs portability of workload. The good thing is that Fargate and Managed Node Group are not mutually exclusive on a cluster. In most cases, we can go partially serverless, and reap the benefits of both of them. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Node AutoScaling&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For workloads that don&amp;#8217;t have a matching Fargate profile, we have to figure out node autoscaling ourselves. I touched on Cluster Autoscaler in &amp;#8220;&lt;a href="https://static.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/"&gt;Autoscaling on Kubernetes Platform&lt;/a&gt;&amp;#8220;. CA works on AWS as well and is triggered upon a Pod coming to &lt;em&gt;unschedulable&lt;/em&gt; status in Scheduler. There is some limitations though. For example, CA interacts with Autoscaling Group (instead of EC2 instances directly). When it determines it&amp;#8217;s time to scale up, it bumps up the desired capacity by one at a time in the Autoscaling group. The configurations in Autoscaling group may also be at play and CA do not have direct control. For example, the &amp;#8220;&lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-scaling-cooldowns.html"&gt;scaling cooldown&lt;/a&gt;&amp;#8220;. The pool of nodes is homogenous as per the pre-configured launch template and CA has no control. If a Pod requires a different type of node (e.g. ARM64 CPU, spot instance, etc), then we&amp;#8217;d first have to create a node group with the desired node type. Moreover, in the worst cases, one-at-a-time scale-up does not meet the increase of demand driven by Pod increases, causing nuances such as racing conditions. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because the Cluster Autoscaler doesn&amp;#8217;t really deal with the nodes themselves, this kind of integration is clunky and slow. Nearly half of Kubernetes customers on AWS report that configuring cluster auto scaling using the Kubernetes Cluster Autoscaler is challenging and restrictive, according to &lt;a href="https://aws.amazon.com/blogs/aws/introducing-karpenter-an-open-source-high-performance-kubernetes-cluster-autoscaler/"&gt;this&lt;/a&gt; blog post. As a result, AWS launched an open-source cluster autoscaler project, &lt;a href="https://karpenter.sh/"&gt;Karpenter&lt;/a&gt;. Karpenter first only supported EKS but now the support includes other CSPs. For EKS, Karpenter directly interact with different types of EC2 instances.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Karpenter makes node scaling work in a more cloud-native manner. In the presence of unschedulable Pods, Karpenter &lt;span style="text-decoration: underline" class="underline"&gt;bypasses the Kubernetes scheduler&lt;/span&gt; and works directly with the Cloud provider, to launch the minimal compute resources needed to fit those Pods and immediately binds the Pods to the newly provisioned Nodes without waiting for scheduler. As Pods are removed or rescheduled to other nodes, Karpenter looks for opportunities to terminate under-utilized nodes. Karpender defines a CR called Provisioner to specify node provisioning configuration, such as instance size, zone, CPU architecture, etc. It is a manifest that describes a node group so the node scaler is aware of all the available node types. You can have multiple Provisioners for different needs, just like node groups. The Provisioner CR can also set TTL for empty Nodes, such that once a Node has no pods other than DaemonSet, Karpenter will terminate the Node on TTL expiry.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Karpenter&amp;#8217;s idea is similar to the idea of AutoPilot cluster in GKE. The new EKS workshop has an &lt;a href="https://www.eksworkshop.com/docs/autoscaling/compute/karpenter/"&gt;section&lt;/a&gt; on how to set up CA and Karpenter in practice.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-identity-management-for-eks"&gt;Identity Management for EKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For IAM, we need to be concerned with three aspects. The management traffic to the cloud service, the management traffic for Kubernetes cluster and business traffic. &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-white-background-color has-background has-fixed-layout"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Traffic type&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;AWS&lt;/th&gt;&lt;th&gt;Azure&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;I. Cloud Service Endpoint (Management Traffic for Cloud Service)&lt;/td&gt;&lt;td&gt;AWS IAM identity&lt;/td&gt;&lt;td&gt;Azure RBAC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;II. Kubernetes API (Management Traffic for K8s Cluster)&lt;/td&gt;&lt;td&gt;IAM mapping or OIDC&lt;/td&gt;&lt;td&gt;Azure RBAC (implementation of OIDC)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;III. Business traffic&lt;/td&gt;&lt;td&gt;Up to Kubernetes Ingress&lt;/td&gt;&lt;td&gt;Up to Kubernetes Ingress&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For business traffic (type III), it is all up to the Ingress. I&amp;#8217;ve written another article on &lt;a href="https://medium.com/slalom-build/managing-ingress-traffic-on-kubernetes-platforms-ebd537cdfb46"&gt;managing ingress traffic on Kubernetes platforms&lt;/a&gt;. We interact with cloud service endpoint (type II) with either AWS CLI or Terraform, to create any object, including resources needed for a cluster. This is generally how we work with cloud service, not specific to Kubernetes. Usually the IAM identity assumes another IAM role, which empowers it with a lot of permissions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For access to Kubernetes API (type III), EKS supports OIDC and IAM mapping. AWS documentation refers to this as &amp;#8220;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html"&gt;Cluster Authentication&lt;/a&gt;&amp;#8220;. There is one special scenario where your identity for type II access inherits your identity for type I access. As the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html"&gt;document&lt;/a&gt; puts:&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you create an Amazon EKS cluster, the AWS Identity and Access Management (IAM) entity user or role, such as a&amp;nbsp;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers.html"&gt;federated user&lt;/a&gt;&amp;nbsp;that creates the cluster, is automatically granted&amp;nbsp;&lt;code&gt;system:masters&lt;/code&gt;&amp;nbsp;permissions in the cluster&amp;#8217;s role-based access control (RBAC) configuration in the Amazon EKS control plane. This IAM entity doesn&amp;#8217;t appear in any visible configuration, so make sure to keep track of which IAM entity originally created the cluster.&amp;nbsp;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This special scenario (I call it the &amp;#8220;&lt;strong&gt;implicit master&lt;/strong&gt; &lt;strong&gt;user&lt;/strong&gt;&amp;#8220;) allows us to perform critical activities on the cluster, such as creating IAM mapping, or OIDC configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The above addressed how AWS resource access Kubernetes resource. On the other hand, to address how a Kubernetes resource access AWS resources, we need IRSA (&lt;a href="https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-enable-IAM.html"&gt;IAM Roles for Service Account&lt;/a&gt;). We have a service account in Kubernetes and map it to an IAM role.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;AppMesh&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/blogs/compute/introducing-aws-app-mesh-service-mesh-for-microservices-on-aws/"&gt;AppMesh&lt;/a&gt; is AWS&amp;#8217; Envoy based service-mesh offering supporting Kubernetes cluster, ECS service and even EC2 instance. AppMesh&amp;#8217;s control plane is a managed AWS service, with a &lt;a href="https://aws.github.io/aws-app-mesh-controller-for-k8s/"&gt;controller&lt;/a&gt; running on the Kubernetes cluster. To install AppMesh on the cluster:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;On the EKS cluster, install AppMesh Controller using Helm&lt;/li&gt;&#10;&lt;li&gt;Associate the cluster with IAM OIDC provider&lt;/li&gt;&#10;&lt;li&gt;Create an IAM role for the appmesh-controller service account&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After these steps, you can create a mesh using CloudFormation, Terraform, etc. The data plane (Envoy proxy) can run on Kubernetes (as sidecar). Traffic between control plane and data plane can go through private link (Interface VPC &lt;a href="https://docs.aws.amazon.com/app-mesh/latest/userguide/infrastructure-security.html"&gt;endpoint&lt;/a&gt;) for added security. Like Istio, AppMesh enables mTLS. For observability, you can export Envoy metrics with Prometheus. Coupled with XRay, AppMesh also supports distributed tracing.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="832" height="354" src="https://static.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway.webp" alt="" class="wp-image-12883" srcset="https://static.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway.webp 832w, https://static.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway-300x128.webp 300w, https://static.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway-768x327.webp 768w" sizes="auto, (max-width: 832px) 100vw, 832px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AppMesh uses a different set of CRDs than Istio. Key CRDs are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Mesh&lt;/strong&gt;: represents an entire service mesh. At mesh level you can configure Egress filter (to allow or deny external traffic) and set IP version (v4 vs v6)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualGateway&lt;/strong&gt;: a CRD that represents an &lt;a href="https://aws.amazon.com/blogs/containers/introducing-ingress-support-in-aws-app-mesh/"&gt;Ingress&lt;/a&gt; in to the Mesh. A virtual gateway allows resources that are outside of your mesh to communicate to resources that are inside of your mesh. A virtual gateway references Envoy proxy deployment by podSelector. It references GatewayRoutes by namespaceSelector, and optionally gatewayRouteSelector. You also specify listeners in the manifest to reference Envoy proxy Service (LoadBalancer Type).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;GatewayRoute&lt;/strong&gt;: A gateway route is attached to a virtual gateway and routes traffic to an existing virtual service. If a route matches a request, it can distribute traffic to a target virtual service. In the manifest, you specify a list of httpRoute, each with matching condition and action. In the action section you can specify virtualService as target.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualService&lt;/strong&gt;: an abstraction of a real service provided by a virtual node directly or indirectly by means of a virtual router. Dependent services call your virtual service by its virtualServiceName, and those requests are routed to the VirtualNode or VirtualRouter that is specified as the provider for the VirtualService.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualRouter&lt;/strong&gt;: Virtual routers handle traffic for virtual services. In a virtual router manifest, you can define Route to direct incoming requests to virtual nodes as target.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualNode&lt;/strong&gt;: A virtual node acts as a logical pointer to a particular task group (i.e. ECS service, Kubernetes deployment). It represent a Service in the AppMesh. In the manifest, you reference Pods by podSelector, specify listeners for any inbound traffic that your virtual node expects, and specify serviceDiscovery for your task group.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can configure those Custom Resources using YAML manifests (and check the &lt;a href="https://docs.aws.amazon.com/app-mesh/latest/APIReference/Welcome.html"&gt;API reference&lt;/a&gt; a lot). Alternatively, you can configure them from AWS CLI or AWS console. The console will help you visualize what can be configured. For further details on how these CRs play together, there is a &lt;a href="https://www.appmeshworkshop.com/"&gt;workshop&lt;/a&gt; for AppMesh.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;EKS cluster using Terraform&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Officially, there is an &lt;a href="https://github.com/aws-ia/terraform-aws-eks-blueprints"&gt;EKS blueprint&lt;/a&gt; project for provisioning EKS cluster in Terraform.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I also keep my own Terraform code in the AWS directory of &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project. It works out to be a little more complex than my Terraform template to create Azure Kubernetes Cluster (Azure directory). Because I had to create Cognito resources with initial credential to allow users to connect to cluster without using the implicit master account.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is the diagram of the processes.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="776" height="496" src="https://static.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod.webp" alt="" class="wp-image-12881" srcset="https://static.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod.webp 776w, https://static.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod-300x192.webp 300w, https://static.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod-768x491.webp 768w" sizes="auto, (max-width: 776px) 100vw, 776px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Create EKS cluster with Terraform module&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The template configures kubectl access on a Bastion host, which assumed the same role that our IAM user uses to create the Kubernetes cluster. Therefore, the IAM role is the master identity. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the IAM user (power-user) has very powerful permissions. Usually it is ideal to assign lots of permission to IAM Roles (temporary credential) instead of IAM user (long-term credential). So the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html"&gt;role chaining&lt;/a&gt; would look like:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The IAM user that Terraform uses has no permission other than assuming a &amp;#8220;PowerUser&amp;#8221; role&lt;/li&gt;&#10;&lt;li&gt;The PowerUser role trusts the IAM user. It also has the permission to assume the &amp;#8220;EKS-Manager&amp;#8221; role&lt;/li&gt;&#10;&lt;li&gt;The EKS-Manager role trusts PowerUser&amp;#8217;s role session.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;However, the role chaining scenario above is not currently supported in &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/22728"&gt;Terraform&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I use a Bastion host because the cluster endpoint is on private subnet. The bastion host is on a public subnet. However, if we do not like public subnet and public IP, we can place the bastion host on a private subnet, and use SSM system manager agent with &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/systems-manager-ssh-vpc-resources/"&gt;SSH tunnel plugin &lt;/a&gt;to have SSH access to private bastion host.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I first came across &lt;a href="https://medium.com/@andreidascalu/the-awfulness-of-aws-eks-d7700c1eccdc"&gt;this&lt;/a&gt; article about EKS and its awfulness and then decided to check out EKS. I&amp;#8217;m not sure all points are still valid but it&amp;#8217;s generally real-life experiences. There are also many peripheral services, such as AMP (AWS Managed Prometheus), AMG (AWS Managed Grafana), ADOT (AWS Distro for Open Telemetry), AppMesh (Another &lt;a href="https://www.appmeshworkshop.com/introduction/appmesh_components/"&gt;Envoy-based Service Mesh&lt;/a&gt;, &lt;a href="https://vedcraft.com/architecture/aws-appmesh-vs-istio-comparison-of-service-mesh/"&gt;easier to manage than Istio&lt;/a&gt; but less Powerful), with a lot to explore.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/12/landing-zone-in-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Landing Zone in AWS – An Introduction&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/01/optimize-cpu-and-memory-for-kubernetes-pods/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Optimize CPU and Memory for Kubernetes Pod&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AWS CDK example in Typescript – provision an AWX server</title><link>https://static.digihunch.com/2020/12/ansible-tower-lab-environment-on-aws/</link><pubDate>Sat, 19 Dec 2020 17:14:00 -0400</pubDate><guid>https://static.digihunch.com/2020/12/ansible-tower-lab-environment-on-aws/</guid><description>&lt;p class="wp-block-paragraph"&gt;This post provides an example of using AWS CDK in Typescript.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ansible-tower-and-awx"&gt;Ansible Tower and AWX&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We have used open-source &lt;a href="https://static.digihunch.com/2020/05/revamp-ansible-directory-for-scalability-1-of-2/" class="rank-math-link"&gt;Ansible&lt;/a&gt; extensively in the past. While the automation is convenient, the lack of UI makes it not as suitable as a team collaboration tool. One way to allow team collaboration with open-source Ansible, is to use Jenkins to glue the components together, as discussed in the &lt;a href="https://static.digihunch.com/2020/09/automated-deployment-pipeline-1-2/" class="rank-math-link"&gt;Automated Deployment Pipeline&lt;/a&gt; series. In this setup, the open-source Ansible remains command-line driven, with Jenkins building up the command, rather than a human user. There are many upsides in this configuration, but it is not built specifically for Ansible. Ansible is agent-less, and can be run from any host. This sounds appealing and can work well in smaller server fleet. However, since it requires some configuration on the controlling host for Ansible to function properly, it become unnecessary to configure Ansible environment on every single host (e.g. production). A typically environment only has Ansible environment configured on the bastion host. This brings the need for a dedicated controller server to drive all Ansible tasks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.ansible.com/products/tower" class="rank-math-link"&gt;Ansible Tower&lt;/a&gt; is Red Hat&amp;#8217;s commercial enhancement to the open source Ansible, providing web-based console, REST API and other services such as Role-based Access Control (RBAC). Managing Ansible via REST API is still somewhat involving but this also enables other open-source contributions to simplify the use of API. For example &lt;a href="https://docs.ansible.com/ansible-tower/3.5.3/html/towerapi/tower_cli.html" class="rank-math-link"&gt;Tower CLI&lt;/a&gt; allows you to use Ansible Tower with simplified command. Ansible Tower has an open-source upstream project called &lt;a href="https://www.ansible.com/products/awx-project/faq" class="rank-math-link"&gt;AWX&lt;/a&gt;, maintained by Red Hat. &lt;a href="https://github.com/ansible/awx" class="rank-math-link"&gt;AWX&lt;/a&gt; is essentially a preview release of Ansible Tower without commercial support. AWX can serve as an engine for all Ansible related task. AWX server is essentially an Ansible control server. AWX, or Ansible Tower, also brings several concepts on top of Ansible:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Job template:&lt;/strong&gt; defines how an Ansible playbook should be executed, including details such as machine credential, project, inventory, and playbook file.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Job:&lt;/strong&gt; the actual execution of job template&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Project: &lt;/strong&gt;connects Ansible Tower to source control such as BitBucket. It is tied to a Git repository and a branch within that repository&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To deploy AWX on EC2 instances, there is a &lt;a href="https://aws.amazon.com/quickstart/architecture/awx/" class="rank-math-link"&gt;reference deployment&lt;/a&gt; by AWS. However, it is provided as CloudFormation template and appears to be outdated (from 2018). In our &lt;a href="https://github.com/digihunch/atlab" class="rank-math-link"&gt;project&lt;/a&gt; (late 2020, named ansible tower lab, or dubbed as &amp;#8220;atlab&amp;#8221;), we provide the infrastructure in AWS CDK (written in typescript), to provision the AWX environment. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The goal is that once the configuration is completed, you can run ansible ping against a target EC2 instance. The steps are as automated as possible. However, a number of key steps are purposefully left manual for learning purpose, such as the installation of AWX on EC2 instance.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-infrastructure-as-code"&gt;Infrastructure as Code&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In previous &lt;a href="https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/"&gt;posting&lt;/a&gt;, I created infrastructure as code in AWS CDK with Python, so I decided to change to typescript in this project, with the assumption it is just a matter of syntax mapping. However, I underestimated the transition to a new language I never learned before. A fuzzy understanding of little details such as when to use let a=4 vs this.a=4, may produce elusive errors that takes hours to troubleshoot. I would therefore strongly recommend reading the basic syntax &lt;a class="rank-math-link" href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-oop.html"&gt;guide&lt;/a&gt; for typescript, before getting started. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In typescript, this example project provides an implementation of configuring autoscaling groups, including cloud init, user data, etc on AWS. Other than the language, everything else is very similar to the project in this &lt;a href="https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/" class="rank-math-link"&gt;post&lt;/a&gt;, which was developed in Python. Also, note that the project directory structure varies slightly based on the language being used.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you are absolutely new to AWS cdk, start with this &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html" class="rank-math-link"&gt;app&lt;/a&gt;. It is beyond the scope of this post, to cover extensively the installation and environment configuration of AWS CDK.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the provision process for Bastion host, the cloudformation init script pulls a specific version from AWX repository, then makes slight modification. User will need to install it manually. Note that AWX can be installed on three types of platforms:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;OpenShift&lt;/li&gt;&#10;&lt;li&gt;Kubernetes&lt;/li&gt;&#10;&lt;li&gt;Docker Compose&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All are documented in their &lt;a class="rank-math-link" href="https://github.com/ansible/awx/blob/devel/INSTALL.md"&gt;README file&lt;/a&gt;. For simplicity in this project, the installation is on standalone docker compose. This is the default mode so there is no need to modify the inventory file.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-the-code-repo"&gt;The code repo&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The repository is version controlled &lt;a href="https://github.com/digihunch/atlab" class="rank-math-link"&gt;here&lt;/a&gt;. To run the project, you need to have aws cli environment, then install the required packages including node js, and npm packages such as aws cdk. Once configured, validate with command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk ls&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This should display the stacks available. Use cdk deploy to deploy each stack.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When BastionStack is deployed, dependent packages should be installed with user data and cloud init. You will just need to SSH on to the server to manually install AWX, as explained in the instruction, to manually install AWX:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ansible-playbook -i inventory install.yml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then you can browse to the server (at port 80 by default). Before the log-in page for the first time, the AWX will upgrade itself, with the following screen presented:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1446" height="988" src="https://static.digihunch.com/wp-content/uploads/2020/12/image-1.png" alt="" class="wp-image-1968"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now log on with default credential (in README.md), you will have the UI for AWX:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1297" height="930" src="https://static.digihunch.com/wp-content/uploads/2020/12/image-4.png" alt="" class="wp-image-1979"/&gt;&lt;figcaption class="wp-element-caption"&gt;AWX Web Console&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;From here, you can edit inventory by adding the host. Or use the helper script (~/awxcompose-helper.sh) from bastion host to create a new inventory (named Private Instance Inventory), and populate it with the hosts in the stack. The helper script does so by querying aws resource, and isssue rest API calls to AWX. After executing the script, you can see a new inventory, and the Private instance inventory should contain all hosts in the stack:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="756" height="774" src="https://static.digihunch.com/wp-content/uploads/2020/12/image-6.png" alt="" class="wp-image-1986"/&gt;&lt;figcaption class="wp-element-caption"&gt;Automatically populated inventory&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can then run Ansible ping against the host to validate connectivity. Note that during inventory creation, the ansible_user is already set to ec2-user (by the helper script):&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="921" height="908" src="https://static.digihunch.com/wp-content/uploads/2020/12/image-7.png" alt="" class="wp-image-1987"/&gt;&lt;figcaption class="wp-element-caption"&gt;Ping result&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-some-technical-details"&gt;Some technical details&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The initialization process on Bastion host creates an RSA key pair, stores the public key to AWS, for the upcoming private instances to uses. It keeps the private key locally in order to make outgoing SSH connection to the private instances. To ensure connectivity between AWX and private instances, there are a couple of (bash) helper scripts involved. Both reflects some technical details that I had to work through.&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;awxcompose-helper.sh&lt;/strong&gt;: the initialization process pulls AWX installation file from git repo. The installation process will build a docker-compose file in ~/.awx/awxcompose, based on a template (~/awx-*/installer/roles/local_docker/templates/docker-compose.yml.j2). When user tells AWX to connect to private instance, the connection was made out of a docker container (instead of from the OS of bastion host), we need this script to map SSH key file from host to container, by modifying the template file. Without this helper, outgoing SSH connection will fail with error (Permission denied (publickey,gssapi-keyex,gssapi-with-mic)). This script is invoked in the cloud init process without requiring manual execution.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;awxinvt-helper.sh&lt;/strong&gt;: once the private stack is up and the installation has completed, we need to add the hosts to AWX inventory. This script gets the instance ID and IP addresses of the private instances, and uses Rest API calls to create inventory and populate it with hosts. Ansible has multiple &lt;a href="https://www.ansible.com/blog/summary-of-authentication-methods-in-red-hat-ansible-tower" class="rank-math-link"&gt;ways of authentication&lt;/a&gt;. This script uses the non-stateful basic authentication with each curl command requiring credential. Ansible Rest API guide is provided &lt;a href="https://docs.ansible.com/ansible-tower/latest/html/towerapi/api_ref.html" class="rank-math-link"&gt;here&lt;/a&gt; and be wary of the &lt;a href="https://docs.ansible.com/ansible-tower/latest/html/towerapi/conventions.html" class="rank-math-link"&gt;convention&lt;/a&gt; where URI must end with a slash. &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This project is just a start of AWX on AWS CDK project using Typescript. In real life scenarios, there are some work to do to make this even more automated. For example, use cfn-hup service to monitor changes of private stack, and therefore update inventories accordingly. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/12/high-performance-computing-cluster/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;High Performance Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/01/basic-kubernetes-resource-object-1-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Basic Resource Object in Kubernetes 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AWS CDK example in Python – provision Kubernetes Nodes</title><link>https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/</link><pubDate>Thu, 03 Dec 2020 21:14:00 -0400</pubDate><guid>https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/</guid><description>&lt;p class="wp-block-paragraph"&gt;There are two mechanisms to initialize instances in AWS. Cloud init and CloudFormation Init. Both are widely used and we discuss each of them in this posting. Then we will give an example of using AWS CDK in Python.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cloud-init"&gt;Cloud-Init&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud-Init is a service originally built for Ubuntu, as a bootstrapping utility to customize a Linux VM as it boots for the first time. It has evolved to be an industry standard multi-distribution method for cross-platform (public or private) cloud instance initialization, or even bare-metal installation. In &lt;a href="https://cloudbase-init.readthedocs.io/en/latest/intro.html" class="rank-math-link"&gt;cloud-init&lt;/a&gt; you can install packages and write files, or configure users and security. Because cloud-init is called during the initial boot process, there are no additional steps or required agents to apply your configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud-Init uses UserData, which is part of instance metadata. With AWS, you can pass two types of user data to&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html#user-data-shell-scripts" class="rank-math-link"&gt; Amazon EC2&lt;/a&gt;: shell scripts and cloud-init directives.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The cloud-config files are text files encoded in base64, with more details covered in the documentation &lt;a href="https://cloudinit.readthedocs.io/en/latest/topics/format.html#cloud-config-data" class="rank-math-link"&gt;here&lt;/a&gt;. cloud-init also works across distributions. For example, you don&amp;#8217;t use apt-get install or yum install to install a package. Instead you can define a list of packages to install. cloud-init automatically uses the native package management tool for the distro you select.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cloudformation-init"&gt;CloudFormation Init&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The cloudformation init mechanism does not only initialize instance, it also provides a mechanism for the resource being created to communicate with other resources. It allows an instance to emit signal to a different resource (via cfn-signal). It can also monitor changes to external resource and invoke local action (using cfn-hup with hooks). CloudFormation Init requires several components to work together:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The cloudformation resource should have metadata. The metadata must have a key &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html" class="rank-math-link"&gt;AWS::CloudFormation::Init&lt;/a&gt; in which configsets are declared.&lt;/li&gt;&#10;&lt;li&gt;The UserData must use helper script (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html" class="rank-math-link"&gt;cfn-init&lt;/a&gt;) to invoke configuration jobs&lt;/li&gt;&#10;&lt;li&gt;The UserData can use helper script (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-signal.html" class="rank-math-link"&gt;cfn-signal&lt;/a&gt;) to signal with a CreationPolicy or WaitCondition (of the same or different resource), so you can synchronize other resources in the stack when the prerequisite resource or application is ready.&lt;/li&gt;&#10;&lt;li&gt;The &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-hup.html" class="rank-math-link"&gt;cfn-hup&lt;/a&gt; service on the instance can be configured, to check for updates to metadata and execute custom hooks when changes are detected.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-comparison"&gt;Comparison&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While there are overlaps between the functionalities of Cloud Init and CloudFormation Init, the major difference is the latter support extended features (signal, update, etc); whereas the former is vendor neutral. The table below summarized some the differences:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Cloud Init&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;CloudFormation Init&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Works on&lt;/td&gt;&lt;td&gt;Linux OS distribution&lt;/td&gt;&lt;td&gt;CloudFormation resource, in combination with cfn &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html" class="rank-math-link"&gt;helper scripts&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Usecase&lt;/td&gt;&lt;td&gt;Initialization only&lt;/td&gt;&lt;td&gt;Both initialization and resource update&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Trigger&lt;/td&gt;&lt;td&gt;cloud-init systemd service&lt;/td&gt;&lt;td&gt;Initial: from UserData&lt;br&gt;Update: by cfn hook&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Adoption&lt;/td&gt;&lt;td&gt;Multiple cloud vendors and bare-metal system&lt;/td&gt;&lt;td&gt;AWS cloud instances&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Action Playbook&lt;/td&gt;&lt;td&gt;/var/lib/cloud/&lt;br&gt;Instance Metadata -&amp;gt; User Data, encoded in base 64&lt;/td&gt;&lt;td&gt;CloudFormation Resource -&amp;gt; Metadata section -&amp;gt; AWS::CloudFormation::Init -&amp;gt; configSets and configs&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Log file and stdout&lt;/td&gt;&lt;td&gt;/var/log/cloud-init.log&lt;br&gt;/var/log/cloud-init-output.log&lt;/td&gt;&lt;td&gt;/var/log/cfn-init.log&lt;br&gt;/var/log/cfn-init-cmd.log&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Comparison between cloud-init and cfn-init&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-aws-cloud-development-toolkit-cdk"&gt;AWS Cloud Development Toolkit (CDK)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, AWS CloudFormation uses &lt;a href="https://aws.amazon.com/cloudformation/resources/templates/" class="rank-math-link"&gt;template &lt;/a&gt;in YAML or JSON for resource declaration. As the size of system grows, the amount of resource involved grows quickly and the size of such declaration file may grow beyond manageable.&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html" class="rank-math-link"&gt; Nested stacks&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html" class="rank-math-link"&gt;export of output&lt;/a&gt; are mechanisms designed to combat the template sprawling, but to a very limited extent. Two reasons it is hard to control template size are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;In declarative statements, each line carries very small piece of information. Without flow controls such as if-else, loops, object oriented structure, the level of code reusability is very low;&lt;/li&gt;&#10;&lt;li&gt;Some auxiliary resources (such as AWS::EC2::VPCGatewayAttachment) must be declared explicitly, even though they are insignificant to the stack functionality&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To address these challenges, AWS introduced &lt;a class="rank-math-link" href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt; (cloud development tookkit), which supports multiple languages (JavaScript, TypeScript, Python, Java, and C#). The CDK was natively developed in TypeScript, which is supposed to be the preferred development language. A &lt;a class="rank-math-link" href="https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html"&gt;tutorial &lt;/a&gt;is provided here, with detailed API documentation &lt;a class="rank-math-link" href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2022/04/image-1.png" alt="" class="wp-image-4962" width="621" height="242"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-python.html" class="rank-math-link"&gt;install &lt;/a&gt;aws cdk and create a hello world project, follow &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html" class="rank-math-link"&gt;this &lt;/a&gt;example.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-an-example-in-python"&gt;An Example in Python&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have created an example for AWS CDK in Python. The purpose is to create some EC2 instance to complete a lab for Kubernetes (without using managed EKS service). The example provisions the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;VPC, a public and private subnets, Internet and NAT gateways;&lt;/li&gt;&#10;&lt;li&gt;Relevant security groups and permissions&lt;/li&gt;&#10;&lt;li&gt;Bastion host, public instances in public subnet&lt;/li&gt;&#10;&lt;li&gt;Private instances in private subnet, with public route through NAT gateway&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The private instances forms a cluster for Kubernetes lab. We will use &lt;a href="https://kubernetes.io/docs/setup/production-environment/tools/kubespray/" class="rank-math-link"&gt;kubespray &lt;/a&gt;to initialize these instances. During the bootstraping, we download &lt;a href="https://github.com/kubernetes-sigs/kubespray" class="rank-math-link"&gt;kubespray&lt;/a&gt;, install ansible, etc.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is the code &lt;a href="https://github.com/digihunch/kubelab/tree/main/kube-cdk"&gt;repo&lt;/a&gt; for this example. With CloudFormation only, the single template could go well beyond 1000 lines. With CDK, the code are organized into several different python files, each representing a stack. The stacks can be stood up with command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy vpc-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy security-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy bastion-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy private-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Although the &lt;a href="https://docs.aws.amazon.com/cdk/api/latest/python/index.html" class="rank-math-link"&gt;documentation &lt;/a&gt;in Python is available, there are generally not a lot of examples built out on the Internet. The &lt;a href="https://pypi.org/" class="rank-math-link"&gt;pypi &lt;/a&gt;site provides some Python specific examples for each module (e.g. &lt;a href="https://pypi.org/project/aws-cdk.core/" class="rank-math-link"&gt;core &lt;/a&gt;and &lt;a href="https://pypi.org/project/aws-cdk.aws-ec2/" class="rank-math-link"&gt;aws-ec2&lt;/a&gt;). Given these libraries are available for only 2 years (since 2018), many advocates TypeScript as the language. However, I have implemented some CloudFormation init, used helper script, and UserData in this example, without running into any language specific issues.It should be noted that the EC2 instance by default will call cfn-init. So there is no need to explicitly run cfn-signal or cfn-init from user data in python code (&lt;a href="https://github.com/digihunch/kubelab/blob/main/kube-cdk/kube_cdk/bastion_stack.py" class="rank-math-link"&gt;example&lt;/a&gt;). This can be verified in file /var/lib/cloud/instances/&amp;lt;instance-id&amp;gt;/user-data.txt which automatically includes the following lines:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# fingerprint: e1b32ead13878deb&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set +e&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /opt/aws/bin/cfn-init -v --region us-east-1 --stack bastion-stack --resource bastionhost5F466975da9934ba490de456 -c config_set_1,config_set_2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /opt/aws/bin/cfn-signal -e $? --region us-east-1 --stack bastion-stack --resource bastionhost5F466975da9934ba490de456&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cat /var/log/cfn-init.log &amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In addition to Python, AWS CDK also supports other languages. In the &lt;a href="https://static.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/"&gt;next&lt;/a&gt; post, we will discuss use of CDK in Typescript.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;IPVS, iptables and kube-proxy&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/12/high-performance-computing-cluster/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;High Performance Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>