Too often, technical options are framed as an “either/or” proposition. There seems to be a sense that if you use technology A, then technology B cannot be implemented; the reality, however, is that the relationship between technologies is often so complex that they defy such comparisons.

Very few things in the software world fall into this paradigm as strongly as containers and microservices. Often discussed as competing strategies, the two represent different ways of thinking that can be leveraged to provide a better overall product.

Below, we’ll compare containers and microservices. We’ll look at what makes them suitable to the average developer, what some strengths and weaknesses are, and some use cases that they are appropriate for. We’ll also discuss some standard technologies behind both and look to the future to see where they are going.

What is a Container?

In its most basic form, a container is simply an isolated process. It is a collection of segmented resources and application functions to do one specific thing — it is, quite literally, a “container” filled with all the code, dependencies, and a runtime environment required to perform a process.

A container is essentially like a car. Cars contain everything a person could conceivably need to get from one location to another in relative comfort and safety. From the engine, the air conditioner, to the carburetor, everything is designed for the specific form function, and there’s very little extraneous functionality. You’ll never find a standard-issue sedan built with an oven inside, because that’s not within the domain of what it’s supposed to accomplish.

This limit in functionality and pre-packaged form factor delivers several benefits. Chief among them is the fact that it allows for portability across stacks and platforms. When a container contains everything required to function, it is platform agnostic — the container can do the work it needs to do regardless of where it lives. Additionally, this makes the containers lightweight and efficient, as there’s minimal outside dependency or unexpected requirements that make operations heavy.

What is a Microservice?

A microservice is a distinct micro-function that forms a singular part of a larger collection of functions. Microservices are better defined by how they fit into this collection rather than as a singular entity. For example, imagine a top-level flow in which a user makes a request, and a page is served to that user. In a monolithic system, all processes and systems would be built into a single monolithic resource. This may work fine at first, but what happens as we expand out the requirements of that data service process? As we grow in complexity, we grow in the overhead to manage.

A solution to this problem is to break up the larger monolithic process into many smaller operations. These smaller processes form “microservices” — small chunks that handle a specific form or function. Microservices are good at translating this data to another service for further processing (or to the end-user).

When a user requests a page, a request handler may be the first microservice it touches. This service may push the request to a group of microservices handling advertising, user heuristics, profiling, data, etc. Each of these microservices can function on this initial request, but they can also be expanded out to provide the same functionality for other requests, thereby allowing microservices to more efficiently handle these first requests and possible future ones.

How Are Containers and Microservices Different?

These two topics may sound quite similar, but there are some very specific differences between the two that should be understood.

First and foremost, containers and microservices can both be used collaboratively or in isolation. It’s possible for an application to exist as a monolith service solution wherein everything needed is included in the single executable function; it’s also possible for this greater service to actually be a collection of microservices without a container in sight. Containers can themselves contain microservices — and a microservice may contain a container to facilitate a core function that cannot be reduced further.

Secondly, there are benefits and drawbacks to each particular implementation. Microservices provide for more extensible and scalable solutions, as each service can move agilely across different segments and platforms. This also means that microservices can be very lightweight compared to other solutions, as each new microservice is simply a part of a greater bulk rather than requiring an entirely new entity to be created with its own resources.

Microservices do come with some drawbacks, however. Most notably, microservices networks can become very complex. Since every microservice is essentially part of a larger ecosystem of services, each individual microservice has to be built and planned within a complicated contextual system.

Containers, on the other hand, have a chief benefit in that the structure is different. As everything is built into its own container, and all the required resources are bundled with the function, everything can be built as its own distinct form and flow. This is a huge benefit in terms of simplicity of development, as you only really ever need to worry about the container itself.

Unfortunately, this also means that there’s a high level of inefficiency. Multiple containerized systems mean duplicated resources, duplicated functions, and in some cases, entirely duplicate services. This means significantly less extensibility and flexibility are granted in terms of resources, even if you gain some extensibility and flexibility in terms of development patterns.

Common Technologies

This topic bears some discussion as to standard technologies for both containers and microservices. In the wild, what do these two technologies actually look like?

Container Technologies

The most common container tools the average developer will come across are Docker and Kubernetes. The main difference between Docker and Kubernetes comes down to the fact that Kubernetes is cluster-centric, whereas Docker is node-centric. In other words, Docker runs containers on a single node of resources, and Kubernetes helps manage a cluster of servers and resources. Sometimes, Docker and Kubernetes are discussed as standards at-odds, but Microsoft states it best thusly:

“The conversation around Kubernetes vs. Docker is often framed as either-or: should I use Kubernetes or Docker? This is like comparing apples to apple pie, and it’s a common misconception that you must choose one or the other.

The difference between Kubernetes and Docker is more easily understood when framed as a “both-and” question. The fact is, you don’t have to choose—Kubernetes and Docker are fundamentally different technologies that work well together for building, delivering, and scaling containerized apps.”

While there are undoubtedly other offerings in this space (notably Apache Mesos, Nomad), these two are the most common and widespread.

Microservice Technologies

Because a microservice is a paradigm, the technologies that support them vary wildly from simple enablement to more complex support and frameworks. Core to the microservice method is the idea of RESTful development, a topic that we’ve talked about at length before. RESTful design enables and extends microservice possibilities and can be leveraged to provide more and more complex functionalities.

To get more specific, let’s look at an example of a technology enabling the future of the microservice conceptual paradigm. GraphQL is perhaps one of the most impactful and promising technologies of the modern microservice communication system.

GraphQL, in essence, allows for the output of a microservice to be largely dictated by the entity requesting the function. By controlling that output and allowing for manipulation and conversion, GraphQL facilitates a microservice environment in which the client forms and utilizes data in the way that it dictates is appropriate. Looking to the future of microservices, we can see that ever-more-complex implementations will require more advanced conversion, transformation, and extension. Accordingly, GraphQL is a promising technology that makes it possible for the microservice to do far more than it could in its most basic form.

Of course, more complex interaction also means more complex systems and more data are pushed through the pipeline. We can look to another strong development in the form of Node.js to find our solution. Node.js is incredibly fast when handling Input-Output tasks because that’s what it was principally built for. In an environment filled with microservices, the adoption of Node.js can significantly reduce overhead and can improve processing speed and resource requirements.

This improvement is best summarized by Stephen Commisso, Senior Software Engineer at GoDaddy. Commisso states that “proper utilization of microservices and Node.js can help to handle the same load with just 10 % of the hardware.” In a world where microservices are constantly expanding to cover more ground while more users are being added by the day, this resource-saving process is vitally important. Microservices will not only have to figure out how to enable more performant functions in the future; they’ll have to figure out how to optimize the data flow to support that.

Conclusion

Containers and microservices are not an either/or situation. Some situations may call for a specific option depending on the application, and there are even some instances where both are appropriate as a collaborative, supportive engine. That being said, there are some strong cases that could be made for each. Containers are best for discrete functions that have stated prerequisites, as they package the function with the resourcing. Microservices, on the other hand, are best when many functions share the same resources, despite the functions each doing very specifically different things.

What do you think about this summary? Do you prefer containers or microservices? Let us know in the comments below.