fuel-dev -- Modular arch description

Add modular arch description and howtos
to fuel-dev docs.

related blueprint fuel-library-modularization
Change-Id: I3a7ecb22d1b90472580d269107da57909728eabd
This commit is contained in:
evkonstantinov 2015-05-25 16:40:02 +03:00 committed by Evgeny Konstantinov
parent 61ef0edfbf
commit f22a183768
6 changed files with 397 additions and 0 deletions

View File

@ -21,3 +21,4 @@ Development Documentation
develop/pxe_deployment
develop/ostf_contributors_guide
develop/custom-bootstrap-node
develop/modular-architecture

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,396 @@
Modular Architecture
====================
The idea behind the modular architecture introduced in Fuel 6.1 is the separation of the legacy site.pp manifests to a group of small manifests. Each manifest can be designed to do only a limited part of the deployment process. These manifests can be applied by Puppet the same way as it was done in Fuel 6.0 or older. The deployment process in Fuel 6.1 consists of a sequential application of small manifests in a predefined order.
Using smaller manifests instead of the monolithic ones has the following advantages:
* **Independent development**
As a developer, you can work only with those Fuel components that you are interested in. A separate manifest
can be dedicated wholly to a single task without any interference from other components and developers. This
task may require the system to be in some state before the deployment can be started and the task may require
some input data be available. But other than that each task is on its own.
* **Granular testing**
With granular deployment introduced in Fuel 6.1, any finished task can be tested independently. Testing can be
automated with autotests; you can snapshot and revert the environment to a previous state; or you can manually
run tests in your environment. With Fuel 6.0 or older, testing consumes a considerable amount of time as there
is no way to test only a part of the deployment -- each new change requires the whole deployment to be started
from scratch. See also the `granular deployment blueprint <https://blueprints.launchpad.net/fuel/+spec/granular-deployment-based-on-tasks>`_.
* **Encapsulation**
Puppet deems all resources to be unique within the catalog and the way it works with dependencies and
ordering. Normally one cannot take a third party module and expect that it will work as designed within the
Puppet infrastructure. Modular manifests, introduced in Fuel 6.1, solve this by making every single task use
its own catalog without directly interfering with other tasks.
* **Self-Testing**
Granular architecture allows making tests for every task. These tests can be run either after the task to
check if it is successful or before the task to check if the system is in the required state to start the
task. These tests can be used by the developer as acceptance tests, by the Continuous Integration (CI) to
determine if the changes can be merged, or during the real deployment to control the whole process and to
raise alarm if something goes wrong.
* **Using multiple tools**
Sometimes you may use a tool other than Puppet (from shell scripts to Python or Ruby, and even binary
executables). Granular deployment allows using any tools you see fit for the task. Tasks, tests and pre/post
hooks can be implemented using anything the developer knows best. In Fuel 6.1 only pre/post hooks can use
non-Puppet tasks.
Granular deployment process
---------------------------
Granular deployment is implemented using the Nailgun plugin system. Nailgun uses the deployment graph data to determine what tasks on which nodes should be run. This data graph is traversed and sent to Astute as an ordered list of tasks to be executed with the information on which nodes they should be run.
Astute receives this data structure and starts running the tasks one by one in the following order:
#. Pre-deploy actions
#. Main deployment tasks
#. Post-deploy actions
Each tasks reports back if it is successful. Astute stops the deployment on any failed task.
.. image:: _images/granularDeployment.png
See also `Fuel Reference Architecture: Task Deployment <https://docs.mirantis.com/openstack/fuel/fuel-6.1/reference-architecture.html#task-deployment>_`.
Task Graph
----------
A task graph is built by Nailgun from *tasks.yaml* files during Fuel Master node bootstrap:
::
fuel rel --sync-deployment-tasks --dir /etc/puppet/
*tasks.yaml* files describe a group of tasks (or a single task).
::
- id: netconfig
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy]
requires: [hiera]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/netconfig.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
where:
* ``id`` - Each task must have the unique ID.
* ``type`` - Determines how the tasks should be executed. Currently there are Puppet and exec types.
* ``groups`` - Groups are used to determine on which nodes these tasks should be started and are mostly related to the node roles.
* ``required_for`` - The list of tasks that require this task to start. Can be empty.
* ``requires`` - The list of tasks that are required by this task to start. Can be empty.
* Both the ``requires`` and ``required_for`` fields are used to build the dependency graph and to determine the order of task execution.
* ``parameters`` - The actual payload of the task. For the Puppet task these can be paths to modules (puppet_modules) and the manifest (puppet_manifest) to apply; the exec type requires the actual command to run.
* ``timeout`` determines how long the orchestrator should wait (in seconds) for the task to complete before marking it as failed.
Graph example
-------------
::
- id: netconfig
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy_start]
requires: [hiera]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/netconfig.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
::
- id: tools
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy_start]
requires: [hiera]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/tools.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: hosts
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy_start]
requires: [netconfig]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/hosts.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: firewall
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy_start]
requires: [netconfig]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/firewall.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: hiera
type: puppet
groups: [primary-controller, controller, cinder, compute, ceph-osd, zabbix-server, primary-mongo, mongo]
required_for: [deploy_start]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/hiera.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
This graph data will be processed to the following graph when imported to the Nailgun. Deploy task is an anchor used to start the graph traversal and is hidden from the image.
.. image:: _images/graph.png
Nailgun will run the hiera task first, then netconfig or tools, and then firewall or hosts. Astute will start each task on those nodes whose roles are present in the groups field of each task.
Modular manifests
-----------------
Starting with Fuel 6.1, granular deployment allows using a number of small manifests instead of the single monolithic one. These small manifests are placed in the ``deployment/puppet/osnailyfacter/modular`` folder and its subfolders. In Fuel 6.0 or older there was a single entry point manifest used -- located at ``deployment/puppet/osnailyfacter/examples/site.pp`` in the `fuel-library <https://github.com/stackforge/fuel-library/>`_ repository.
To write a modular manifest, you will need to take all the resources, classes and definitions you are using to deploy your component and place them into a single file. This manifest should be able to do everything that is required for your component.
The system should be in some state before you will be able to start your task. For example, database, Pacemaker, or Keystone should be present.
You should also satisfy the missing dependencies. Some of the manifests may have internal dependencies on other manifests and their parts. You will have to either remove these dependencies or make dummy classes to satisfy them.
Modular example
---------------
Here is an example of a modular manifest that installs Apache and creates a basic site.
::
>>> site.pp
$fuel_settings = parseyaml($astute_settings_yaml)
File {
owner => root,
group => root,
mode => 0644,
}
package { apache :
ensure => installed,
}
service { apache :
ensure => running,
enable => true,
}
file { /etc/apache.conf :
ensure => present,
content => template(apache/config.erb),
}
$www_root = $fuel_settings[www_root]
file { “${www_root}/index.html” :
ensure => present,
content => hello world,
}
As the first line of any granular Puppet manifest, add the following:
::
notice("MODULAR: $$$TASK_ID_OR_NAME$$$")
It will help you debug by finding a place in ``puppet.log`` where your task started.
Now let's split the manifest into several tasks:
::
>>> apache_install.pp
package { apache :
ensure => installed,
}
>>> apache_config.pp
File {
owner => root,
group => root,
mode => 0644,
}
$www_root = hiera(www_root)
file { /etc/apache.conf :
ensure => present,
content => template(apache/config.erb),
}
>>> create_site.pp
File {
owner => root,
group => root,
mode => 0644,
}
$www_root = hiera(www_root)
file { “${www_root}/index.html” :
ensure => present,
content => hello world,
}
>>> apache_start.pp
service { apache :
ensure => running,
enable => true,
}
We have just created several manifests. Each will do just its simple action. First we install an Apache package, then we create a configuration file, then create a sample site, and, finally, start the service. Each of these tasks now can be started separately together with any other task. We have also replaced ``$fuel_settings`` with hiera calls.
Since there are some dependencies, we cannot start the Apache service without installing the package first, but we can start the service just after the package installation without the configuration and sample site creation.
So there are the following tasks:
* install
* config
* site
* start
* hiera (to enable the hiera function)
A visual representation of the dependency graph will be the following:
.. image:: _images/dependGraph.png
**start**, **config**, and **site** require the package to be installed. **site** and **config** require the **hiera** function to work. Apache should be configured and **site** should be created to start.
Now, lets write a data yaml to describe this structure:
::
- id: hiera
type: puppet
role: [test]
required_for: [deploy]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/hiera.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: install
type: puppet
role: [test]
required_for: [deploy]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/apache_install.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: config
type: puppet
role: [test]
required_for: [deploy]
requires: [hiera, install]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/apache_config.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: site
type: puppet
role: [test]
required_for: [deploy]
requires: [install, hiera]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/create_site.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
- id: start
type: puppet
role: [test]
required_for: [deploy]
requires: [install, config, site]
parameters:
puppet_manifest: /etc/puppet/modules/osnailyfacter/modular/apache_start.pp
puppet_modules: /etc/puppet/modules
timeout: 3600
Nailgun can process this data file and tell Astute to deploy all the tasks in the required order. Other nodes or other deployment modes may require more tasks or tasks run in different order.
Now, let's say you have a new apache_proxy class, and want to add it to the setup:
::
>>> apache_proxy/init.pp
file { /etc/apache.conf :
owner => root,
group => root,
mode => 0644,
ensure => present,
source => puppet:///apache/proxy.conf,
} ->
service { apache :
ensure => running,
enable => true,
}
This tasks updates the main Apache configuration as well, and it conflicts with the previous configuration tasks. It would not be possible to combine them in a single catalog. It also attempts to enable the Apache service, which produces a new duplicate error.
Granular deployment solves this. You can still use them together without trying to do something with duplicates or dependency problems.
.. image:: _images/dependGraph02.png
We have just inserted a new proxy task between the **config** and **start** tasks. The proxy task will rewrite the configuration file created in the **config** task making the **config** task pointless. This setup will still work as expected and we will have a working Apache-based proxy. Apache will be started on the proxy task but the **start** task will not produce any errors due to Puppet's idempotency.
There are also `granular noop tests <https://ci.fuel-infra.org/job/fuellib_noop_tests/>`_ based on rspec-puppet. These CI tests will put -1 for any new Puppet task not covered with tests.
Testing
-------
Testing these manifests is easier than having a single monolithic manifest.
After writing each file you can manually apply it to check if the task works as expected.
If the task is complex enough, it can benefit from automated acceptance testing. These tests can be implemented using any tool you as a developer see fit.
For example, lets try using `http://serverspec.org <http://serverspec.org>`_. This is an rspec extension that is very convenient for server testing.
The only thing the install task does is the package installation and it has no preconditions. The spec file for it may look like this:
::
require 'spec_helper'
describe package(apache) do
it { should be_installed }
end
Running the spec should produce an output similar to the following:
::
Package "nginx"
should be installed
Finished in 0.17428 seconds
1 example, 0 failures
There are many different resource types *serverspec* can work with, and this can easily be extended. Other tasks can be tested with specs like the following:
::
describe service('apache') do
it { should be_enabled }
it { should be_running }
end
describe file(/etc/apache.conf) do
it { should be_file }
its(:content) { should match %r{DocumentRoot /var/www/html} }
end