This blog was originally published on Medium on March 13, 2019.
Traditional development workflows are relatively straightforward:
Step 1: Write and build code.
Step 2: Test and fix code.
Step 3: Deploy code.
Step 4: Identify issues and iterate.
In addition to the steps above, container native applications come with new considerations. After compiling your code, it will have to be built into an image. Rather than deploying to a single virtual machine you have to connect to a distributed container orchestration platform such as Kubernetes. In order to schedule deployments to your that platform you will also need an image registry.
This means your development flow now needs to take into account building a container image of your application, pushing the image to a registry service, and deploying the image from the registry to a Kubernetes cluster. If done manually, each step consists of multiple commands that can start to eat into your time and disrupt your development flow. This can be further complicated by the increased complexity of coordinating the deployment of a large number of decomposed and interconnected microservices rather than a single traditional monolithic service. Also, keep in mind this process has to be followed for every one of your microservices.
Identifying issues may also look very different in a containerized vs. traditional development process. Rather than having one central view of all of the logs generated by your monolith, you will have to pull logs for each container individually. Juggling a large number of containers makes it easy to miss logs from one or two or not notice that the container failed to start up in the first place.
You also have the option of developing your microservice without deploying to Kubernetes during the development process. You can deploy your container directly to Docker and test it in isolation. But, deploying to Kubernetes is needed to test dependencies between multiple services. It can also be helpful to run tools to test the microservices you are developing, for instance application tracing. Of course it is also handy if you need your development environment to mirror your deployment environment.
Why Do I Care?
I started to take a look at these tools after finding myself wasting a lot of time adding instrumentation for tracing into a smallJava microservice application. Each time I updated the application, I had to repackage it, create a new Docker image, push the image to a registry, deploy the application, and verify the change actually accomplished what I was trying to accomplish. That was a lot of keystrokes just to make minor application changes — especially when the change did not work on the first, second, third, etc. try. I needed a tool to help me pinpoint and solve my problems by compiling, building, and starting my application and also redeploying it as I made changes. Fortunately, I found out I was not the only one with these needs.
You may be thinking that some or all of this could be taken care of by means of a continuous integration and continuous delivery (CI/CD) pipeline. You would not be wrong in thinking so. Those pipelines are purpose-built to make the process of modern application development simple and repeatable. But imagine that you are trying something new, for instance pushing tracing data to an endpoint of a Zipkin service deployed on your Kubernetes cluster. You are not exactly sure what string to use for your endpoint and Stack Overflow is not being particularly useful, so you decide to try a bunch of different strings to find out which one works. In a traditional CI/CD system this would result in a ton of unnecessary changes being tracked as you tested out various solutions to your problem. Alternatively, if you had access to a local development tool, you could choose to commit only the final version — the one with the correct endpoint — and ignore all of the unsuccessful iterations along the way.
Why Do You Care?
Chances are that during the process of developing containerized applications, you have found yourself facing much harder issues than determining the proper endpoint for a tracing service. At the end of the day, you want to be able to start your application, make a change to the application, and identify problems with that application. You do not want to be bogged down by the complications of containerized development and finding yourself repeatedly typing out docker push and kubectl blah blah.
I am exploring a series of open source tools to simplify the inner loop of the container native development workflow. This describes the period of time during which you are writing code, but have not yet pushed it to a version control system. These tools, Draft, Skaffold, and Tilt each take a different approach to the task at hand. Each tool can be used to build an image of your project, push the image to a registry service of your choice, and deploy the image onto a Kubernetes cluster. Adopting these tools will free up your time and allow you to focus on writing code.
Check out more cloud native and container native projects at http://cloudnative.oracle.com.