Update: Multi-tenant deployments will be shipping as part of Octopus Deploy 3.4 and Beta 2 has been released! See the 3.4 Beta 2 blog post for the latest information.
This is a follow-up RFC to the original RFC on better support for multi-tenant deployments in Octopus Deploy.
Octopus Deploy is designed to deploy software releases, and promote them through a series of environments with repeatable results. Octopus models this typical scenario nicely, but it doesn't scale well for multi-tenant applications.
Our previous proposal explored the concept of tagging environments to make dealing with many environments easier. This RFC will explore how Octopus Deploy could behave if we implemented tenant a first-class concept instead of environment-pretending-to-be-a-tenant.
This is a big RFC, but it's also a big set of features! We encourage you to take the time to understand what we're proposing, how it will affect your situation, and help us steer!
- Why another RFC?
- Differences from the original RFC?
- What we propose to do about multi-tenancy in Octopus Deploy
- Wrapping up
- Leave a comment!
The brilliant thing about getting involved with the RFC process is that you really can make a measurable impact on the direction we take with Octopus Deploy. There were a few compelling features that prompted us to draft another RFC:
- Tenant-aware lifecycles where you want to ensure a release has been deployed to the
Tenant 1before you can deploy it to the
Tenant 1, and likewise for all tenants.
- Easier leak-proofing where you want to ensure you don't accidentally deploy a release for
Tenant 1into an environment belonging to
- Tenant-centric workflows where you want to manage tenants, their projects, environments, variables, and deployments from the perspective of a tenant.
We could build these features using environment tags, but we quickly found the complexity of the implementation growing, let alone trying to describe how to configure Octopus to achieve these compelling features. Considering tenant as a first-class concept makes these features easier to implement, and significantly easier to explain.
And let's not forget, Octopus Deploy is designed to work with a small number of environments. Introducing tenants means we could introduce features to make it easier to deal with lots of tenants, and you could return to managing a small handful of environments!
You will probably notice a lot of similarity between both designs. If you haven't already read the original RFC we would highly recommend doing so. In most cases you could simply replace any environments-pretending-to-be-tenants with tenant-as-a-first-class-concept:
- The core problem being multi-tenancy is painful has not changed
- You could still manage lots of tenants easier by treating them as groups of tenants using tenant-tags instead of environment-tags
- To make working with tags a little bit easier, you could color-code your tags
- Lifecycles would revert to work how they do today, explicitly adding Environments to Phases, but Lifecycles could become tenant-aware to ensure you safely promote releases to each Tenant's Staging environment before going to Production (for example)
- The dashboards could still be aggregated in a similar way, but we could make this more of a pivot table where you could select two options from Tenant, Release or Environment to customise your view
- You could still define variable templates, but instead of defining the values on an environment-pretending-to-be-a-tenant you would define them on the tenant itself
- The variable inspector could still work similarly to how we originally proposed
- Instead of implicitly mapping tenants to projects via lifecycles, you could explicitly map tenants to projects
You will probably also notice a few extra compelling features for managing multi-tenant deployments with Octopus Deploy, alongside a few delightful enhancements that make sense with tenant-as-a-first-class-concept.
The one thing that will be more difficult is the migration process. With environment-per-tenant there would be no migration required - you would just start using the new features additively. With tenant-as-a-first-class-concept you would need to convert certain environments to tenants, bringing certain variable sets/variables with them.
Instead of wrestling either Environments or Projects into a shape to achieve multi-tenant deployments, we propose to model Tenant as a first-class concept. You could:
- Create and manage Tenants
- Specify which Projects should be deployed into which Environments for each Tenant
- Manage variables specific to each Tenant
- Define Lifecycles that are Tenant-aware
As usual we want multi-tenancy to be an additive set of features: if you don't need multi-tenancy, your Octopus experience will be the same as it is today except for some features that will benefit everybody.
Once you've enabled multi-tenant deployments the interface could add a Tenant tab to the main menu bar where you could manage your tenants.
You could give each tenant a name and logo making it easier to distinguish tenants when they are listed. You could also explicitly define which Projects will be deployed to which Environments for each tenant. In this case
Customer-2 will have deployments of the
Mojo Projects to the
This is a specific area where we'd like feedback: How granular should this linking be? If a tenant has a Staging and Production environment, does that apply to all Projects? Or would you like to select specific Environments for each Project?
When you have lots of tenants (a great problem to have) working with them as individuals is painful. We (still) propose to introduce tags to enable these types of scenarios. You may have noticed we've defined several tags for
Customer-2 above, in this case as a
VIP tenant with the defined modules, hosted in the
Shared 2 hosting group/cluster/farm.
Firstly you would determine which tags you want to make available by creating tag sets in the Library, with the valid list of tags for each tag set. Which tag sets and tags you create is completely up to your discretion. In this example we have created several tag sets each representing different attributes we want to leverage throughout Octopus Deploy.
Note these tags mean very little at the moment - read on to see how each of these different attributes can be leveraged to enable some compelling scenarios, especially when your tag sets represent orthogonal concerns.
Also note that tags is an additive feature: if you don't define any tag sets, Octopus won't prompt you for anything to do with tags.
If you have lots of tenants it would be a lot easier if you could manage which tags are applied to tenants in bulk. This would enable you to add a new tag set, and very quickly tag all of your tenants appropriately.
Once you have configured some tag sets and tagged some tenants Octopus could aggregate the tenants page by tag sets. You could search for a tenant by name or tag, or drill directly in to one of the tags and be presented with the matching tenants.
With tenant as a first-class concept Octopus could make the dashboards more like a pivot table where you could select from project, environment, tenant/tag set. Consider an example of Project/Environment (the default today) Octopus could aggregate the most important information about each group including the number of tenants, overall status of the group with indicators that draw your attention if necessary, the range of releases that have been deployed to the contained tenants, and the progress of rolling out the latest release to the contained tenants.
Some people might be more interested in Project/Customer Type or another aggregation.
Similarly the project overview could be grouped by environment or tenant/tag set showing the release history as it does today.
Octopus could also make lifecycles tenant-aware preventing you from deploying a project to the
Production environment for
Customer-1 until you have deployed to the
Staging environment for
Lifecycles could work just like they do today, except where it sees a tenant in multiple environments: in this case the lifecycle could ensure a release is promoted through the chain of environments for each tenant.
Once you have created some tenants you could select a tenant and an environment to deploy a release. You could also select multiple tenants and environments (limited by lifecycle) to run the deployments in parallel (just like you can deploy to multiple environments in parallel today).
Octopus could also provide a screen specifically designed for rolling a deployment through all of your tenants. Notice we can now arrange the tenants vertically (no more horizontal scrolling) and potentially filter tenants by name or tag. Octopus could show the recent release history for each tenant, and the Deploy button so you can upgrade all of your tenants from the same screen.
In certain parts of the Octopus world it could make sense to identify tenants. Consider authorising deployments to specific deployment targets, or accounts, or scoping variables and steps to specific tenants. Rather than being limited to identifying specific tenants (possibly hundreds), Octopus could allow you to select a set of tenants using combinations of tags. Octopus could resolve this tenant query at runtime to provide a point-in-time list of matching tenants.
While referencing tenants using these queries could be more convenient in many situations, it could become more confusing depending on the complexity of each situation. To help tame the additional complexity we could provide a design view, similar to what we've done for designing the version rules for Channels.
Consider these scenarios related to multi-tenant deployments:
- you may provide dedicated machines for each of your tenants, and you don't want to leak deployments nor sensitive variables/data to the wrong tenant's machines
- you have a customer who is providing their own Azure subscription, and you want to ensure deployments for other tenants don't leak into that subscription
- you want to implement a dedicated/shared-hosting model, where some tenants will be hosted in a shared pool/cluster/farm, and other tenants will be hosted in a dedicated pool/cluster/farm
Octopus could help you achieve these goals by allowing you to determine which tenants, by name and/or tag, should be allowed to deploy to specific targets and accounts.
This is an example of setting up one node in a shared hosting cluster. Octopus could automatically include this deployment target for deployments of
Synergy-Web-Server for tenants tagged with
Hosting: Shared 1 to the
Production environment. Additionally could prevent this deployment target from being included for deployments that don't match this specification.
This is an example of restricting which deployments are authorized to use an account. Octopus could authorize deployments for
Customer-3 to the
Production environment to use the
Customer-3 Synergy Production Subscription account, denying deployments that don't match this specification.
Deployment targets and backwards compatibility
We would like to maintain backwards compatibility and allow you to opt-in to multi-tenanted deployments. We think by differentiating between normal projects and multi-tenant projects (projects that are mapped to at least one tenant) will help. Consider how we could calculate which deployment targets should be involved in a deployment:
- When deploying a normal project: Find all deployment targets with matching roles ignoring any tenant scope specified by the deployment target. This is equivalent to what we do today.
- When deploying a multi-tenant project for a specific tenant: Find all deployment targets with matching roles where the tenant matches the tenant scope specified by the deployment target.
This means a deployment target that has no tenant scoping specified would not be available for deployments of a multi-tenant project. The other upside to this is that you could build infrastructure ahead of time, and safely add tenants to the infrastructure as you need it.
Scoping variable values would be more convenient if you could scope based on tags. In this example the variable value would be used when we deploy to the
Production environment for any of the
Early adopter tenants using the
Similarly scoping deployment steps could become much simpler. In this example we have customized our deployment process based on several different tags. Firstly we will deploy the Exploration module to tenants tagged with
Module: Exploration. We will also notify the priority support team when a VIP customer, tagged with
Customer type: VIP, has been upgraded. We will also send a tailored email notification to our tenants once their production deployment has been completed successfully.
The next piece of the puzzle is managing variables specific to each tenant. To deal with this we are planning to let you add variables directly to an tenant, but before we get on to that, how do you know which variables need to be added to these tenants? Imagine if projects could define the variables required for each different tenant, and then each tenant could prompt you for the variables it requires. We are proposing to introduce the concept of variable templates.
Variable templates could allow a project to define which variables are required for a successful deployment. We think variable templates will be more manageable as composite parts, just like project variables and library variable sets are today. Each project could define the set of variables that change between tenants, optionally including common variable templates from the library. In this example the project has defined two specific variable templates and included two variable set templates from the library.
Each of these variable templates could be defined in a similar fashion to how parameters are defined for a step template, where you can provide the variable name, a label, help text, a default value and an input control type, such as single-line/multi-line text box, sensitive/password box, check box, or drop down.
Many customers doing multi-tenant deployments with Octopus today will create a library variable set for each tenant. We propose to make variables available as part of the tenant settings. This way you could specify all of the tenant-specific variables as part of the tenant itself, and Octopus would automatically and implicitly scope these variables to that tenant. When you deploy a release for a particular tenant Octopus will automatically merge the variable set from that tenant.
In this example
Customer-2 will be required to provide variables for both
Mojo projects, and based on the variable templates for both projects we would require:
TenantAliasfrom the Standard tenant details library variable set template
StorageAccountUrifrom the Common storage account library variable set template
SynergyApiKeyfrom the Synergy project
MojoDatabasefrom the Mojo project
In this case we haven't defined a value for the
MojoDatabase variable and are being prompted to set that value.
Today, variables in Octopus Deploy generally come from projects, library variable sets, or step templates. Adding tenants as a source for variables will simplify some aspects of managing variables, but diagnosing problems with variables could become more difficult. We are proposing to add a variable inspector that will make it easier to get an overview of all of the variables in a project, their source, and if there are any problems.
With these features in place we hope the pain involved with managing large-scale multi-tenant deployments will be eased significantly. Consider what would be involved in creating a new tenant:
- Create new tenant called
- Enter the prompted variables
- Deploy the latest release(s) to the environment(s) for
What do you think? This is your chance to speak up (again) and help us build the right features for your situation.
This time around we would like to pose some specific questions:
- How do you like this proposal compared to the previous one (environment tags)?
- Could you describe how you would want to link tenants to projects/environments to suit your situation? Do you want to select a project and specify which environment(s) should be available for the tenant in that project? Or does every tenant have the same environments regardless of project? Perhaps something else?
- Do you see any compelling reason you should be able to scope a variable in a project or library variable set to a specific tenant by name, or should we force you to specify the value on the specific tenant?
- Is there any specific feature we're missing?