Infrastructure as Code made easy for React engineers
Using CDK to setup React App infrastructure
We are react engineers, we build impressive front-end app. But if we need another team’s help to launch our work, it diminishes the value of our work; if another team has high priority work and say “sorry” to us, we may felt paralyzed.
We don’t need to, and building the full infrastructure for launching our app could be as easy as writing the Javascript (to me, Typescript) code, thanking to AWS CDK.
(This is a follow up post of “Does Serverless make DevOps jobs less relevant in the long run?” in the context of front-end engineering.)
What is CDK?
AWS CDK is the acronym of AWS Cloud Development Kit. It is quite revolutionizing in the infrastructure as code (IaC) world, particularly to software engineers — because it allows software engineers to define IaC with the language they use everyday instead of going through lengthy json or yaml files.
Who should spend sometime to understand this?
A lot of front-end engineers I talked to know how to host and launch their app through services like Heroku, Netlify and Vercel — which include the hosting service and CDN under the hood — out-of-box solutions for developers.
But sometime companies may want to manage their infrastructure in AWS.
The above solutions may not fit your bill. For example, Vercel’s free tier (cost-free) only allows for hobby / non-commercial project. What if you wanted to build a side-project that you want to potentially charge your customer?
If that’s the case, is that very hard for front-end engineers to setup the needed infrastructure for React apps? The answer is No, and you can set up your infrastructure as easy and as familiar as you do ‘create-react-app’, everything is under 5 mins — through AWS CDK.
Build the S3 + Cloudfront as the React hosting solution in 5 mins
Essentially, we need to have S3 bucket as our build asset storage, and then we need to have Cloudfront as our CDN and app serving layer:
S3 is easy to understand as it’s the single page application storage infrastructure;
Why do we need Cloudfront?
it gives much better end-user experience as it’s a CDN infrastructure;
it provide the HTTPS service (S3 does not), so that we can build secure servicing layer.
We only need the following ~40 lines of code to build the React app hosting infrastructure.
bin/cdk-fe-stack.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { CdkFeStackStack } from '../lib/cdk-fe-stack-stack';
const app = new cdk.App();
new CdkFeStackStack(app, 'CdkFeStackStack', {
env: { region: 'us-east-1' }
});
lib/cdk-fe-stack-stack.ts
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
export class CdkFeStackStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
// S3
const bucket = new s3.Bucket(this, "CDKClearCacheBucket", {
publicReadAccess: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
websiteIndexDocument: "index.html"
});
// Cloudfront
const cf = new cloudfront.CloudFrontWebDistribution(this, "CDKClearCache", {
originConfigs: [
{
s3OriginSource: {
s3BucketSource: bucket
},
behaviors: [{isDefaultBehavior: true}]
},
]
});
}
}
Please read this github repo, a CDK project, if you wanted to follow what we discussed here
Using Github Action for CI/CD
One more thing we need to achieve is the continuous integration and continuous deployment — when we check in our code to github, we need the new code get service to our live site.
Edit: we added another post explaining cache invalidation mechanism — please read it if you’d like to understand the under the hood mechanism.
And that can be done with Github Action. Here is the Github action pipeline file I quickly wrote for the purpose of this post.
name: Upload Website
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-build- ${{ runner.OS }}-
- name: Install
run: npm install
- name: Build
run: npm run build
- name: Install awscli2
run: |
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --update
aws --version
- name: Upload
run: |
aws s3 cp \
--recursive \
--region ${{ env.AWS_REGION }} \
${{ env.SOURCE_DIR }} s3://${{ env.AWS_S3_BUCKET }} env:
NODE_ENV: development # optional: defaults to production
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: us-east-1 # optional: defaults to us-east-1
SOURCE_DIR: build # optional: defaults to public
- name: Invalidate Cloudfront
run: |
aws cloudfront create-invalidation --distribution-id \ ${{ env.CLOUDFRONT_ID }} --paths "/*" --region us-east-1
env:
CLOUDFRONT_ID: ESVTWI4SBI9PL
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Essentially, we need to
when the code get checked into github repo, trigger the build
if the build is successful, push the new build asset to S3 bucket we set up in the previous session;
invalidate the Cloudfront cache, so that the client get the new release.
BTW, if you work with a large team, you may consider reading our other post “I'll never step on your toes...”, in that article, we talked about how to prevent multiple team members from blocking the healthiness of a branch.
Summary:
In this post, we discussed an easy way for front-end engineers to set up their hosting infrastructure, with zero cost
A free tier Github give us 2000 action mins each month (that’s a lot for building a side project)
AWS Cloudfront free tier can service tens of thousands requests for a small site
AWS S3 free tier (5GB) can host almost any SPA project asset for free