Infrastructure & Automation

Using AWS CloudFormation to deploy software into Amazon EKS clusters

Updated 31 May 2020

This blog post has been updated to cover the new public Amazon Elastic Kubernetes Service (Amazon EKS) resource and the new public Helm resource types that have been added to the Amazon EKS Quick Start.

Do you like using Helm in Kubernetes to deploy applications into your cluster? Would you rather not have to interact with a bastion host when installing applications into the cluster. What if you could use AWS CloudFormation to install the applications that you normally install with Helm and Helm charts?

The following post covers how to use AWS CloudFormation to model and deploy Kubernetes resource manifests or Helm charts into an Amazon EKS cluster. This can be useful for bootstrapping clusters with required components. Or, for applications that depend on AWS resources, you can model both the application and its dependencies in a single AWS CloudFormation YAML document.

Solution overview

In this walkthrough, I cover using AWS CloudFormation to call Helm in the Amazon EKS cluster to deploy WordPress. Kubernetes uses Helm as the package manager, which allows you to more easily package, configure, and deploy software into a Kubernetes cluster. Helm packages are called charts, and they are made up of YAML configuration files and some templates that are rendered into Kubernetes manifests. For more information on Helm, see the Helm site.

I chose to use WordPress as the example software for deployment because it has a web server, a database, and a load balancer, items that are typical in most web applications.

Before working through each example, take a couple of minutes to review the AWS CloudFormation template associated with the example. This will help you to understand, how AWS CloudFormation is building the stack.

Deployment with Helm

In the first deployment, AWS CloudFormation calls Helm to install WordPress into a namespace within the Amazon EKS cluster. Because I don’t specify any parameters to an external database, the deployment automatically steps up its own database in a container. This is the default behavior for the Helm chart. This is an example of how to deploy WordPress for non-critical workloads, because all of the data is stored within the container for the database. You are governed by the storage available to the pod, and there is no built-in resiliency.

Deployment with Helm and an external database

In the second deployment, AWS CloudFormation calls Helm to install WordPress into a namespace using an external database. For the external database, I use Amazon Relational Database Service (Amazon RDS) with the MariaDB engine. This is an example of a way to deploy WordPress with data that might be more critical. Amazon RDS features give you increases in elasticity and resiliency. Helm knows to use the external database because I provide some parameters that allow Helm to tie WordPress to the external Amazon RDS database.

As you work through the examples, think about how you can use this deployment method to manage your Amazon EKS environment. Being able to manage your Amazon EKS cluster bootstrapping, as well as the provisioning of applications into the cluster, can potentially reduce administration effort and time. It also allows you to deploy similar environments for disaster recovery or for other business requirements in a more streamlined fashion.

Prerequisites

Before working through the deployment part of the blog post, you must complete the following tasks.

Deploy an Amazon EKS cluster by using the Modular and Scalable Amazon EKS Architecture Quick Start. After the Amazon EKS cluster is deployed, on the Outputs tab, note the following outputs.

  • KubeClusterName
  • KubeGetLambdaArn

If you are using the second deployment example, which includes an external database, you also need the following outputs.

  • NodeGroupSecurityGroupId
  • BastionSecurityGroupId

Deployment with Helm

The following is one way that you can use an AWS CloudFormation template to deploy software into an Amazon EKS cluster using Helm. When you use AWS CloudFormation to deploy software into your cluster, you can then use features of this service to manage your environment, including changes, additions, and removal of items from the cluster.

This example is similar to how you might want to deploy WordPress into a development Amazon EKS cluster. This lets the Helm chart create the database using the same backing storage as the WordPress nodes.

The CloudFormation template

The template installs the WordPress Helm chart the same as if you logged in to the Kubernetes cluster and ran the following command.

helm install stable/wordpress

The following section of the template shows how Helm is used to deploy WordPress. It also creates a load balancer host name, so that you can access the WordPress site.

Resources:
  HelmExample:
    Type: "AWSQS::Kubernetes::Helm"
    Properties:
      ClusterID: !Ref KubeClusterName
      KubeConfigKmsContext: !Ref KubeConfigKmsContext
      Namespace: !Ref Namespace
      Chart: stable/wordpress
      Name: !Ref Name
      Values:
        wordpressUsername: !Ref wordpressUsername
        wordpressPassword: !Ref wordpressPassword
  WPElbHostName:
    DependsOn: HelmExample
    Type: "Custom::KubeGet"
    Version: '1.0'
    Properties:
     ServiceToken: !Ref KubeGetLambdaArn
     ClusterName: !Ref KubeClusterName
     KubeConfigKmsContext: !Ref KubeConfigKmsContext
     Namespace: !Ref Namespace
     Name: !Sub 'service/${Name}-wordpress'
     JsonPath: '{.status.loadBalancer.ingress[0].hostname}'

Run the template

To run the template, do the following.

  1. Copy the helm3-wordpress-deploy.yml template file, and save it on your computer.
  2. Sign in to the AWS account where you deployed Amazon EKS.
  3. Choose Services, choose CloudFormation, and then choose Stacks.
    Your existing Amazon EKS stack should be listed.
  4. In the upper right, choose Create stack, and then choose With new resources (standard).
  5. In the Specify template section, choose Upload a template file, and then choose Choose file.
  6. Navigate to the file named helm-wordpress-deploy.yml that you saved in step 1.
  7. On the Specify stack details page, enter the required information.
    Some parameters have defaults assigned.

    • KubeClusterName
    • KubeGetLambdaArn
    • Namespace
    • Name
    • wordpressPassword

    You can change the default values, but make sure that you note the changes that you make. You will need those values later.

  8. On the Configure stack options page, choose Next.
  9. On the Review <Stack Name> page, review all information. Then choose Create stack.

Test WordPress

To test the deployment, log in to the WordPress site by using the load balancer URL.

  1. On the new stack’s Outputs tab, get the value of WPElbHostName.
  2. In your preferred browser, enter the value of WPElbHostName.
  3. Under the Meta section of the page, choose Log in.
  4. Enter the WordPress Username and Password you created in the stack creation.

You are now in the user’s dashboard. Congratulations! You have used AWS CloudFormation and Helm to deploy a version of WordPress.

Deployment with Helm and an external database

For this example, I am deploying WordPress as the software, with MariaDB in Amazon RDS as the external database. Both the external database and WordPress are deployed by same the AWS CloudFormation template. This is similar to how you might want to deploy software into a production Amazon EKS cluster. Keeping the application data in a separate Amazon RDS database gives you options for more resiliency and elasticity.

The CloudFormation template

The template installs the WordPress Helm chart into the cluster and installs an external MariaDB database using Amazon RDS. This is similar to manually running the following Helm command.

helm install --name <release-name> \
    --set wordpressUsername=admin,wordpressPassword=<password>,mariadb.enabled=false\
    ,externalDatabase.host=<hostname>,externalDatabase.user=<username>\
    ,externalDatabase.password=<password>,externalDatabase.database=<db-name>\
    ,externalDatabase.port=3306 stable/wordpress

The following section of the template, as in the first example, shows Helm creating a WordPress site and a load balancer host name. Then, this template creates an Amazon RDS instance and a database.

WPDB:
    Type: AWS::RDS::DBInstance
    Properties:
        AllocatedStorage: '10'
        AutoMinorVersionUpgrade: true
        BackupRetentionPeriod: 7
        DBInstanceClass: db.m5.large
        # In the next line of code the '-DB' delimeter is used to get the root stack name for database identifier
        # 'AWS::StackName' produces MASTER_STACK_NAME-DB (as DB is the name of the nested stack resource).
        DBInstanceIdentifier: !Sub ["${RootStack}-db", RootStack: !Select [0, !Split ['-DB', !Ref 'AWS::StackName']]]
        DBSubnetGroupName: !Ref DBSubnetGroup
        Engine: mariadb
        EngineVersion: '10.3'
        MasterUsername: !Ref DBMasterUsername
        MasterUserPassword: !Ref DBMasterUserPassword
        MultiAZ: false
        StorageEncrypted: true
        StorageType: gp2
        DBName: !Ref DBName
        Tags:
          - Key: Name
            Value: !Sub ["${StackName} Confluence MariaDB Database", StackName: !Ref 'AWS::StackName']
        VPCSecurityGroups:
        - !GetAtt DBEC2SecurityGroup.GroupId
HelmExample:
    Type: "AWSQS::Kubernetes::Helm"
    Properties:
        ClusterID: !Ref KubeClusterName
        KubeConfigKmsContext: !Ref KubeConfigKmsContext
        Namespace: !Ref Namespace
        Chart: stable/wordpress
        Name: !Ref Name
        Values:
            wordpressUsername: !Ref wordpressUsername
            wordpressPassword: !Ref wordpressPassword
            mariadb.enabled: false
            externalDatabase.host: !GetAtt WPDB.Endpoint.Address
            externalDatabase.user: !Ref DBMasterUsername
            externalDatabase.password: !Ref DBMasterUserPassword
            externalDatabase.database: !Ref DBName
            externalDatabase.port: 3306
WPElbHostName:
    DependsOn: HelmExample
    Type: "Custom::KubeGet"
    Version: '1.0'
    Properties:
        ServiceToken: !Ref KubeGetLambdaArn
        ClusterName: !Ref KubeClusterName
        KubeConfigKmsContext: !Ref KubeConfigKmsContext
        Namespace: !Ref Namespace
        Name: !Sub 'service/${Name}-wordpress'
        JsonPath: '{.status.loadBalancer.ingress[0].hostname}'

Run the template

To run the template, do the following.

  1. Copy the helm3-wordpress-deploy-RDS.yml template file, and save it on your computer.
  2. Sign in to the AWS account where you deployed Amazon EKS.
  3. Choose Services, choose CloudFormation, and then choose Stacks.
    Your existing Amazon EKS stack should be listed.
  4. In the upper right, choose Create stack, and then choose With new resources (standard).
  5. In the Specify template section, choose Upload a template file, and then choose Choose file.
  6. Navigate to the file named helm-wordpress-deploy-RDS.yml that you saved in step 1.
  7. On the Specify stack details page, enter the required information.
    Some parameters have defaults assigned.

    • KubeClusterName
    • KubeGetLambdaArn
    • Namespace
    • Name
    • wordpressPassword
    • DBMasterUserPassword
    • NodeGroupSecurityGroupId
    • BastionSecurityGroupId
    • Subnet1ID
      (Select a private subnet from the main EKS stack.)
    • Subnet2ID
      (Select a different private subnet from the main EKS stack.)
    • VPCID
      (Select the EKSStack VPC.)

    You can change the default values, but make sure that you note the changes that you make. You will need those values later.

  8. On the Configure stack options page, choose Next.
  9. On the Review <Stack Name> page, review all information. Then choose Create stack.

Test WordPress

  1. On the new stack’s Outputs tab, get the value of WPElbHostName.
  2. In your preferred browser, enter the value of WPElbHostName.
  3. Under the Meta section of the page, choose Log in.
  4. Enter the WordPress Username and Password you created in the stack creation.

You are now in the user’s dashboard. Congratulations! You have used AWS CloudFormation and Helm to deploy a version of WordPress and an Amazon RDS database.

Cleanup

To remove the AWS CloudFormation stacks that deployed with Helm and Helm with RDS:

  1. Sign in to the AWS account where you deployed Amazon EKS and the Helm stack.
  2. Choose Services, choose CloudFormation, and then choose Stacks.
    The stacks for your existing Amazon EKS and Helm deployment (or Helm with Amazon RDS deployment) stacks should be listed.
  3. Choose the Helm deployment stack (e.g., helm-example-1) from the first example, and then choose Delete.
  4. On the CloudFormation main page, choose Stacks.
  5. Select the stack for the Helm with Amazon RDS deployment stack (e.g., helm-rds-example-2) from the second example, and then choose Delete.

To remove the Amazon EKS Quick Start stack:

  1. After the two stacks from the examples are deleted, choose the Amazon EKS stack (e.g., Amazon-EKS) from the prerequisites, and then choose Delete.
  2. After all stacks are deleted, your cleanup is complete.

Conclusion

In this blog post, I showed you how you can use Helm in an AWS CloudFormation template to deploy WordPress into an Amazon EKS cluster. In the examples, you saw two different ways of deployment: one that was more of a development method, and one that was more of a production method and gave you more control of the elasticity and resiliency of the database.

In the first example, the database was automatically created by the Helm chart and stored in the same storage as the rest of the pod. In the second example, the template deployed a MariaDB database within Amazon RDS. With this deployment method, you have control over the database’s elasticity and resiliency. This is because you have access to the full compliment of the Amazon RDS services and features at your disposal.

I encourage you to take the information that you have learned here and try applying it to your own environment and applications needs. Also, try modifying one of the examples to see how a stack update works, so you can see the full potential of managing your Amazon EKS environment using AWS CloudFormation. Finally, I recommend taking some time to review how AWS CloudFormation can be used in managing your AWS environment, by reviewing the AWS CloudFormation Resources.

Feel free to leave suggestions about the approaches in this blog post in the Comments section.