5. 3. 2019
9 min read
How to build, deploy and host static website and node.js applications using CI/CD pipeline at AWS
If you have an idea and you want to make it public as soon as possible or if you just need a quick, resilient and sustainable solution for deploying and hosting your app, this tutorial is for you. I am going to show you how to setup CI/CD pipeline to build, deploy and host static client and node.js server app at AWS.
Pavol Madar
The goal of this article is to have both client and server side code deployed from Github repository to AWS and be able to run build and deployment pipeline automatically after every push to the Github repository.
Create mono-repo for client and server side code
We are going to choose monorepo over two separate repositories in this case because of a few reasons:
atomic changes - you don’t have to synchronise deployment of client and server because you are deploying both at the same time
ease of code reuse
better development experience Here is out “Hello world” app repository. For the client side I am going to use create-react-app and for the server side node.js.
The folder structure of example application is going to look like this:
AWS Regions
To reduce data latency in your applications, most Amazon Web Services offer a regional endpoint. Before creating the new service in AWS console always make sure that you are in the correct region, you can check it on the right side of the top bar. Exception is CloudFront which only has one global region situated in N.Virginia. That’s why we will have to create the SSL certificate for CloudFront in this specific region, for every other service we will choose the same one region (in our example app Frankfurt).
Setup DNS
Amazon Route 53 is a highly available and scalable cloud Domain Name System (DNS) web service. It is designed to give developers and businesses an extremely reliable and cost effective way to route end users to Internet applications by translating names like www.example.com into the numeric IP addresses like 192.0.2.1 that computers use to connect to each other. Amazon Route 53 is fully compliant with IPv6 as well.<br/> Amazon Route 53 overview
In this section, we are going to create Hosted zone in AWS Route 53, point our domain to this newly created hosted zone and create SSL certificates in Certificate Manager.
Create Hosted zone in Route 53
Go to Route 53
Click “Hosted zones” and “Create hosted zone”
In “Domain name” input enter the name of you domain (without subdomain)
yourdomain.com
Click “Create”
Change NS records at your domain provider (required only if you didn’t buy a domain at Amazon)
Go back to your Hosted zone in Route 53
Copy “Name servers” values
Go to page where you’ve bought domain and change the NS records to match copied “Name servers” from the previous step
Request SSL certificate in Certificate Manager
Navigate to Certificate Manager
Make sure you’re in N.Virginia region, it’s the only region which is available for CloudFront
Click “Request a certificate”
In “Domain name” input enter
yourdomain.com
, if you want to setup subdomain enter*.yourdomain.com
Go through the wizard and validate your certificate (I prefer to validate it with DNS, you just need to click the button which adds DNS record to Route 53 automatically)
NOTE: We need to create the same SSL certificate again, but this time, it has to be in the same region as your Elastic Beanstalk instance will be. If your Elastic Beanstalk instance will be in N.Virginia region skip this step.
Create Elastic Beanstalk to host server node.js app
AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services. You can simply upload your code and Elastic Beanstalk automatically handles the deployment, from capacity provisioning, load balancing, auto-scaling to application health monitoring.<br/> AWS Elastic Beanstalk – Deploy Web Applications
I’ve chosen Elastic Beanstalk because of simplicity and ease of use. On top of this, it supports auto-scaling and pay-as-you-go.
Create Elastic Beanstalk application
Go to Elastic Beanstalk
Click “Create New Application“
Enter “Application name”
Click “Create”
Create environment
Click “Actions” and then “Create environment”
Enter “Environment name” and “Domain”
For “Platform” select node.js “Preconfigured platform”
For “Application code” select “Sample app” for now
Click “Create environment”
For now, there is only a sample AWS application deployed. We are going to deploy ours using CodePipeline in the next steps.
The node.js Elastic Beanstalk platform sets the PORT environment variable to the port which the proxy server passes traffic to. Read this variable in your code to configure your application’s port:
const port = process.env.PORT || 3000
app.listen(port, () => { console.log(`Example app listening on port ${port}!`)})
Point domain to Elastic Beanstalk instance
Go to Route 53 into your hosted zone
Click “Create Record Set”
Inside “Name” enter subdomain for you server app (for example
api.yourdomain.com
)Inside “Type” select “CNAME”
Inside “Value” enter your Elastic Beanstalk URL (you can find it in the Elastic Beanstalk dashboard)
Setup Elastic Beanstalk to use SSL
In order to use an SSL certificate for your Elastic Beanstalk App, you’ll need to change the configuration of your app to use Load Balancersas opposed to a single instance. This can get costly, so make sure to check your billing dashboard to ensure you’re not going over your budget.
Go to Elastic Beanstalk
Click “Configuration”
Click “Modify” under “Capacity” tab
Change “Environment type” to “Load balancer”
Click “Apply” (This might take some time, so you might need to wait before proceeding to next steps)
Click “Modify” under “Load balancer” tab
Uncheck “Enabled” toggle in default setting
Click “Add listener”
Inside “Listener port” enter 443
Inside “Listener protocol“ select HTTPS
Inside “Instance port” enter 80
Inside “Instance protocol“ select HTTP
In “SSL certificate” select your certificate created in previous steps
Click “Add” button (modal should be closed after this step)
Click “Apply” button (if you don’t apply your settings, all will be dismissed)
Create S3 bucket and CloudFront distribution to host client
S3 is designed to store and retrieve any number of files or objects from anywhere on the internet. It’s simple to use and offers durable, highly available and scalable data storage at a low cost. CloudFront is a content delivery network (CDN) service that delivers static and dynamic web content, video streams and APIs around the world, securely and at a scale. By design, delivering data from CloudFront can be more cost-effective than delivering it from S3 directly to your users. CloudFront serves content through a worldwide network of data centers called Edge Locations. Using edge servers to cache and serve content improves performance by providing content closer to where viewers are located. We are going to create S3 bucket which will be private, then create CloudFront distribution which will have permissions to read from private S3 bucket therefore users could access files only through the CloudFront. Serving content throughout the CloudFront rather than S3 is faster, more secure and cheaper.
Create S3 bucket
Go to S3
Click “Create bucket“
Inside “Bucket name” enter the name for you bucket, I would recommend to name it according to the domain you want to serve it at.
Select region, but it doesn't matter which one cause we'll use CloudFront for the webpage delivery. I would try to keep it in the same region as other services just for the convenience
Click “Create”
Create CDN distribution at CloudFront
Navigate to CloudFront
Click “Create Distribution”
Click “Get Started” under the Web distribution
Inside the “Origin Domain Name” select S3 bucket you’ve created in previous step
Inside “Restrict Bucket Access” select “Yes”
Inside ”Origin Access Identity” select “Create a New Identity”
Inside “Grant Read Permissions on Bucket” select “Yes, Update Bucket Policy”. This step adds Bucket Policy to our S3 bucket to allow CloudFront read the files
Change “Viewer Protocol Policy” to “Redirect HTTP to HTTPS”
Under “Distribution Settings” inside “Alternate Domain Names (CNAMES)” input enter your domain name (
yoursubdomain.yourdomain.com
)Select “Custom SSL Certificate” in the “SSL certificate” section
Find and select your SSL certificate you’ve created in previous steps
Inside “Default Root Object” enter root file for your client app (
index.html
)Click “Create Distribution”
Turn off caching of client root file index.html
Navigate to CloudFront
Click on distribution you’ve created in previous step
Click “Behaviors” tab
Click “Create Behaviour”
Inside “Path Pattern” enters your client root file name (
index.html
)For “Object Caching” select “Customize”
Set “Maximum TTL” and “Default TTL” to
0
Click “Yes, edit”
Point domain to CloudFront URL
Go to your Hosted zone in Route 53
Click “Create record set”
Inside “Name” enter your subdomain or leave it blank if you are setting root domain
For “Type” select “CNAME”
Inside “Value” enter your CloudFront URL. You can find at CloudFront dashboard in “Domain Name” column
Click “Create”
Test it
Go to your S3 bucket
Upload your website (it has to have an “index.html”)
Go to your website URL (if you get 403, you have to wait until CloudFront becomes configured - it might take a few minutes)
Setup CodeBuild and CodePipeline to automate build and deployment
AWS CodePipeline is a continuous delivery service you can use to model, visualize, and automate the steps required to release your software. You can quickly model and configure the different stages of a software release process. CodePipeline automates the steps required to release your software changes continuously.<br/> What Is AWS CodePipeline?
Create CodePipeline
Go to CodePipeline
Click “Create pipeline”
In the first step “Choose pipeline settings” enter “Pipeline name”. In “Service role” choose “New service role” and in “Default location” choose “Artifact store”
In the second step “Add source stage” select Github source
Click on “Connect to Github” and follow the instructions
Back in Create pipeline wizard choose your repository and branch
In the third step “Add build stage” in “Build provider” select AWS CodeBuild
Click “Create new project”, choose your build environment and add environment variables if needed
In the fourth step ”Add deploy stage” choose your S3 bucket from below and check “Extract file before deploy”
Click “Next”, re-check summary and click “Create pipeline”
Setup output artifacts to supply deploy step
Go to pipeline detail created in previous steps
Click “Edit” button
Click “Edit stage” and then edit icon under the “Build” step
Add client and server into the “Output artifacts” inputs:
Click “Save”
Click “Edit stage” and then edit icon under the “Deploy” step
Select client in “Input artifacts”
Click “Save”
Click “Add action” under the “Deploy” step
Select AWS Elastic Beanstalk in “Action provider”
Select server in “Input artifacts”
In “Application name” select your app. Same goes for the “Environment name”, where you select your environment
Click “Save” and then “Save” again under pipeline detail
Add build config file
Add buildspec.yml
to the root folder of your project
version: 0.2
phases: pre_build: commands: - cd client && npm install && cd .. - cd server && npm install && cd .. build: commands: - cd client && npm run build && cd .. post_build: commands: - mv ./client/build ./buildartifacts: secondary-artifacts: client: files: - '**/*' base-directory: build server: files: - '**/*' base-directory: server
buildspec.yml
file has two sections, phases
and artifacts
. In phases
, you can see the commands for installing the dependencies, building and moving the build output to the root folder. From my own experience, it’s impossible to set an artifact’s base-directory
to a nested folder and that’s why we move it to the root folder. It’s important to notice that the keys in secondary-artifacts
have to match “Output artifacts” in the “Build” stage of CodePipeline.
Summary
Now you are ready to build something great and you don’t have to worry about deploying your code by hand. Every commit to your target branch will run the CodePipeline which builds and deploys your client and server code. Another advantage of this setup is that all the technologies used here are managed, support auto-scaling and have pay as you go model which could be a good foundation for almost every web app.
Resources:
You might
also like