Automating the build and deployment of our team site with Jekyll, GitHub, Travis, S3 and CloudFront
For the last seven years this site has been hosted on GitHub Pages, which is based on Jekyll and used a custom domain. This has been a very fast way to host our site without having to worry about a complex CMS.
Why move it?
The site needed to be fully SSL so we started to look at options. GitHub Pages can run fully SSL under the .io domain, but we wanted to retain our custom domain.
Where did it move to?
We wanted to make sure we could continue to allow all staff to write new posts easily, along with using the latest tools to help us meet our goals. We chose to look at an option that allowed us to have automated builds and publishing, along with redundant storage and CDN-backed delivery.
We wanted builds to be made for all branches to ensure Jekyll completed, but only deployed if the branch was master.
We selected GitHub to host the Jekyll files, Travis CI to push the built site to S3 and finally CloudFront on top with an SSL certificate. Choosing Travis CI to do the build and deploy was something that’s familiar to our team already and utilising Amazon Web Services (AWS) gives us great flexibility. Following great recent successes of moving ~4TB of assets to S3 from on-disk storage for the White Label Dating application and using Route 53 to enhance our DNS resilience, AWS was the perfect choice.
How we got there
All our master branches in GitHub are protected to ensure proper and full code reviews are performed and any required automation occurs.
At a high level, the new development and deployment process looks like this:
- Developer pulls master repo and creates a branch
- Developer makes changes to the site or writes a new post
- Developer tests locally using
bundle exec jekyll serve
- Developer pushes branch to GitHub which triggers a Travis CI build (but no deploy)
- Assuming the Travis CI build passes the pull request can be reviewed and then merged
- Once merged to master, another build is done. Travis CI checks the branch name and, as it’s master, also pushes the content to S3 along with creating an invalidation at CloudFront using s3_website.
Notes on deployment choices
- You could use Travis’
deploy
option for S3. This is great for shipping the content but you’d then have to installpip
and awscli so you could manually call the invalidation. - You could run the build as normal, then, simply use the script deploy
to execute the
s3_website push
but thanks to how Travis CI goes back to rvm 1.9.3 the ruby version and thus the build and bundle is lost. There is a section on TRAVIS_RUBY_VERSION in the document which may get around this, but, I didn’t test it. - You could use your own deploy scripts. This is simple, obvious, but requires dependencies to be included as part of the Travis CI setup and we wanted to try and keep that as minimal as possible.
The detail
With the following steps, I’m going to assume you have command line experience and some knowledge of AWS services and how it works, but, will include guides elsewhere or commands you can use to carry out the tasks. With nearly all AWS services both command line and web GUI’s can be used. You can find out more about the command line tool on Amazon’s site and there are plenty of guides online.
Jekyll
Firstly you’ll need a Jekyll site.
S3
You’ll need an S3 bucket, with a static website set up and a policy. You can use s3_website to create that, or you can do it manually. The following is a quick step-by-step guide to do the very basic steps to get the bucket live.
Now you have the bucket to store the files, you can enable the built-in feature to host static websites
and configure the default index
document.
Permissions on S3 buckets are something to which you need to pay close attention. In this case we’re not going to be storing any confidential data so we can open up the permissions to allow general public read access.
At this point you can access your site at http://my-example-jekyll-site.s3-webite.eu-west-2.amazonaws.com, although there will be no content. You could choose to upload some content, but as we’re going to be using s3_website for this we’ll carry on.
SSL Certificate
For our SSL certificate we used a free AWS certificate issued via Certificate Manager. You’ll need this set up prior to going to CloudFront or you’ll need whatever certificate/key you are going to be using for your site. I’m not going to cover using importing your own certificate in this post.
If you want to get a free certificate from Amazon, there’s a blog post you can read to find out more. Note that the Certificate Manager is only available in the us-east-1 region.
Make a note of the ARN as you’ll
need it later when you setup the CloudFront distribution. If you don’t, you can always get it back by listing
your certificates using aws acm list-certificates --region us-east-1
(again note you’ll need to be in us-east-1).
If you do the certificate request via the web GUI you’ll get an email automatically for you to approve and have the certificate issued. Via the command line you may need to trigger that approval yourself.
When the certificate is approved it’ll be available in Certificate Manager and you’ll be able to use it in CloudFront.
CloudFront Distribution
When you publish your CloudFront setup it’ll take a while to distribute. The same goes for any changes you make down the line. Typically, from my experience, this is between 45-60 minutes.
This configuration is the most complicated looking part of this process. It’s arguably quite a bit more simple using the web GUI so I’ll use a couple of screenshots to illustrate what we’re doing on the command line.
I’ll briefly summarise what this configuration is doing, but, it’s much easier to see this via the web GUI.
Summary
- The
Origins
section tells the config where to get its config. This can be via S3 or a custom URL. - The
DefaultCacheBehavior
sets up both caching options, but also tells the distribution how to behave with regards to HTTP to HTTPS redirection. - The
ViewerCertificate
section tells it to use the certificate you created earlier. - The
Aliases
section specifies which CNAMEs you’ll be setting in DNS: in our case our custom domain name that we want to retain.
Screenshots of settings
Creating the Distribution
Put the content of the configuration into a file.
Use that file to create the distribution. Once it completes you’ll get a config back. Note the DomainName as that’s what you’ll need to actually point to your new CloudFront distribution.
DNS Updates
Now I’m covering this here but you won’t want to do this until you’ve actually got your Jekyll site built and published via Travis CI otherwise you’ll point at a blank site. In our case we set this up in parallel so the first step we did was to drop the TTL our of DNS down to 30s so we could move things around easily.
Your DNS should be set as a CNAME to point at the DomainName item after you created the CloudFront config.
Manual invalidation
We’ll be using s3_website to handle content invalidation so when the site is published the whole cache is cleared, however you can also do that manually at any time.
IAM User
As we’ve mentioned we’ll be using s3_website to publish our site so we need a user that can be used for access. We set up a specific build user in IAM with command line access and a policy to allow it to do everything it needed to and to keep it isolated from all our other IAM policies and users.
How you set up access to your S3 bucket and CloudFront is up to you. You can use existing IAM users and policies or if you need a new one (or a basic one) you can follow the steps below. IAM is a large and important topic which I recommend you take some time to understand.
To start our basic access, we’ll need a user.
Give this user an access key so they can use the command line tools.
Next you need to create a policy which will allow the user to carry out the tasks we need. For the purpose of this guide that’s manipulating content and performing a purge against CloudFront.
Lastly you’ll need to attach your new policy to your user. You’ll need to use the ARN from the command you just ran.
s3_website
When I mention environment variables in the next sections these refer to the newly create ones you got from this post or credentials you already have.
Within Travis CI we’ve set up environment variables to take care of the AWS parts we’ll need. Here’s
our s3_website.yml
file showing how those are referenced.
Before we push this configuration to Travis CI and have it try to build it, you can (and should) test that the credentials you’ve got actually work. You should get output along the lines of this:
Travis-CI Configuration
Our .travis.yml
file ended up looking like this. We also post all our build notifications to a Slack channel.
Other reading
There’s a few posts and bits of documentation I found useful during this process which are included here for reference:
- Travis CI - Customizing the build
- Travis CI - Script Deployment
- Post build deploy error 127
Conclusion
Now the whole process is live, working well and our team site is secure. It’s being built with Travis CI, pushed to S3 and served securely using CloudFront and a certificate from Certificate Manager.
Orginally published at dev.venntro.com