In my first post mentioned that i have ideas for improvements of my blog infrastructure. I figured it’s time to introduce them. This will be a write-up of how i automated the process of updating this blog by hosting it as an image in docker hub and using Jenkins CI/CD tool.

Setting up jenkins container and nodes

First things first, we need jenkins instance running somewhere. I chose to run it on my server in a docker container. Heres a docker-compose config i use to run the jenkins container:

---
version: '3.8'
services:
  jenkins:
    image: jenkins/jenkins:lts-jdk11
    environment:
      - DOCKER_HOST=tcp://192.168.0.135:2375
    ports: 
      - 8079:8080
      - 50000:50000
    restart: on-failure
    volumes:
      - /usr/bin/docker:/usr/bin/docker
      - ./jenkins_home:/var/jenkins_home

We will also need Jenkins agent to run somewhere when executing the pipeline, therefore we also need to expose the docker API over TCP. This can be achieved by editing /lib/systemd/system/docker.service we can expose the API over TCP by editing the ExecStart parameter like this:

ExecStart=/usr/sbin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock $DOCKER_OPTS -H unix:///var/run/docker.sock

Similar thing can be achieved by exposing the docker socket, however it’s a huge security risk.

Now we need to point the docker host URI to the TCP socker in the jenkins configuration. This can be done via jenkins web ui by going to Dashboard > Manage Jenkins > Manage Nodes and Clouds > Configure Clouds, than adding a new cloud and configuring it like this: jenkins cloud configuration

I also figured i want the jenkins to be able to run shell scripts to for automated deployments, for this i added a new node which would launch it’s agent over ssh. So i went to Dashboard > Manage Jenkins > Manage Nodes and Clouds > New Node, configured it’s launch method to ‘Launch agents via SSH’ added ssh key and changed availability to ‘Bring this agent online when in demand, and take offline when idle’ and set in demand delay and idle delay to ‘1’. This is to ensure that jenkins agent is sshing into my server only when the pipeline is running.

Pipeline script

Before creating a pipeline i created a repository in docker hub to store my container image.

Now it’s time for the actual pipeline that will update the blog automatically. I created a new blog-updater pipeline, set the Build Triggers as ‘Poll SCM’ and set the schedules to * * * * *. This means that jenkins will run the pipeline if it detects any changes on the git repository and the check will run once every minute.

Next i wrote a pipeline script:

node {
    stage('Clone repository') {
        git credentialsId: '3cc6bc1e-84b1-4e66-b3d2-6bdcdcccdf1a', url: 'http://192.168.0.210:3000/xxx/blog.git'
    }
    
    stage('Build image') {
        dockerImage = docker.build("imthesloth/theslothblog:latest")
    }
    
  stage('Push image') {
        withDockerRegistry([ credentialsId: "docker-imthesloth", url: "" ]) {
            dockerImage.push()
        }
    }
}

node('blog-deployer') {
    stage('Deploy new docker image') {
        sh '/home/xxx/docker/blog/deploy.sh'
    }
}

As you can see this is a simple pipeline which does the following:

  1. Clones this blogs git repository;
  2. Builds a docker image;
  3. Pushes the image to docker hub;
  4. Runs deploy.sh script on my server;

The deploy.sh is used for restarting the container so that the website gets updated with the latest changes, it’s a really simple shell script:

#!/bin/sh
docker-compose -f /home/xxx/docker/blog/docker-compose.yml down &&
docker-compose -f /home/xxx/docker/blog/docker-compose.yml up -d

Limitations

I played with the idea of having 0 downtime deployments, however it seemed too much of a hassle, given the blog currently has virtually zero readers and the container restarts in less than a second. I might try to implement it in the future just for the fun of it though.

Summary

Although very simple the pipeline makes my life a lot easier, whenever i want to write a new post i just push the changes to the git repo and the pipeline builds, deploys and restarts the container automatically. This makes writing a lot more enjoyable as i don’t have to perform manual ops works just to update the blog with the new posts anymore.

Hopefully this write-up will be usefull for someone. If you have any feedback or questions shoot me a message on mastodon.