In this post, we are deploying our static websites (e.g js, css, html) to a S3 Bucket to allow for higher performance and global distribution with lower costs.
Motivations
S3 is an awesome object storage, but is also a great server for static websites.
Alongside Cloudfront as our Content Distribution Network with low maintenance, and with SSL out of the box with AWS ACM.
Easy to deploy new assets, as easy as folder copy.
Cheaper than an instance, or even a droplet as I used to host this blog, atleast for low traffic websites.
S3 and Cloudfront have great performance.
Requirements
AWS account
Terraform (v0.12+)
aws-cli
Docker (Optional: to generate our static sites without polluting our workspace)
Domain (Optional: to use our custom domain instead of using an aws provided one with some random chars, this can be from any registrar, even route53 itself)
Manually pointing our Domain to our Route53
First you must create your Route53 hosted route first, in order to use your custom domain, if you have none.
So navigate to : https://console.aws.amazon.com/route53/home?#hosted-zones:
And create a new hosted zone:
After creating your hosted zone, you must update the nameservers(ns) that were created for this zone in your current registrar.
Creating your Cloudfront and S3 Buckets
Now that we’ve created our hosted zone for our domain and instead of reinventing the wheel, we will be using an update version of David Newman “terrablog module” modified to be terraform 0.12 compatible.
The module we will be using is: Static Website Terraform
So let’s create our terraform file. We will just create a main.tf for simplicity as we will reuse the module.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
provider "aws" {
profile = "default"
region = "us-east-1"
}
#Optional - If you wish to keep track of your current state instead of a local machine
terraform {
backend "s3" {
region = "us-east-1"
bucket = "my-state-bucket"
key = "terraform.tfstate"
dynamodb_table = "my-state-lock"
encrypt = true
}
}
module "static_website" {
source = "git@github.com:Ilhicas/static-website-terraform.git"
site_bucket_name = "name-for-our-s3-bucket"
domain_name = "my-domain.com"
cdn_price_class="PriceClass_100"
}
So, besides the provider, and the optional backend to keep our state, let’s understand the variables we are passing. Both site_bucket_name
and domain_name
are pretty self-explanatory, the first is the bucket name that will hold our assets, and the second one is the domain we will use with both our certificate and the cloudfront distribution.
The cdn_price_class
refers to the type of our distribution that we want for our cloudfront. Different pice classes, have global, or regional distribution, with global price classes having higher prices for traffic incoming to our website.
In order to know more, and to find out which price class best suits you, visit aws amazon cloudfront pricing and details page here
The possible values are PriceClass_All
, PriceClass_200
, PriceClass_100
, and you may check more info on the cloudfront_distribution terraform resource here.
Ok, so when you are comfortable with it, lets apply our terraform.
1
2
3
4
5
6
#We must first initialize terraform in our current folder
terraform init
#first lets plan to check everything is ok
terraform plan
#Now lets appply it
terraform apply
This should take a long time, as the cloudfront distribution takes around 15 minutes to apply, and it will take even more to validate the created TLS certificate if you just changed your domain nameservers to point to the newly created hosted zone.
Creating your static assets (Docker optional)
By now our infrastructure should be all set.
Since its out of scope a web application is not provided. For instance, you may use the following command to generate a sample app for react.
1
npx create-react-app my-app
Or you may use just a simple index.html and skip npm alltogether, its your choice.
Let’s then use a docker container to generate our assets for a sample vue application.
Let’s use the following dockerfile:
1
2
3
4
5
6
7
8
FROM node:10-alpine AS builder
RUN apk --update add git
WORKDIR /home/node/app
COPY . .
RUN npm install
RUN npm ci
ENV NODE_ENV=production
RUN npm run build
So now let’s build our image using docker
1
docker build -t app .
And lets run it to extract our generated assets. It doesn’t matter what you run, you are only interested in extracting the assets.
1
docker run --name assets app ls
And to copy the generated files
1
2
3
docker cp assets:/home/node/app/dist site
#And lets clean the containers
docker rm assets
If you wish, you may only run the npm ci, and npm run build commands directly if you have node and npm installed locally without resorting to a Docker image.
Ok, so now you may see that under site, its your generated assets, including the index.html
All we are missing is the deployment.
Deploying you static assets to S3 bucket
So, your deployment is as simple as using the aws cli to cp all your files up to S3 Bucket
1
aws s3 cp site/ s3://my-bucket-name/ --recursive --profile default
And that’s it, you may now visit your domain and you should have your cloudfront serving your assets from s3.
Thanks for reading, hope you find it helpful somehow, and if it helped you please share.