9. CICD Pipeline - GitHub to S3

GitHub logo and AWS CodePipeline Logos

How to use AWS infrastructure to create a git push CI/CD pipeline for a Nuxt 3 Static website built and deployed to a AWS S3 bucket with a fully qualified custom domain name

Why Use a CICD pipeline?

A Continuous Integration and Continuous Delivery (CICD) system creates stress-free and consistent deployments. The pros and cons of using CICD vs. manual are:

Why Use a CICD Pipeline?

The main reasons to use a CICD pipeline are:

  1. Automation
    • once set up, the pipeline will automatically build and deploy your site with no manual intervention. This is especially useful for frequent updates or multiple contributors.
  2. Consistency
    • the pipeline ensures that the build and deployment process is consistent and repeatable, reducing the risk of human error.
    • The Build Machine is fresh, dedicated to building and always on-demand. No chance of update, installation issues, or viruses.
    • Your source code is pulled fresh from GitHub. No chance of uploading code that hasn't been checked-in.
    • Your NPM dependencies are always installed anew on each build.
  3. Speed
    • the pipeline can significantly speed up the build and deployment process, allowing for faster updates and releases.
    • Downtime for your web-site is minimal
    • You can roll-back to previous versions
  4. Version Control Integration
    • the pipeline can be integrated with version control systems like GitHub, allowing for automatic builds and deployments triggered by code changes.
  5. Testing and Validation
    • the pipeline can include automated testing and validation steps, ensuring that code changes do not introduce bugs or issues.
    • the CICD can put insert 'approval' checkpoints or stages
  6. Solo Scalability
    • the pipeline can easily scale to handle multiple projects deployments. As a solo developer as I scale by creating more than one site, I can use the same pipeline with minor configuration changes.

CICD Cons

  1. It costs money. This can be mitigated by using small build machines and unnecessary build times (the main driver of money is how many minutes of build machine time is used.)
  2. CICD errors require 'operational' skill set to locate and fix. On the other hand, you can have similar problems and skill set issues with the manual method as well, so it is usually easier with a CICD than finding out what manual steps you took.

Prod Infrastructure

Prod (Production) Infrastructure consists of cloud resources that host your web site for public viewing. For JAMStart, the prod infrastructure recommendations are:

  1. Custom Domain
  2. HTTPS Site Certificate
  3. AWS Route53 Hosted zone, subdomain redirect
  4. AWS CloudFront Distribution with WAF (web application firewall)
  5. AWS S3 Bucket
  6. CodePipeline, CodeBuild, CodeDeploy

Here are my monthly costs for hosting my blog with this infrastructure:

ServiceMonthly CostNotes
Custom Domain$1.25$15/year registration renewal amortized
AWS Certificate Manager$0.00Free for public certificates
AWS Route53$0.551 hosted zone, 0.40 per 1,000,000 queries for the first 1 Billion queries
AWS CloudFront with WAF$8.501TB data transfer out, 10 million requests
AWS S3$0.05Requests per 10,000, first 50 TB / month of storage used, varies on demand
AWS CodePipeline/Build/Deploy$0.00Free tier 100 build minutes/month, 1GB storage
Total$10.35Approximate monthly cost

Custom Domain

You need a registered custom domain first. Yes, really. Create, transfer, and/or attach custom domain nameservers in AWS Route 53. Web registration at Amazon is $12-$15/year depending on the domain name. You can also transfer your domain from another registrar to AWS Route 53 for $12-$15/year. If you have a domain registered with another registrar, you can change the nameservers to point to AWS Route 53 and manage the DNS records in Route 53.

Simplicity is best, so I recommend registering or transferring your domain to AWS Route 53.

Site Certificate

To validate your site so that your site displays without warning or sketchiness, create a site certificate.

Request Certificate

To request a new certificate 🎫 use the AWS certificate manager.

  • AWS Certificate Manager → Certificates → Request

Certificate properties

FieldState
Certificate type'Request a public certificate'
Domain names : Fully qualified domain names{custom domain name}
Optional Recommend - Domain names : Add another name to this certificatewww.{custom domain name}
Validation methodDNS validation

CNAME cert records

In order to DNS validate your certificate you need to add one or two CNAME records to your hosted zone. Conveniently from the certificate request details you can automatically create the records. Choose the 'create CNAME records' button.

🛑 Important - DNS validation requires these CNAME records to be created.

AWS Route53 hosted zone

To use your S3 static site bucket with a custom domain name, you need an AWS Route53 hosted zone. If you acquired your custom domain through Route53, the hosted zone will be automatically setup. If you transferred your custom domain names into Route53, you may need to create one.

Creating Public hosted zone

Creating a hosted zone is simple, double-check that it is 'Public hosted zone'.

Default hosted zone

A new hosted zone in Route53, should have two records in it.

RecordTypeValue
{custom domain name}NS{set of AWS name servers}
{custom domain name}SOA{single AWS name server}

You can create a hosted zone without certification or with certification (recommended)

In order to create a certificate for your site, you need a CloudFront distribution and a certificate issued for that distribution. You will also create a CNAME record that will point your domain name to your CloudFront Distribution.

Flow Chart with Route53 CloudFront and S3 static bucket

Chart showing how users interact with Route53, CloudFront, Certificate Manager and S3 for your site

Wait until it the certificate is issued.

Without Certification

Flow Chart with Route53 and S3 static bucket

Chart showing how users interact with Route53 and S3 for your site

No cert ANAME record

In your hosted zone choose 'Create record' for your root

FieldState
Record name(leave blank for root)
Record typeA - Routes traffic to AWS resource
Route traffic to endpointAlias to S3 website endpoint
Route traffic to region(choose your region)
Enter S3 endpoint(pick your endpoint, should be in dropdown list) (only if your S3 bucket name matches domain name)

Verify

Your hosted zone should now have at least three records in the record set.

  1. NS record
  2. SOA record
  3. ANAME record - Domain to S3

AWS CloudFront Distribution

In AWS CloudFront choose 'Create CloudFront Distribution' and change the following

FieldState
Origin : Origin domain{insert your S3 Bucket website endpoint (no 'http://')}
Default cache behavior : Viewer protocol policyRedirect HTTP to HTTPS
Settings: Alternate domain name (CNAME) - optional{custom domain name}
Optional - Settings: Alternate domain name (CNAME) - optionalwww.{custom domain name}
Settings: Custom SSL certificate - optional{pick your certificate from dropdown}
Settings: Price classUse only North America and Europe (Recommended)

Verify

Your hosted zone should now have at least four or maximum five records in the hosted zone record set.

  1. NS record
  2. SOA record
  3. CNAME record(s) for certificate - 1 or 2 depending on if you included 'www' or not
  4. ANAME record - Domain to CloudFront

Subdomain redirect

Optionally, if you've created your certificate and CloudFront distribution with 'www', you need to create an ANAME 'www' redirect. In your hosted zone choose 'Create record' for your root

FieldState
Record name'www'
Record typeA - Routes traffic to AWS resource
Route traffic to endpointAlias to another record in this hosted zone
Route traffic to Choose record(pick your ANAME domain without the 'www')

Verify

After your certificate is issued and the CloudFront distribution is deployed, you can validate that your custom domain opens your static site hosted from S3 after you deploy it.

AWS S3 Bucket

Create an AWS S3 bucket that will host your static site. You can use the AWS Console ('Create bucket' in the Console S3 General purpose bucket) or AWS CLI (aws s3 mb). The bucket must be in the same region as your CICD pipelines.

S3 Bucket Name

You must name the S3 Bucket exactly the same name as your custom Domain name, i.e. for 'pennockprojects.com' custom domain, create a bucket named pennockprojects.com. Otherwise, your endpoint will not be visible as an alias for the Route 53 record

Allow public access

S3 Bucket Permissions Block all public access should be set to Off

Bucket Policy

Add a bucket policy for object access, replace {{BucketName}} with your bucket name.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{{BucketName}}/*"
        }
    ]
}

S3 Bucket Properties

Set the following S3 bucket properties as appropriate.

PropertyState
Static website hostingEnable
Hosting typeHost a static website
Index documentindex.html
Error document - optional404.html (or whatever you have)

Production Pipeline

The Prod Pipeline will gather your source from git, build it, and deploy it to production infrastructure.

Source Build Deploy

The base Build and Deploy pipeline consists of three Stages

  1. Source
  2. Build
  3. Deploy

Source

The source stage will trigger on a 'git push' to the main branch (or whatever git branch you designate). It will copy all the code from GitHub to the AWS CodeBuild Machine. The package from GitHub to AWS Codebuild is called SourceArti and is stored in CodePipeline S3.

Build

A CodeBuild EC2 machine is spun up on-demand. It is a Linux:g1.small machine with Free Tier 100 build minutes a month. For my blog I typically use about 1 1/2 minutes - 2 minutes a build.

The SourceArti from Source is copied and uncompressed onto the file-system of the CodeBuild EC2 machine.

The CodeBuild machine then follows the phases in the buildspec.yml file in the root of the repo. It will do the npm install reading the repo package.json file and installing packages directly. It will run the npm run generate command to layout your Nuxt SSG site.

If successful, the built files from dist will be compressed and stored in the CodePipeline S3 bucket as BuildArti.

Deploy

The CodeDeploy phase will grab the BuildArti compressed files, decompress and copy the files to your live website S3 bucket.

New CodePipeline Steps

Important, you should have created a custom domain, prepared an S3 bucket and received a site certificate prior to creating the AWS CodePipeline.

  1. Requisition a S3 bucket configured for Static Site Hosting
  2. Requisition a CodePipeline - Git Sync, AWS CodeBuild, Deploy to S3 Bucket
  3. Requisition a Route 53 hosted zone
    • Non-certificate (http warning)
    • CloudFront with certificate (https)
  4. Requisition a CloudFront Distribution
  5. Requisition a https Certificate

Create an AWS CodePipeline that will perform three tasks.

  1. Attach and watch a GitHub repo branch for changes
  2. Initiate AWS Codebuild and generate your Nuxt 3 Static Site
  3. Copy the built Nuxt Static Site into your S3 site bucket

Create a CodePipeline

From AWS Console find CodePipeline and choose 'Create'

  • Create a unique name for the pipeline
  • Choose Queued
  • Leave the pipeline role

Next

Pipeline Git sync

Choose a stage source

FieldState
Source Provider'GitHub (version 2)'
ConnectionUse existing or choose 'Connect to GitHub'
Repository name(pick your repo)
Default branch(pick your branch)
Output artifact formatCodePipeline default
Trigger - Trigger typeNo filter (launches on push)

Next

Pipeline CodeBuild

Build - Optional

Build provider → AWS codeBuild

FieldState
Build Provider'AWS CodeBuild'
Region(pick your region)
Input artifactsSourceArtifact (from the GitHub sync above)
Project Name(pick your project name or choose 'Create Project')
Environment variables(leave blank or add)
Build typeSingle build

Create Build Projects

FieldState
Project name(pick a project name)
Source 1 - PrimarySource provider - AWS CodePipeline
Environment Provisioning modelOn-demand
Environment imageManaged image
Environment ComputeEC2
Operating systemAmazon Linux
RuntimeStandard
Imageaws/codebuild/amazonlinux2-x86_64-standard:5.0
Image VersionAlways use the latest image for this runtime version
Buildspec Build specificationsUse a Buildspec file
Buildspec name(leave blank or buildspec.yml)

Pipeline buildspec.yml

Add the following buildspec.yml file to your repo. These are the instructions that will be used to build the Nuxt static site as well as indicating where the built files will exist after build.

version: 0.2

phases:
  install:
    commands:
      - npm install
  build:
    commands:
      - npm run generate
artifacts:
  files:
    - '**/*'
  base-directory: 'dist'

AWS CodeDeploy

The third stage of the pipeline, deploy, copies the built files to the S3 website bucket

FieldState
Deploy provider'Amazon S3'
Region(pick your Region, same as build)
Input artifacts'BuildArtifact'
Bucket(pick your bucket)
Extract file before deployenabled

Verify

If all your steps are correct, your code pipeline should run. You should validate by opening the website at the S3 bucket website endpoint.

S3 → {static site bucket} → properties → Static website hosting | Bucket website endpoint

Dev Infrastructure (optional)

The Dev (Development) Infrastructure consists of cloud resources that host your development web site - for testing and validation before deployment. A dev infrastructure and pipeline are optional and may or may not be implemented for your site needs. A content heavy site with multiple proofreaders, editors, or authors, might want a dev infrastructure to view the web site before deployment to production. A solo author might be fine will validating the site locally and skip the dev infrastructure.

Recommended Resources

  1. AWS Route53 Hosted zone
  2. AWS S3 Bucket

Dev Pipeline (optional)

A Developer Pipeline is used to host your site for testing before deploying to prod

Resources

The following videos illustrate many of the steps used here: