I cheated for this one! Read on to see how:
AWS Cloudformation is a robust Infrastructure-As-Code tool. It's well-supported and allows us to write templates in JSON or YAML. And it is also used behind the scenes by a number of tools in the cloud native ecosystem. For example kops. But more importantly for this post - it's used by eksctl
!
In my previous post I used eksctl
to spin up a cluster complete with Karpenter and a few more add-ons.
So for this post - instead of starting from scratch I decided to just reuse the stack template created by eksctl
.
Yes, I could have used one of the quickstart stacks provided by AWS but this repo has so many options that I got lost reading the docs.
So instead I just opted to export the template from the existing stack. That's the cheating part :)
Exporting the CloudFormation template
But how does one export a CloudFormation template from eksctl
? As I found out - this was requested repeatedly, but never implemented. See here for example.
So instead I went to the CloudFormation console in AWS, clicked on the stacks the I wanted (the ones eksctl
generated) and went to the 'Template' tab.
But as you can notice - this only gives us JSON. Now I'm a YAML engineer, so I wanted yaml. For which I clicked on 'View in Application Composer' - and when there - 'Template' and switched the toggle to 'YAML'. Voila - I can now copy the template text and continue editing it on my laptop.
In its basic form eksctl
creates 2 stacks:
- one for the EKS control plane
- another one for the managed nodegroup
I could've bundled both stacks into one file but this separation actually makes a lot of sense. We may want more than one node group in our EKS, or we may decide to let go of node groups and opt to manage nodes with Karpenter. (You should!)
So I created 2 template files - eks.yaml (for the control plane) and ng.yaml (for the node group).
I've edited them both so they have no hard-coded resource names. Everything is based on the stack name you chose.
Second stack receives the name of the first stack as a parameter and uses some of its exports like this:
# the parameter:
Parameters:
ClusterStack:
Description: Name of the ClusterStack
Type: String
Default: eks-way4
# and the reference:
SecurityGroupIds:
- Fn::ImportValue: !Sub '${ClusterStack}::ClusterSecurityGroupId'
I'm also defining SSH access to the nodes with an imported SSH key.
You could of course skip the whole SSH stuff, but I tend to believe it's important - especially when bringing up clusters for learning purposes.
Spinning Up EKS iwth CloudFormation
Here's how to use this:
- Clone the example repo: ```bash
git clone https://github.com/antweiss/9-ways-2-EKS.git
2. Change into the cloudformation folder:
```bash
cd 9-ways-2-EKS/way-4-cloudformation
- Generate an ssh key: ```bash
ssh-keygen -f ./id_rsa -N '' -C eks-way4
4. Insert the public key into the node group template:
I'm saving the original file with *bak* extension.
```bash
sed -ibak "s/SSH_KEY/$(cat id_rsa.pub)/g" ng.yaml
The result should look something like (ng.yaml line 15):
ImportedKeyPair:
Type: AWS::EC2::KeyPair
Properties:
KeyName: !Ref AWS::StackName
# this was PublicKeyMaterial: SSH_KEY
PublicKeyMaterial: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMgmzvgvz7NENY5X25QFLFlMHVCp7U98ykm1s3+JYftI eks-way4
- And finally run the deploy.sh script with 2 parameters - the desired name of the cluster and the AWS region: ```bash
./deploy.sh eks-way4 eu-central-1
Inside the script this is translated into the following 2 CFN (cloudformation) invocations:
```bash
aws cloudformation create-stack --stack-name $1 \
--region $2 \
--template-body file://eks.yaml \
--capabilities CAPABILITY_NAMED_IAM
aws cloudformation create-stack --stack-name $1-ng \
--parameters ParameterKey=ClusterStack,ParameterValue=$1 \
--region $2 \
--template-body file://ng.yaml \
--capabilities CAPABILITY_NAMED_IAM
Things to note here: the necessary capability CAPABILITY_NAMED_IAM
and the parameter named ClusterStack that I'm passing to the second stack.
After a short while we can verify the state of our stacks:
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE --region eu-central-1 --max-items=2 --query "StackSummaries[*].StackName"
If this returns the names of our 2 stacks:
[
"eks-way4-ng",
"eks-way4"
]
then we're great! If not - proceed to the CloudFormation UI in AWS console to find the reasons why your stack creation failed.
Summary
CloudFormation is a great IaC tool if you're fine with AWS vendor-lock. It's definitely possible to create an EKS cluster with CloudFormation and that's what such tools as kops
and eksctl
do under the hood.
While writing CloudFormation from scratch is no fun - we can use the templates generated by eksctl
- as I did. Or we can build the templates ourselves in the AWS Application Composer. But then we need to take into the account everything necessary for the cluster operation - security groups, gateways, routing rules, IAM policies. And that's a lot to take care of.
Are you using pure CloudFormation as your IaC tool?