Skip to content

Commit

Permalink
[spec] Propose EDPM OpenStack service architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
gibizer committed Jun 13, 2023
1 parent 45151ce commit 52167c4
Showing 1 changed file with 323 additions and 0 deletions.
323 changes: 323 additions & 0 deletions spec/edpm-openstack-service-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
# OpenStack service deployment architecture on EDPM Node

## Problem description

Various OpenStack componentes have agents that need to run on the External
DataPlane Node. For example OpenStack Nova's nova-api, nova-conductor
and nova-scheduler services can run in kubernetes pods independently from the
hypervisors they manage. In the other hand the nova-compute service is designed
to be collocated with the hyperviors it manages. In the current design the EDPM
Node serves as a hypervisor for Nova, therefore the nova-compute service needs
to be deployed on EDPM nodes.

Similar examples from other OpenStack compontents are ovn-metadata-agent, neutron-nicswitch agent, and in the HCI case the cinder-volume service.

### The deployment dependency

The deployment of an EDPM node needs to consider multiple phases

* **phase -1: Bare metal provisioning**. This step transforms an empty bare
metal machine to a runnign server with a host OS.
* **phase 0: Infrastructure deployment and configuration** of storage,
networking, and computing resources. This step transforms a generic running
server to a more specific storage node, networking node, computing
hypervisor, or a mix of these.
* **phase 1: OpenStack service deployment**. This transforms the server to act
as an OpenStack storage node, networking node, or compute node (or a mix of
them) by deploying and configuring the specific OpenStack services that
connects the server to the OpenStack control plane.

Both phase -1 and 0 can be performed independently from the rest of the
OpenStack control plane deployment. However phase 1 needs to be coordinated
with the podified infrastructure, and OpenStack service deployment. For example
to generate the configuration of the nova-compute service on an EDPM node the
deployment engine needs to know how to connect the nova-compute service to the
podified infrasture, e.g. to which nova cell and therefore via which message
bus. Handling this dependency is one of the scope of this specification.

### The OpenStack service configuration interface

Independently from the fact that an OpenStack service is running in a
kubernetes pod or in a container on the EDPM Node the service has a well
defined and widely known configuration interface implemented by oslo.config
configuration options in one or more *.ini like* files.

The configuration options can be divided to two major parts:
* configuration that is generated by the deployment engine. E.g. database
hostnames and message bus transport URLs
* configuration that is provided by the human deployer. E.g. CPU allocation
ratio, PCI device spec and alias

The existing podified control plane API, the OpenStackControlPlane CRD and the
service sub CRDs, provides a simple way for the human deployer to pass
configuration parameters to the OpenStack services. Each CRD that represents
an OpenStack service type provides a `CustomServiceConfig` field that is a
plain text field to carrying oslo.config formatted configuration. Then the
deployment engine makes sure that this configuration snipet is provided
to the deployed OpenStack service binary in a way that the config options
defined in it has higher priority than the automatically generated, default
service configuration.

On the podified control plane side kubernetes APIs are used to generate, store,
and transfer the OpenStack service configuration, and to deploy, configure, and
start the OpenStack service binary. While parts of this is visible to the human
deployer. E.g. kubernetes Pods, Deployments, ConfigMaps, VolumeMounts. The
human deployer only needs to understand the `CustomServiceConfig` text fields
to be able to pass through the desired configuration to an OpenStack service.

As OpenStack services are running also on the EDPM Nodes there is also a need
to pass through human defined configuration to these services as well.

On the EDPM side Ansible roles are used to provision and configure services.
Ansible has its own configuration passing mechanism, Ansible variables. It is
technically possible to pass through oslo.config options as Ansible variables
to configure the OpenStack services.

However it means that the same oslo.config configuration interface of the
OpenStack service are represented in two different formats. On the podified
control plane side it is an oslo.config snipet in the `CustomServiceConfig`
field. While on the EDPM side Ansible variables. The former does not involve
any translation. The naming and structure of the config options are exactly the
same in the `CustomServiceConfig` field as in the actual oslo.config file of
the OpenStack binary. In the other hand the latter applies a name mangling to
avoid name clashes between different oslo.config sections and between different
services using the same Ansible inventory to pass through variales. Moreover
the latter requires explicit work in the Ansible roles every time a new config
variable is added.

For example, enabling debug logging for the OVN metadata agent on the EDPM node
requires the following oslo.config file to be provided to the service:
```ini
[DEFAULT]
debug = True
```

The `CustomServiceConfig` the following needs to be added to the CR
representing the given OVN metadata agent:
```yaml
customServiceConfig: |
[DEFAULT]
debug = True
```
With ansible variables the following needs to be added to the CR that triggers
deployment of the given OVN metadata agent:
```yaml
ansibleVars: |
edpm_ovn_metadata_agent_metadata_agent_DEFAULT_debug: true
```
## Proposed change
Keep the existing DataPlane, DataPlaneRole, and DataPlaneNode CRDs to represent
and coordinate phase -1 and phase 0 of the EDPM deployment. But decouple
phase 1 from the existing DataPlane model. This means that the DataPlane CRDs
will not represent and therefore won't directly deploy any OpenStack services
on the EDPM node. These CRDs still expose the status of the phase -1 and
phase 0 deployment, so other operators can decide when to start deploying
OpenStack service to the EDPM node.
Define CRDs representing each OpenStack service type that needs to be run on
the EDPM node (e.g. NovaExternalCompute, OVNMetadataAgent, etc). These CRDs
are defined in and therefore reconciled by the respective service operator
(e.g. nova-operator, ovn-operator). These OpenStack service CRDs will be
created automatically based on the existence of DataPlaneRole and Node CR and
the user specified annotations on these CRs. The service operator
will ensure that both the podified control plane dependencies
(e.g. message bus) are deployed and the EDPM node phase 0 is finished before
generates the service configuration and creates AnsibleEE CRs to deploy
OpenStack service on the EDPM node.
The EDPM related OpenStack service CRDs will have `CustomServiceConfig` fields
that will have the same syntax and semantic as the same fields in the existing
podified Openstack service CRDs, while the implementation of such configuration
will use Ansible to transfer the generated configuration to the service running
on the EDPM node.

**Benefits**:

* The DataPlane CRDs and therefore the dataplane-operator implementation kept
OpenStack service agnostic. Adding support for deploying a new OpenStack
or partner service on the EDPM node can be done without changing the
DataPlane CRDs or the dataplane-operator implementation.

* The service operators encapsulates all the service specific logic. Every
deployment, configuration or upgrade concerns for an OpenStack component can
be handled within a single, cohesive operator implementation.

* The `CustomServiceConfig` field in the service CRDs simplifies and unifies
the OpenStack service configuration for the human deployer regardless where
the service is being depoyed (i.e. podified or EDPM).

**Drawbacks**:

* The mechanism to watch DataPlane CRs from service operators and auto create
service CRs adds impementation complexity and introduces possible performance
impacts that could be avoided if dataplane-operator creates the service CRs.

* The Ready condition of the DataPlaneRole and the DataPlaneNode can only
represent the phase -1 and phase 0 readiness of the EDPM node. The phase 1
readiness can only be collected by checking the Ready condition of every
service CRs related to this EDPM node.


### Example

NovaCell/nova-cell1
```yaml
apiVersion: nova.openstack.org/v1beta1
kind: NovaCell
metadata:
name: nova-cell1
...
```
No changes in NovaCell.

OpenstackDataPlaneRole/edpm-compute
```yaml
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneRole
metadata:
name: edpm-compute
annotations:
nova.openstack.org/cell: nova-cell1
spec:
dataPlane: openstack-edpm
nodeTemplate:
ansiblePort: 22
...
```
The `novaTemplate` is removed from the `nodeTemplate` and a new user specified
annotation `nova.openstack.org/cell` is added so the human deployer can
define that this Role describe a set of EDPM nodes that are compute nodes
connected to cell1.

OpenstackDataPlaneNode/edpm-compute-0
```yaml
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneNode
metadata:
name: edpm-compute-0
spec:
ansibleHost: 192.168.122.100
hostName: edpm-compute-0
node:
ansibleSSHPrivateKeySecret: dataplane-ansible-ssh-private-key-secret
role: edpm-compute
...
```
The `novaTemplate` is removed from the `node`. As this DataPlaneNode referst
to a DataPlaneRole that has a `nova.openstack.org/cell` this DataPlaneNode is
a compute node connected to cell1.

OpenstackDataPlaneNode/edpm-compute-1
```yaml
apiVersion: dataplane.openstack.org/v1beta1
kind: OpenStackDataPlaneNode
metadata:
name: edpm-compute-1
annotations:
nova.openstack.org/cell: nova-cell2
spec:
ansibleHost: 192.168.122.101
hostName: edpm-compute-1
node:
ansibleSSHPrivateKeySecret: dataplane-ansible-ssh-private-key-secret
role: edpm-compute
...
```
This DataPlaneNode has an `nova.openstack.org/cell` annotation that overrides
the cell annotation of its role. So this EDPM node is a compute node connected
to cell2.

NovaExternalCompute=edpm-compute-0
```yaml
apiVersion: nova.openstack.org/v1beta1
kind: NovaExternalCompute
metadata:
name: edpm-compute-0
spec:
dataplaneNodeName: edpm-compute-0
cellName: nova-cell1
customServiceConfig: |
# human deployer can uset this to pass through service specific config
sshKeySecretName: dataplane-ansible-ssh-private-key-secret
...
```
This CR is auto created by the nova-operator based on the corresponding
DataPlaneNode CR and the cell annotation set on it. The spec fields, except
`CustomServiceConfig` is populated by the nova-operator. The
`CustomServiceConfig` field is never set by the nova-operator it can be used
by the human deployer.

The following fields will be copied over from the DataplaneNode (or Role due to
inheritance) and changes there will be propagated to the NovaExternalCompute CR
* InventoryConfigMapName
* NetworkAttachments
* SshKeySecretName

The AnsibleEEContainerImage, NovaComputeContainerImage, and
NovaLibvirtContainerImage will be set based on the respective ENV value via the
defaulting webhook in nova-operator during NovaExternalCompute auto creation
but these fields can be changed later independently from their defaults.

The NovaInstance field is removed and the CellName field is redefined to hold
the name of the NovaCell CR. The name of the NovaCell CR is a combination of
the name of the cell from the NovaCellTemplate in the Nova CR and the name of
the Nova CR. So it uniquely identifies a cell even if there are multiple Nova
CRs exist in the namespace.

The upstream nova project does not support moving computes between cells.
Therefore the NovaCell field can only be set at creation and cannot be updated
later. The dataplane-operator needs to implement a validation hook to prevent
the related annotation to change after being set.


## Alternatives

* Continue the pattern what we have today. The DataPlane CRDs would embed
OpenStack speific service templates and the dataplane-operator would create
service specific CRDs based on that to trigger OpenStack service deployment
on the EDPM node after phase 0 is succeded. This alternative can support
`CustomServiceConfig` based OpenStack service configuration.

It is not the preferred alternative as it would cause that the
dataplane-operator gather OpenStack service specific implementations. And
therefore OpenStack component specific knowledge would be split across
multiple operators.

* Do not represent OpenStack service types as CRDs. Keep DataPlane CRDs as
generic Ansible interfaces to trigger service deployment.

It is not the preferred alternative as it would duplicate the service
configuration syntax and semantic for the human deployer.
Also it would move OpenStack service specific logic to the
dataplane-operator, and therefore OpenStack component specific knowledge
would be split across multiple operators.

* Decouple the service CRDs from the DataPlane CRDs as in the main proposal
but instead of watching the DataPlaneNode CRs to trigger the auto creation
of the relevant service CR, the DataPlaneNode - Service CR connection is
defined by DataPlaneNode CR names part of the OpenStackControlPlane CR.
E.g. the `OpenStackControlePlane.Spec.Nova.Template.CellTemplate` can contain
a list of DataPlaneNode names to define that the given DataPlaneNode are
compute nodes and therefore NovaExternalCompute CR creation is needed.

This alternative would remove the need for the watch in the service operator
and therefore remove some of the complexity and performance overhead of of
the main proposal. However it would mean that the OpenstackControlPlane CR
contains data plane related information.

## Implementation considerations

The nova-operator will have a new controller that will watch DataPlaneNode CRs,
therefore the controller will Reconcile when a DataPlaneNode CR is created or
updated. The controller will look up the CR and checks for the
`nova.openstack.org/cell` annotation. If it is not present then the reconcile
ends. If the annotation is present then the controller will CreateOrPatch a
NovaExternalCompute CR based on the fields in the DataPlaneNode CR and the
fields of the NovaCell CR pointed by the `nova.openstack.org/cell`.

The existing controller for NovaExternalCompute will reconcile the CR as today
but it will wait for the ReadyCondition of the DataPlaneNode CR before creating
the AnsibleEE CRs to deploy compute services on the EDPM node.

0 comments on commit 52167c4

Please sign in to comment.