This article gives insight and analysis regarding container usage for dev pipeline of a software product built using microservices architecture. Innovative businesses are using a microservices approach because it enables the introduction of new applications that are independently deployable without disrupting the rest of the business.
The microservice architecture provides great benefits for development of complex software systems, but it comes at a price. You need to run a lot of service instances to support the system lifecycle with multiple product development teams, all while ensuring high quality standards.
In addition to the production environment, each microservice will be run in dozens of instances in various other environments required for system development and testing.
Considering that each service is “micro” – in other words it is lightweight compared to the monolith architecture – it does not make sense to run each microservice on a separate computer. On the other hand, running a service in isolation from other services greatly improves the overall system stability.
And for that the industry has a proven solution – Virtual Machines (VMs). One can spawn a new VM that will be dedicated to run each microservice in isolation from other services. The hardware virtualization increases utilization and reduces provisioning times, hence reducing the overall environment cost.
VM Farm as an Option
There is a wealth of information on how VMs are helping to reduce the cost of deploying and managing IT environments. Over the last few decades, this virtualization technology has been perfected by many. As a result, there are many standardized reasons to carefully look into the option of using a set of virtual machines to support the needs of developing the software based on a microservices architecture.
But there are a couple of challenges to highlight if all those environments are implemented using VM farms:
1. To gain operational efficiency while managing sets of virtual machines, it is very important that each VM be as standardized as possible. This is because each and every customization introduces the risk of human error (both creating a template and provisioning it for a certain need), as well as increasing the complexity of managing the farm of virtual machines. But with the reality of multiple teams developing microservices, each service tends to end up with a different VM in the environment because of the varying needs of each service.
2. There are still a lot of virtual machines to manage, because of a variety of logistical, technical and administrative challenges (like access rights, handling resource utilization and consumption peaks, for example) it is much safer to keep one service running per single VM. This does, however, mean that the density of the load per VM is suboptimal, thus reducing the ROI.
3. And last, but not least, someone has to keep the full stack (i.e. OS, web server, libraries, etc.) patched and updated for each virtual machine, which can become a very heavy burden.
Containers to the Rescue
With containers, the operating system, not the physical hardware, is virtualized. The three defining characteristics of microservices to keep in mind: they are stateless, distributed, and independent.
As we’ve seen, the use of virtual machines to host those environments may present certain challenges. That’s why the next evolution of the virtualization technology—containers–presents many advantages. Let’s look at how it can be done using the Docker ecosystem, on top of a cloud service, and how it compares against the use of VMs there.
At the top of the diagram above there are services, and each team is responsible for their own service. The green bars at the edges of the diagram denote the responsibility of that service layer. The left side of the diagram depicts VMs running services with 1 service per VM as explained earlier, while on the right side, there is a set of Docker containers running services. Again, pay attention to the responsibilities highlighted at the outer edges. With VMs, the ownership is shared, which causes the problems discussed in the previous section. With containers, only the development team is responsible at that level.
Here are a few points that we’d like to emphasize on how Docker containers help development teams and DevOps/IT to navigate through the complexity of the environments and further reduce the cost of managing them:
• Developers have direct access to the non-production environments for service deployment giving them greater flexibility and velocity.
• Clear delineation of responsibilities – DevOps manage the infrastructure, while dev teams are deploying containerized services.
• Deploying a new service instance takes minutes rather than days, provisioning the proper VM, negotiating settings, and actually running the service.
• Utilizing the user and role management from Swarm helps to keep access to the service more transparent. On a VM, the access control is usually done using public key management, which adds to the burden of managing all these keys. In addition, VM users are responsible for restoring access to the VM after it’s re-created.
• Higher services density decreases the overall spend on all environments.
•Using Docker allows the implementation of OS patching. Since base OS images are regularly updated with patches and because each Docker image is built from the base OS every time it needs to run, the patches for OS and libraries appear automatically and quickly, thus increasing security.
But There is a Catch
As usual, even with Docker containers, there is a catch! The items below are more ‘things to keep in mind’, than blocking issues or serious deficiencies:
1. The “pet” mindset: When switching from VM to Container the mindset needs to change from taking care of your “favorite pet” (i.e. VM with carefully built and configured stack of software) to “cattle” (i.e. containers which are a disposable unit that can be thrown away when broken or outdated). As an example, if there is something broken in the container configuration, folks with a “pet” mentality try to fix the problem by fixing the running instance of it instead of getting rid of the faulty instance and fixing the container image as it should be.
2. Monitoring the load and capacity of your environment: Keep an eye on the resource utilization, because containers can eat up (due to their density) a lot of resources without dev teams realizing it. So there must be either strong controlling mechanisms or policies on how many containers your dev teams can spawn in the environment, or, better yet, the proactive monitoring of resource utilization.
3. Make sure you think through the logging mechanism of your services – it shouldn’t break the concept of disposable container instances, while providing the robust persistent logging storage for your services (and the entire product).
Microservice architecture requires a strong orchestration layer (like Docker Swarm) on top of any container engine to allow for scalable usage of the capabilities without introducing a lot of overhead to managing that scalability. The bottom line: Do a careful selection of the orchestration engine and account for its engineering trade-offs.