Photo by Alexander Andrews (https://unsplash.com/@alex_andrews)
About a week ago I was trying to figure out if there’s a good started Makefile I could use for Go projects.
Huge number of followers I have couldn’t help me much …
I was able to find a bunch of resources that ranged from very simple Makefiles to pretty advanced stuff that includes various tasks, such as running tests, linting, go format, etc.
As I was going through all those resources, I stumbled upon one of the Makefiles from Jess Frazelle’s Github repos. In addition to what I considered at this point ‘standard’ targets (build, lint, format) she also uses a bump-version
target that increments the project/package version. To increment the version, Jess uses a sembump tool she created, and I was thinking it would be nice to have a service that I could just curl to and it would do pretty much the same — you’d provide it a version, the portion of the version you want to bump (major, minor or patch) and the service would respond with a new version.
What started as a search for a starter Makefile
for Go, ended up a simple service for bumping semversion. Service I created is up and running at https://semver.xyz and you can either curl to it or try it out by going to e.g. https://bump.semver.xyz/minor?version=1.0.0
I’ve also uploaded the whole project to GitHub in case you want to follow along and actually look at the code.
Thanks to the bunch of work already done by others, there’s not much to the actual code — it’s only a couple of lines. I am using an existing library called semver, built by Benedikt Lang. That library does the hard part of parsing the version, and I just take the portion of the version you want to bump up and increment it. In addition to this library, I am using Gorilla to expose an endpoint you can call to bump the version it receives.
With the coding portion pretty much done, I started to think what would be the easiest and fastest way to quickly deploy this service to Kubernetes cluster.
At this point an image a toy truck on a huge flatbed truck comes to mind — it’s probably wasteful to run such a small thing on a cluster, but I already had an existing cluster with stuff running on it, so I am excused :)
Anyway, in order to get stuff deployed to the cluster, I started with Kubernetes Deployment and Service — something like this:
Deploying this is a of running kubectl apply -f filename.yaml
and you’re good. But, once you make some changes to your code, you have to rebuild the Docker image, push it to the registry and then restart the Pod in order for the new image to be picked up. That meant I probably need a Makefile
target that builds and pushes the image(s) for me, which roughly looked like this (I did parametrize the Docker
file name and the image name though):
So, now I am able to build my image and push it to the registry. Next steps was to modify the Kubernetes yaml
with the new image name and then deploy it. At first, I wanted to just do define some variables inside the yaml
file (e.g. %IMAGE_NAME%
, %VERSION%
, …) and use sed
to replace them with actual values. Sounded like a good approach, but then I remembered I could use Helm and templatize the deployment — it would make things much easier in the long run as I’ll be able to upgrade the releases and have some tracking that way.
With Helm installed, I started by creating a chart (helm create semver-svc
). Luckily, the empty chart Helm has pretty much everything I needed, so it was only a matter of updating the values in the values.yaml
file. Once I tested it out by running helm install
I ended up back in the Makefile
and create another set of targets for installing and upgrading Helm releases:
I defined an install
and upgrade
targets for the service — install is used first time you’re trying to create a Helm release and upgrade is used for subsequent updates to the release.
With most of the targets parametrized I was able to define the uber upgrade
target that bumps the version, builds and publishes the Docker images and finally calls upgrade on the service to actually upgrade the thing that’s already running in the Kubernetes cluster.
The bump-version
part of the Makefile
is pretty much the same Jess has in her Makefile, difference being that once I had my service deployed, I was able to call the service to bump the version like this:
I also wanted to expose this service through a custom domain (https://www.semver.xyz) all with SSL and stuff! Setting this up involved installing [kube-lego](https://github.com/jetstack/kube-lego)
(for automatically obtaining the SSL cert from Letsencrypt) and an Nginx ingress (to expose the service to the world) like this:
helm install stable/kube-lego \--set config.LEGO_EMAIL=[YOUR_EMAIL] \--set config.LEGO_URL=https://acme-v01.api.letsencrypt.org/directory
helm install stable/nginx-ingress --namespace [NAMESPACE]
Since the default Helm chart that was created for the service already contained an ingress.yaml
template, it was just a matter of setting the values for it:
Finally, I had to go over to my domain registrar (name.com) and hook up the cool domain I got with the IP address of the Nginx ingress controller (you basically need to create an A record for your domain that points to the IP address).
Done! I had the service now accessible publicly over https! (Later I also added a simple static HTML page and basically used the same targets and a new Helm chart to deploy it).
The implementation of the actual service was extremely straightforward and simple. I spent most of my time figuring out the Makefile
targets, creating the Helm charts and coming up with the names for my targets (install? upgrade? deploy? release?).
As I next step, I’ll think about trying to come up with a good basic template with a Makefile/Helm charts that I could use for any new services/projects I might work on.
Any feedback on this article is more than welcome! You can also follow me on Twitter and GitHub. If you liked this and want to get notified when other parts are ready, you should subscribe to my newsletter!