First, create a directory for your app and then cd into it:
We’re going to be using the CDK CLI to setup our application, to do this we can use npx:
Then, remove the package.lock.json so we can then swap the app over to use yarn with:
Note that we’ll be adding npm packages as we need them instead of all at once
Now, do git init and push the application up to GitHub as the pipeline will source the code from there
Add our Application Files
Before we jump right into the CDK and pipeline setup, we need an application to containerize. We’re going to create a simple Node.js app which uses express and redis
Create the app directory in the root of our CDK app, init, and add the required dependencies
Since the TypeScript CDK app is setup to ignore .js files by default, we want to create a .gitignore file in our app directory with the following:
app/.gitignore
Then, add an index.js file with the following:
app/index.js
The above consists a simple app which will use service discovery to connect to Redis and create/retreive values based on their key
Next, add a Dockerfile for this application:
app/Dockerfile
And that should be everything we need to do at this point in terms of the application itself - after all, using Redis with Node.js not the focus of this doc
Setup
A CDK Pipeline consists of a few different stage, namely:
Pipeline Stack
To define a pipeline we use the @aws-cdk/core package as the base, create a lib/pipeline-stack.ts file in which we’ll define a Stack which represents our deployment pipeline:
lib/pipeline-stack.ts
Then, instantiate this stack update the bin/pipeline.ts to have the following:
bin/pipeline.ts
Then reference this from your cdk.json file in the root directory:
And also add the following to the context section of your cdk.json file:
cdk.json
Pipeline Account Permissions
First, install the cdk CLI at a project level with:
The reason for this is to ensure we use a version of the cdk that was installed for our specific application and we aren’t accidentally using something that maybe exists somewhere else on our computer
And then add the following to the scripts section of your package.json file:
package.json
Before we can use the pipelines we need to grant CDK some permissions to our account, we can do this with:
The above will create a CDKToolkit stack which you will be able to see in AWS’s CloudFormation Console
GitHub Repo Permissions
We need to provide AWS with credentials to our GitHub repo. To do this go to GitHub > Settings > Developer settings > Personal access tokens and create a token with access to repo and admin:repo_hook permissions
Then add the token to AWS’s Secrets Manager via the console with a plaintext value of the token you just generated above, then name the token github-token and complete the rest of the steps to store the new secret
Develop the Pipeline
Now that we’ve got most of the scaffolding in place, we need to actually deploy our pipeline to AWS so that it’s aware of the codebase and everything else it needs to hook into our git repo for the building and deployment of our project
We need to install some of the cdk libraries packages, we can do this with yarn:
Then we can use these packages in the pipeline-stack.ts file we’re going to add the following imports:
lib/pipeline-stack.ts
Next up, we’re going to be writing everything else within the PipelineStack we defined earlier:
lib/pipeline-stack.ts
First, we need to create sourceArtifact and cloudAssemblyArtifact instances for the pipeline:
Then, we define the sourceAction which is how the pipeline neeeds to get our code from our repository. In this case we use the GitHubSourceAction. We use the SecretValue.secretsManager function to retreive the GitHub token we created previously:
Ensure you’ve replaced the owner, repo and branch with the one that contains your code on GitHub
Then, we define the synthAction which is used to install dependencies and optionally run a build of our app:
And lastly, we combine these to create a CdkPipeline instance:
So our overall lib/pipeline-stack will now look like this:
Next, initialize the Pipeline in AWS by using yarn cdk deploy. This should be the only manual deploy we need. From this point all other Pipeline runs will happen directly in CDK via GitHub Commits:
Add App to Deployment
To create deployments we need to have a class that inherits from cdk.Stage, in this Stage we specify all the requisites for an application deployment. We’re deploying the AppStack application, we will reference it from a Stage called AppStage which will just create an instance of the application:
lib/app-stage.ts
We can then add the above AppStage to the pipeline-stack using the pipeline.addApplicationStage function:
lib/pipeline-stack.ts
Once all that’s been added, the final pipeline-stack.ts file will have the following:
App Stack
Since our app will use a Docker container we need to install the @aws-cdk/aws-ecs, @aws-cdk/aws-ec2 and @aws-cdk/aws-ecs-patterns packages:
Next, from our lib/app-stack.ts file, we want to create two services:
A Docker service which builds a locally defined Docker image
A Docker service which runs the public redis image
In order to define our servivce, we need a vpc, cluster, and some image information and configuration
Importing everything required we would have the following as our AppStack:
lib/app-stack.ts
Our applications need a VPC and Cluster in which they will run, we can define a vpc with:
lib/app-stack.ts
And a cluster:
lib/app-stack.ts
The cluster requires a CloudMapNamespace to enable service discovery. This will allow other containers and application within the Cluster to connect to one another using the service name with the service namespace
lib/app-stack.ts
Using the cluster above, we can create a Task and Service using the NetworkLoadBalancedFargateService as defined in the aws-ecs-patterns library
Defining the appService involves the following steps:
Defining the App as a Docker Asset
lib/app-stack.ts
Defining the App Task
lib/app-stack.ts
Adding a Container Definition to the Task
lib/app-stack.ts
Create a Service
lib/app-stack.ts
Enable Public connections to the serive
lib/app-stack.ts
Defining the Redis service is pretty much the same as above, with the exception that we don’t need to define the Image Asset and we can just retreive it from the reigstry, and instead of allowing public connections we only allow connections from the appService we defined
lib/app-stack.ts
Lastly, we want to add the Load Balancer DNS name to our stack’s outputs. We can do this with the cdk.CfnOutput class:
lib/app-stack.ts
We can break the AppService definition into a createAppService function, and the RedisService into a createRedisService function for some organization, the final lib/app-stack.ts file looks like this:
lib/app-stack.ts
We can kick off the pipeline by pushing to the GitHub repo we setup above which will cause all our services to be deployed. Once that’s done we can go to the Outputs panel for the Dev-AppStage and open the AppLoadBalancerDNS url, this will open our application.
Test the App
Set Data
With the server running you can create a new item with:
Get Data
You can then get the value using the key with:
And if all that works correctly then congratulations! You’ve successfully setup an application that uses multiple Docker Containers with CDK on AWS