Update 29/01/2019 Since AWS Amplify is out, please find newer version [here]({{ site.baseurl }}/cloud/Migration-to-Amplify/)

Update 09/02/2018 I’ve updated this article to remove the EC2 used for draft check (for CostSaving), I’m now using S3 bucket with a custom _config.yml.

Requirement: Before reading this article, please find those how-to to publish simple static Jekyll website to S3 using ACM SSL and CloudFront CDN. here, here, or here

I know, you will told me, why are you using such workload to publish simple blog, It sounds crazy!?. Why not using Medium instead ? The answer is to get my own custom blog platform, and play with AWS tools and Services.

In this article, I will explain how I manage my Continuous Build / Continuous Deployment (CB/CD) pipeline from my IDE to this blog using Cloud9, GitHub, CodeBuild, S3, CloudFront and Lambda function.

The main idea is to build and push your Jekyll website to your AWS S3 bucket and then force your CDN (CloudFront) to refresh origin.

In my scenario, I’ve activated the GitHub hook, to be able to see my drafts from another S3 bucket before pushing to production.

alt

Steps Summary

  1. I’m writing articles on Cloud9 IDE
  2. Code is committed to public Github repository
  3. AWS CodeBuild is generating the website (Build) and push the result to S3 (Deploy)
  4. AWS Lambda function is triggered with AWS S3 events to invalidate AWS CloudFront items (cache refresh)

Cloud9 (IDE)

I’m using Cloud9 to get my configured workspace from everywhere (home, work, travel). If you need offline writing, just git pull locally. (super tips)

Create your own private C9 workspace on Cloud9. It’s free for one private workspace.

GitHub (Source Code Repository)

I assume your Jekyll website is already created, same for your GitHub repo.

git commit & git push your Jekyll website to your GitHub repository

Back to your Cloud9 workspace and git clone REPO_URL

Now you can write your Jekyll articles from Cloud9 easily. When you’re done, just commit and push your work to GitHub.

CodeBuild (Build & Deploy)

Create your CodeBuild project

screen-project01

screen-project02

  • Project name: your_blog_name
  • Source provider: github
  • Repository URL: your_git_repo_url
  • Webhook: check this if you need to automatically build on any code change on GitHub
  • Environment image: Use an image managed by AWS CodeBuild
  • Operating System: Ubuntu (currently the only available option)
  • Runtime: Ruby
  • Version: ruby:2.3.1
  • Build specification: Use the buildspec.yml in the source code root directory
  • Artifact type: No artifact
  • Service role: Create a service role in your account

Next, you’ll have to create at the root of your GitHub repository a buildspec.yml file:

version: 0.1

phases:
  install:
    commands:
      - gem install jekyll bundler
  pre_build:
    commands:
      - bundle install
  build:
    commands:
      - echo "******** Building Jekyll site ********"
      - bundle exec jekyll build
      - echo "******** Uploading to S3 ********"
      - aws s3 sync _site/ s3://your_bucket_here

Please take a look at my Gemfile too, it could be useful:

source "https://rubygems.org"
gem "minimal-mistakes-jekyll"
gem "jekyll-twitter-plugin"
gem "jekyll-archives"

Modify your generated role

{
    "Effect": "Allow",
    "Resource": [
        "arn:aws:s3:::your_bucket",
        "arn:aws:s3:::your_bucket/*"
    ],
    "Action": [
        "s3:PutObject",
        "s3:Get*",
        "s3:List*"
    ]
}

Lambda & CloudFront (CDN)

You will find below the python script from @yagonobre I’m using. For each new file uploaded to S3, this Python Lambda function trigger the invalidation of CloudFront Objects.

{% gist yagonobre/2666c13cd0f339eb00700226abc95195 %}

here the role used: (change with your Distribution ID)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudfront:CreateInvalidation"
            ],
            "Resource": [
                "arn:aws:cloudfront::567589703415:distribution/XXX9UAIDF1XXX"
            ]
        }
    ]
}

Here we go, now you have a full deployment pipeline from your preferred IDE to S3 using AWS serverless functions or managed services.

That’s all folks!

zoph.