One of the exciting patterns we have been utilizing and working on within the Panamax project is the ability to use Docker containers to perform a functional workflow or a series of asynchronous tasks in the background. Recently, I ran across this interesting post on the Iron.io blog: The Ephemeral Life of Dockerized Microservices, which inspired me to write this post. Although I was already familiar with the Iron.io Worker service it was great to see how they were using containers, specifically short lived or ephemeral containers, to accomplish their service. We have been using ephemeral containers as well, albeit somewhat differently, as a series of functional containers in a workflow. These functional workflows can be triggered on an event or scheduled and allow work to be done on-demand in the background. In our case, the workflow required initial steps to be completed and the output provided to later steps in order for the complete workflow to be successful.
Panamax Workflow Use Case
In the Panamax project, we want to help our users setup a remote cluster on a cloud provider so that they can easily deploy Panamax templates to them. Although we have the capability today to deploy templates to remote infrastructures, the setup of the infrastructure and the installation of our Remote Agent/Adapter are all separate processes.
Lets run through an example of what it would take to automate this use case end-to-end. This is a simplification but assume there are three basic steps to accomplish this capability:
- VM Creation - Spin up VM’s on the cloud provider that will host the cluster
- Manager/Orchestrator Installation on VMs - (CoreOS, etc.)
- Panamax Remote Agent/Adapter Installation and Agent Registration
Now let's say the logic for each one of these steps is encapsulated in a Docker container so that each container is responsible only for a specific step. We could fire up these containers in parallel, but are limited by the fact that each step needs to get some data from the previous step in order for it to execute successfully (e.g. we can’t install the orchestrator in step 2 until we know the IP address of the server created in step 1).
Executing the Workflow
In trying to address this use case, we set out to build a general purpose framework that we could use to orchestrate container-based workflows like the one described above. Among other things our framework would need to:
- Accept a description of the workflow to be executed (i.e. the list of containers)
- Start containers in the correct order and monitor their execution
- Marshal data between the different steps in the workflow
- Report the current status of a workflow
One of the more interesting problems to solve was how to move data between containers. The solution that we settled on was to use a Unix piping model where we would attach to the standard output stream of the running container, capture any output, and then write that data to the standard input stream of the next container in the chain. As each subsequent step in the workflow is executed, the output of the previous step becomes the input to the next step.
Initially we're going to bundle this framework into Panamax, but we'll soon be releasing it as a separate stand-alone project to help others implement this similar pattern.
Ephemeral Containers as Microservices
Let’s talk about some of the advantages of using Containers in a functional workflow:
- Containers can be run anywhere, so the workflow or even steps of the workflow could be executed on any compute resource
- Containers are great for logical units of work, and that isolation lends itself well to the micro-service design pattern
- Computing resources are only consumed when the workflow is executing, thus compute resources are only needed at execution time.
- Modularity of the workflow allows each unit of work or steps in the workflow to be easily changed/extended/re-composed (decoupled architecture)
Given each step in our process is decoupled it has been easy for us to reuse each logical unit. This is one of the exciting aspects of Microservices architecture: the ability to have composable units of work that can be re-ordered, easily modified and extended. Although, we are writing all our worker containers in GoLang in reality each container is a black box and therefore developers can choose any implementation language they wish. This is the same concept as Panamax's Remote Agent/Adapter model, which is also encapsulated in containers.
Functional Container Workflows
Hopefully, now you have some idea as to how such a pattern can be applied. Although the example that was discussed solves a very specific use case, there are much more exciting possibilities for the pattern. The ability to truly build on-demand sensor driven or event based architectures excites me tremendously and can have a very broad impact on how computing resources are consumed. If Docker containers allow you to start a container sub-second, then how solutions are architected can be totally rethought in many cases to be more event driven and on-demand. This means less resource intensive solutions, as compute resources would only be needed for the time the ephemeral container exists. There is also the potential to provide functional libraries of containers that could easily be visually composed into all sorts of interesting workflows by less-technical users. Sensor containers could query APIs where Actuator containers could do units of work. Finally, although we are only doing basic sequential workflows once you add in complex workflows and some simple logic/business rule definition along with the workflow schema you can truly create some unique solutions. Combine all these possibilities with the 'run anywhere' ability of a container itself and the possibilities are truly exciting. How are you using microservices? Have you been able to use containers as more than just holders of application stacks ? We would love to hear from you or please jump in and get involved in one of our many open source projects.