This is the first time for me to use jekyll to host my blog. Maybe ‘host’ is not an appropriate phrase here since jekyll is only a tool to generate static HTML pages. However since I’ve never used an blogging tool like jekyll. Everything is pretty new to me. Ruby is new to me. nginx is a bit new to me(though I do have limited experience with it). So as this is my first blog post using jekyll. I’m gonna to log who I went through to deploy a blog hosting site on my VPS.

Some basic info

I understand that the easiest way to get a jekyll blog would be host it on github pages. One drawback of hosting your blog on github is the ***.github.io is blocked in China(at least for me). Also I would perfer a way that I’m able to take down the website within 1 second in case something went wrong. So I decide not to go this way. Another issue is it seems not possible to get HTTPS working on ***.github.io site. I saw someone get it working use Cloudflare. However I would perfer a Let’s Encrypt cert instead of Cloudflare’s cert. So in the end I decide that I will still use Jekyll, but I’m gonna to host it on my own VPS.

Auto deployment

After reading some blog post and referring to Jekyll official document. I decide to make the workflow of posting a blog post like this:

  1. I have a github repo (preferably private) on my local machine. The github repo is created by calling ‘jekyll new’ and I have all my blog post in this repo.
  2. After writing a new post, I can use jekyll to build the site and serve it locally. Then I can test if the images and post are working properly.
  3. The I’ll commit my changes to github repo. Then after a few minutes my blog would somehow magically appears on the VPS and nginx will be serving the site.

How I proceed from step 2 to step 3 is a little bit tricky. Initially I decide to use git post-receive hook on my VPS and it will pull the repo once it receives the new commit. Then I realized that this is not gonna to work since the post-receive hook only works on server side the server in my case is github’s git server, not my VPS. For sure github is not gonna to let you run script on their server. So post-receive hook is a no go here. You can, indeed, use github’s webhook service that notify third party after the commit. However it might be too complicated and I decide to not use the webhook. Maybe in the future I’ll look into this method.

After reading some online post. I decide to look into Travis CI. In my previous internship, my company use Jenkins. Jenkins and Travis are more or less the same thing. But basically with Travis what I can do with this blog site is after I push my commit(new blog pages) to github. Travis is triggered and it will pull and build my blog using jekyll. Then it will transfer the static site to my VPS so my nginx on VPS is able to serve it.

Nginx docker to server static blog site

I created a bash script to use acme.sh to generate TLS certs and spawn a docker nginx container to server file under /var/www/myblog.build folder

#!/bin/bash

acme.sh --issue --d xhuang.top --standalone
acme.sh --installcert -d xhuang.top \
	--fullchain-file "$PWD/certs/xhuang.top.cer" \
	--key-file "$PWD/certs/xhuang.top.key" \
	--reloadcmd "docker container restart myblog_nginx"
docker container run \
	-d \
	-p 80:80 \
	-p 443:443 \
	-v "/var/www/myblog.build":/usr/share/nginx/html \
	-v "$PWD/conf.d":/etc/nginx/conf.d \
	-v "$PWD/certs":/etc/nginx/certs \
	--name myblog_nginx \
	--restart=unless-stopped
	nginx

Config Travis CI

Since I do have a github student account. I have unlimited private repo as well as travis.com access(Travis.org lets you CI public repo for free where as Travis.com is for private project). Travis CI integrates well with github. For those of you who are not familiar with CI. How CI services like travis work is after you push your code to server. Travis is triggered and it will create a temporary GCE VM instance to build your code on that VM. Then according to your config it will perform test on your built project. Then it will return the test result to you. How you want to test the code is purely designed by your self. As the _config.yml in your project defines Travis’s behavior after building your code.

So basically all this means is I can use Travis to build my blog site and run some scripts. Then deployment is fairly easy. Because I can write a script to push the static files to the my VPS. So it’s just a matter of how to config the credentials so Jenkis will have access to push files to my VPS. It could be as easy as using rsync or something like that. The way I decide to use is push static files to a seperate github repo and SSH to my VPS and make it pull the files.

SSH on travis

Since in my _config.yml file I need have script to SSH into my VPS. I need to set up proper credentials in order to make this work. So on my local machine:

ssh-keygen -N "" -f sshkey

Command above will generate a public public private key pair with empty passpharse. Then on the remote VPS I add public key to authorized keys:

cat sshkey.pub | ssh root@VPS.IP 'cat >> ~/.ssh/authorized_keys'

After that I’m able to use this key pair to ssh into my VPS. However there are some tunables required in order to let Travis use this key pair.

Add SSH key on Travis

I need to add private key into my git repo in order for Travis to use it. However it is not wise to put private key in a git repo even the repo itself is private. So we need to use Travis’ file encryption tool to encrypt the private key. Under the git repo, do following to encrypt a file:

Encrypting Files

gem install travis
travis login --pro # Enter your travis credentials here
travis encrypt-file sshkey --add
git add *
git commit -a 

--add is used to add proper environmental variable and command to decrypt the file in _config.yml.

Config Travis to build blog

This part is heavily borrowed from mritd.me’s blog post. His post gives a detailed guide on how to set up Travis to build Jekyll site. The link to the blog is at the bottom of this post.

language: ruby
rvm:
- 2.4.1

before_install:
- openssl aes-256-cbc -K $encrypted_e7b6eeb82324_key -iv $encrypted_e7b6eeb82324_iv
  -in aliyun.ppk.enc -out aliyun.ppk -d
- chmod 600 ./aliyun.ppk
- echo -e "Host xhuang.top\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config

before_script:
- chmod +x ./script/cibuild
script: "./script/cibuild"

after_success:
- git clone https://$DEPLOY_TOKEN@github.com/clive2000/myblog.build.git
- cd myblog.build && rm -rf * && cp -r ../_site/* .
- git config user.name "xhuang travis autobuild"
- git config user.email "autobuild@xhuang.top"
- rm aliyun.*
- git add --all .
- git commit -m "TravisCI auto build"
- git push --force https://$DEPLOY_TOKEN@github.com/clive2000/myblog.build.git master
- cd .. && ssh -i aliyun.ppk USERNAME@HOSTNAME  "echo RUNDEPLOYSCRIPTHERE"

env:
  global:
  - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
addons:
  apt:
    packages:
    - libcurl4-openssl-dev
  ssh_known_hosts: xhuang.top   
sudo: false
cache: bundler

Deploy script

On my VPS, I wrote a tiny deploy script to shutdown nginx container, pull from myblog.build.git repo, copy it to /var/www and then restart nginx container. After that nginx will host up-to-date static site on my blog.

The deoply script looks like this:

#!/bin/bash
docker container stop myblog_nginx
cd /var/www
rm -rf myblog.build
git clone https://[GITDEPLOYMENTKEY]@github.com/clive2000/myblog.build.git
docker container start myblog_nginx

Reference

https://mritd.me/2017/02/25/jekyll-blog-+-travis-ci-auto-deploy/