Building a cheap Continuous Delivery environment

IPhoto by Andrik Langfield on Unsplash

I wanted to test building a Continuous Delivery (CD) environment with a minimal cost for Kotlin-based spring-boot maven application.

The application code is hosted at Gitlab (free), Gitlab is a great code collaboration platform, it supports free private git-based code hosting, CI server, and a Container registry.

Providing a Container registry is a big advantage, all that I need is a $5 machine to deploy the latest docker image, and some way to make every successful merge to develop branch triggers a deployment with a button click.

And this what I achieved with only $5, Let’s drill into the details.

Here’s my ci script in Gitlab (.gitlab-ci.yml):

You will need to replace myAwesomeGroup and myAwesomeApp-api with yours.

The script has 3 stages, test, build and deploy.

The test is a typical maven build and test (as generated from Gitlab itself)

The build embraces the Gitlab Container Registry feature, we build the Dockerfile (see next) and push it to Gitlab Registry.

Will talk about the deploy stage later.

The following is the Dockerfile for my app (very straightforward):

Now, we need to go do DigitalOcean (or any — cheap — cloud provider) and create a droplet (VM) with CoreOS Linux distribution.

What we are going to do is to create 2 systemd services, one for the Postgres DB and the other for the API service.

In this case, the API need to access the database, so the first thing I did after Login to the server (digital ocean machine) is to create a docker network:

Copy the following two systemd Unit service definitions into /etc/systemd/system:

myAwesome-db.service:

It is straightforward service, the tricky part here is the container is created under the network myAwesome-network.

myAwesome-api.service:

Same here, we create the pull the latest docker image of myAwesomez-api from Gitlab container registry and refer to the database by docker container name (instead of IP).

Note, to login to Gitlab registry you need to create a “Deploy Tokens” from Gitlab at Settings -> Repository Settings > Deploy Tokens then add read_registry token, and use the username and password in the docker login command above.

What we try to do here is to pull the latest image of the application from the registry and run it, so every restart to the service will get the latest image and deploy it.

Install both Systemd services as follows:

Now the application should be deployed and accessible on port 8080 (or the port you choose in the service definition)

Now let’s test the installation so far. From your personal machine try to remotely restart the service:

Your application should be redeployed.

Now let’s come back to the last part of this article on how to trigger the systemd service restart from the Gitlab build server.

Let’s revisit the file .gitlab-ci.yml, this time we will talk about the deploy section:

To run the service restart command from Gitlab ci, we need to make the deployment server to trust your machine, you accomplish this using SSH client certificate.

We will generate a new SSH key pair, install the private on the Gitlab (client) and add the public into the Deployment Server (the server).

Use the command ssh-keygen to generate a key pair without password and then copy the public key into the server’s~/.ssh/authorized_keys.

Now go to Gitlab at Settings > CI/CD > Variables and create a new variable with the key SSH_PRIVATE_KEY and the value is the private key from the previous step, and click save.

The deploy step above is to use the SSH_PRIVATE_KEY variable to communicate with the deployment server and issuing remote commands over ssh. and here’s the systemd restart service command.

Now when you send a pull request, and it got merged with your develop branch, then it will be Tested, Docker image will be created and pushed into Gitlab Docker repository and if you want you can execute the last deploy step which will restart the systemd service on the deployment server which will ask the docker registry to get the latest application image and run it.

That’s all folks.

Software Developer/Architect