You are reading our blog series: Aegir5 Roadmapping
In our previous post, we talked about our recent client work building a Kubernetes-based system for hosting web applications. We’ve defined a general framework to support our development and production hosting workflows, and recognized this as a solid basis for an alternate backend to plug in to the existing Aegir5 front-end. Today we’ll take a look at the Drupal architecture underlying that front-end.
In Aegir5, the building blocks consist of Task and Operation entities. Tasks represent relatively low-level reusable functionality. For example, we might have a Task for the Ansible back-end that is responsible for writing an Nginx vhost. We might then bundle that Task, along with others, in order to compose an “Install Drupal” Operation. Since the need to write a vhost is pretty common for web applications, that same Task might also be bundled into an “Install Joomla” Operation.
Operations represent the actions that a user might want to trigger. Operations are configured to contain one or more Tasks. Each Task is a standalone fieldable entity. As a result each Task is responsible for gathering whatever data it needs to run its back-end tasks. The Operation includes these Tasks as inline entities.
When we trigger an Operation, we are presented with a form that includes all the fields from the various Tasks associated with that Operation. These values, across all of the related Tasks, are marshalled and sent to the back-end.
Logs are kept at the Operation level and so the output from the back-end gets piped back into a field on the Operation. In addition to the terminal output from the back-end, we also include the return code of the operation, so that we can record whether it succeeded or not.
Another type of task is what we call an “action” task. These do not get bundled
into Operations, but rather happen within a given page request. For example,
instead of explicitly triggering a “password reset” task, as we do in Aegir 3,
we can have a task dispatched when we click a “Log into site” button. This task
drush uli (via the Celery task queue), and relays the result directly
back to the front-end, almost immediately. This can in turn redirect us
directly to that URL. The result is that we are logged into the site without
having to wait for a fully logged Operation to run.
In the same way that Operations are made up of Tasks via inline entities, other entities such as Sites and Platforms are themselves made up of Operations embedded as inline entities. When we create a Platform entity, we instantiate all of its Operations and related Tasks. Among other things, this allows us to reuse Tasks between Operations.
For example, when, taking a backup, we will want to record the file path to the tarball. This data then becomes available to the restore task that may come later.
This case also illustrates the need for the back-end to be able to write
data to the front-end. This is another function of
relayd in the queue
architecture. (see the upcoming post on this for more details). In addition to
writing logs to Operation entities, it can write data to any field within any
entity on the front-end.
The sequence diagram below illustrates the flow from an Operation being triggered to run, through placing a task on the queue, running the backend executor (currently Ansible, soon Kubernetes), and getting feedback returned to the frontend.
This architecture largely mirrors how Aegir 3 works. However, it provides a more flexible, robust and scalable solution. In our next post, we’ll take a look at the higher-level UI architecture we plan to implement to represent the key components in our Kubernetes hosting framework: Clusters, Projects, Releases, and Environments.
We've disabled blog comments to prevent spam, but if you have questions or comments about this post, get in touch!