Pattern: Single Tenancy
Written on
Segmenting customers to their own copy of the services
When managing a system for multiple customers, it can sometimes be advisable to keep the workloads for the different customers in isolation. Sometimes this is due to the fact that the application isn't designed to handle multiple customers, as is often the case with an existing product being converted to a SAAS model. Another driver for single-tenancy may be due to data privacy concerns. Consider a simple, multi-tenant approach where I store all financial records for all my customers in the same Account service:
The code that manages both sets of data runs in the same process, on the same machine. I enforce that Customer A can only see their own data either via row-level permissioning in the data store, or in the service application code itself. I may be concerned that a bug in coding will allow customers to see each other's data. I may also be concerned that if a single node is compromised, then having all the information for all customers on a single node is too big a risk. In such a situation I may adopt a single-tenant approach, where I might maintain separate account services for each customer, as we see here:
Concerns around tenancy can extend beyond the service level, to the underlying platform. Many people make use of virtualised platforms where even if I have separate Account services they may actually run on the same underlying hardware, as seen below:
Some provisioning platforms can make it hard to know where two containers/VM are actually sharing the same underlying hardware or not, so you'll need to check if this is a concern to you. Such an approach may help reduce operational costs as we avoid the need for different underlying hardware. You may be able to get especially advanced here and only spin up the instance of a service when it is needed, which again could reduce the costs associated with single tenancy. A container-based approach is likely to make this more practical due to fast spin-up times and lower resource overhead than when compared to a full-fat virtualisation approach.
Container or virtualisation platforms provide varying levels of protections to stop a malicious process breaking out to access the host operating system, and therefore accessing the other VMs/containers running on the same machine, but these vary in how robust they are. Container-based technology has historically been very vulnerable to this sort of attack, which is partly why Docker have spoken more about their containers being about general OS isolation, not secure isolation. Others have even suggested that all virtualisation technology is inherently insecure, although the concerns raised thus far haven't been enough for the bulk of the industry to consider normal Type 2 virtualisation to be a fairly safe isolation mechanism for most circumstances.
Some platforms give us a level of control over tenancy at the virtualised layer. AWS for example allows you to pay an additional cost to launch dedicated instances, which ensures that the given instance(s) is isolated from other AWS customers all the way down to the hardware level. If you used a separate AWS account for each customer's setup, this could give you some security if you are worried about customer's accessing each-others data via the underlying hardware.
This may mean that, depending on the platform you are using, you may need to enforce single-tenancy at the platform level as well as at your application level. This can require additional hardware, and an associated additional cost. If your platform consists of multiple services, and the whole system needs to be single tenant (in other words you aren't considering a Hybrid Tenancy option), then this additional cost can be significant.
You may more cost-effectively manage a multi-service single tenancy system by co-existing multiple services for a single customer on a smaller number of machines, or perhaps by providing some level of virtualisation/container isolation on a machine but ensure that only that customer has access to that node:
This can come at the cost of resiliency though. With a multi-tenancy system I may be able to distribute load across a larger number of machines in a much cheaper fashion, achieving greater resiliency at the same time as more machines need to fail before a given customer is impacted. With a single tenancy model I may have more eggs in one basket, purely for cost reasons.
In general, systems which need to be fully single tenant can add significant overhead in operational management and associated computing resource costs. These costs can be reduced by tending towards more monolithic services, as I've discussed before, as you end up with fewer services and therefore the need for fewer machines to run them on. On the other-hand, with a finer-grained microservice system, you may be able to selectively apply a single tenancy model to parts of the system to find a happy balance, as I cover when discussing the Hybrid Tenancy pattern.