Server-Side Swift With Vapor, AWS Fargate, and the AWS Cloud Development Kit
By: Yuri Ferretti
Tags: Swift, Vapor, Programming, Xcode, AWS

3. AWS Fargate and AWS CDK

Now it's time to start thinking about the cloud infrastructure where our app will be deployed. Wouldn't be nice if we could invest our main focus on developing the app itself and not the infrastructure aspects like scaling, security, and provisioning? Fear not: AWS Fargate is here to help.

In AWS Fargate docs’ own words:

“AWS Fargate is a serverless compute engine for containers that works with both Amazon Elastic Container Service (ECS) and Amazon Elastic Kubernetes Service (EKS). Fargate makes it easy for you to focus on building your applications. Fargate removes the need to provision and manage servers, lets you specify and pay for resources per application, and improves security through application isolation by design. Fargate allocates the right amount of compute, eliminating the need to choose instances and scale cluster capacity. You only pay for the resources required to run your containers, so there is no over-provisioning and paying for additional servers.”

This sounds like a very good candidate for our app compute platform. So let's start writing our infrastructure code and deploy it.

Yes, you read that right. I said, “Let's write the infrastructure code.”

In July 2019, AWS launched its Cloud Development Kit (CDK), a toolkit that enables you to define your infrastructure in a code-first approach. This way, you can easily define your infrastructure resources using many programming languages, leverage code versioning, and avoid copying and pasting a lot of ARNs in AWS console.

To proceed to the next steps, you should have an AWS account configured. Then you need to install AWS CDK. Near the bottom of the linked page, you will find how to install CDK and its prerequisites. Make sure to use TypeScript as the programming language since we'll be using it in this tutorial.

Jump back to your terminal window and navigate to your project root directory. Then run:

#1 $ mkdir cdk

#2 $ cd cdk

#3 $ cdk init app --language typescript

Create a new directory called cdk inside your project root folder. Enter the created cdk folder. Create a new cdk app template prepared for the TypeScript language.

This last command will install a lot of Node.js dependencies. After that is done, you should have a project structure that looks like this:

Open the bin/cdk.ts file (I suggest using VSCode or a text editor other than Xcode) and you should see the following code:

Change Line 6 to new CdkStack(app, 'VaporSwiftStack'); to change the stack name to VaporSwiftStack . CDK uses a concept from AWS called stack that encloses and encapsulates a set of related resources in your cloud infrastructure.

Now jump into lib/cdk-stack.ts and you should see something like this:

This file defines a CdkStack class, and we will define all the other resources required to create our infrastructure inside the class constructor.

Now jump back to the terminal and run:

$ npm install @aws-cdk/aws-ecr-assets

This will install a CDK module capable of uploading our Docker image to the AWS Elastic Container Registry. In order to do that, modify your class to the following:

Import the DockerImageAsset class from our recently installed module. Import the path module so we can manipulate file paths. Create a new instance of DockerImageAsset that will be responsible for uploading our image to ECR. The directory property is the local directory that has our Dockerfile that will be built. The exclude property will exclude the specified folders or files from our image build. Set our repository name to vapor-swift-image-repo .

Since we will need to upload assets like our Docker image, we first need to bootstrap our CDK application with our AWS account. To do so, run the following in your terminal:

$ cdk bootstrap

You will see a lot of resources being created and finally something like this as the output:

⏳ Bootstrapping environment aws://<acount-id>/us-east-1...

CDKToolkit: creating CloudFormation changeset...

[██████████████████████████████████████████████████████████] (3/3) ✅ Environment aws://<account-id>/us-east-1 bootstrapped.

So far, so good. Let's continue with our CdkStack class. Before that, let's install two more required CDK modules in our project. Reach your terminal window and run:

$ npm install @aws-cdk/aws-ecs @aws-cdk/aws-ecs-patterns

Now jump back to the lib/cdk-stack.ts source file and change it to the following:

Import the AWS ECS module. Import the ECS patterns module that has the pattern we will use to deploy and run our Docker image. Create a cluster that will contain a task running our Docker image and therefore our Vapor app. Create an ApplicationLoadBalancedFargateService (more on that later). Set the cluster we just defined in our Fargate definition. Set the maximum memory to 512 Mb. Define that we will only have one task running with our service. Define our service name. Set the image to be run as our previously created ECR asset. Set our container port to 8080 (more on that later). Set the load balancer that will run in front of our Fargate service to be visible outside our VPC.

An ApplicationLoadBalancedFargateService is a deployment pattern that will create an ECS task (our service running in our Docker container) managed by Fargate and put it behind an Application Load Balancer. This way, any traffic sent to the load balancer DNS will be redirected to the containerPort defined in point No. 10.

As you may have noticed, you can configure Fargate service properties like CPU, memory, the number of tasks, and a lot more. Please visit this AWS CDK page to learn more about all the options you can define.

Now let's build our infrastructure code. Go back to your terminal and run:

$ npm run build

If everything was installed and imported correctly, the TypeScript compiler should emit no errors. Now let’s deploy our infrastructure by running:

$ cdk deploy --require-approval never

This will generate a cloud formation template change set and deploy our code without asking if it can create or update IAM roles and policies.

It will take a while and you will start seeing the same output that the docker build command generated in Section 2. After the image is built, you will see an output like the following:

CdkStack: creating CloudFormation changeset...

[██████████████████████▎···································] (15/39) 9:59:18 PM | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | CdkStack

10:00:15 PM | CREATE_IN_PROGRESS | AWS::EC2::VPCGatewayAttachment | Cluster/Vpc/VPCGW

10:00:15 PM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | Cluster/Vpc/PublicS

ubnet2/Subnet

10:00:15 PM | CREATE_IN_PROGRESS | AWS::IAM::Policy | VaporSwiftFargate/.

..Role/DefaultPolicy

10:00:15 PM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | Cluster/Vpc/Private

Subnet1/Subnet

10:00:16 PM | CREATE_IN_PROGRESS | AWS::EC2::Subnet | Cluster/Vpc/PublicS

ubnet1/Subnet

10:00:19 PM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | VaporSwiftFargate/L

B/SecurityGroup

10:00:20 PM | CREATE_IN_PROGRESS | AWS::EC2::SecurityGroup | VaporSwiftFargate/S

ervice/SecurityGroup

This shows all the resources being created in your AWS account.

When the resource creation finishes deploying, you will see the following output:

Finally, grab the HTTP URL in the output list (this is your load balancer address — and your address will be different from this one) and check if the service is up by running the following in your terminal:

And you should see:

$ I'm the root path

Then append /hello to the URL path and run curl again:

You should receive:

$ Hello, AWS!!!

Awesome! You have a Vapor server running in AWS. Pretty easy, pretty fast, minimal effort, and you didn’t have to reach the AWS console.

Now I recommend playing around with your Vapor server and then redeploying it. Add other routes for other HTTP methods like POST and PUT , and check what else you can do.

Before wrapping up, I must remind you to clean up your AWS-created resources. Otherwise, you'll be billed for their usage. To delete your stack, reach your terminal and run: