If you read my background in the front page, you know I’m not a prolific programmer by any definition. This isn’t going to be a gigantic revalation and if you’ve worked with deploying your own applications to kubernetes you’ll already know most of the following things. With that said, what are the problems with deploying a distributed application? I was tinkering with gifinator and while writing the kubernetes manifests I realized there was a problem. A few bugs required multi-service re-deployments. Let’s say there was something affecting the render service and the gifcreator worker service. I could rebuild 2 new docker images, push them to the local image repository, update the version in their respective manifests and apply the changes. The issue is that after doing it once or twice it became really annoying when wanting to roll back to check the previous behavior. The more this cycle went on, I had to keep track of a set of 4 different versions which quickly became unmanageable.
That’s when the obvious dawned on me, the upstream kubernetes manifests all used the same image somehow.
I quickly opened them up and checked to see they were redefining the container’s CMD
directive!
In fact the actual Dockerfile did not even have a CMD
nor ENTRYPOINT
defined.
The code was built, the binaries were copied to the second stage and the final image was shipped.
Using this method the final image is as small as possible without needing 4 separate ones.
It’s pretty easy to do this so let’s take a look at the Dockerfile as well as the kubernetes manifests.
This specific Dockerfile is available on gitlab for those interested but I’ll paste it here for ease:
|
|
First up we set some basic environment variables and copy the code into the building environment.
Then one by one the services are compiled and installed into the build stage image.
Following that, in the run stage the needed directories are created, the binaries are copied over, assets are also copied over and a final environment variable is set.
There is no CMD
or ENTRYPOINT
as you can see, this will probably result in the inability to just docker run -p externalport:internalport imagename
but the tradeoff is well worth it.
With the Dockerfle out of the way, let’s look at the way these images are deployed. The primary way I run containers is through kubernetes so we’ll be looking at kubernetes manifests. Same as the Dockerfile you can find the source for these manifests on gitlab, I’ll be linking each one. kube/manifests/gifinator/render/deployment.yml
|
|
kube/manifests/gifinator/gifcreator-server/deployment.yml
|
|
kube/manifests/gifinator/gifcreator-worker/deployment.yml
|
|
kube/manifests/gifinator/frontend/deployment.yml
|
|
Here are 4 different services, all easily using the same image. Just by redifining the command it’s easy to simplify the development process by a quite a lot. It’s worth noting that I’ve only shown the deployment manifests since the other ones don’t have anything to do with the container image used.
This is a short post mostly made up of code snippets but I hope you learned something and can improve your workflow with distributed microservice applications.