|
| 1 | +# Usage of a relational database, as described in the architecture document, rather than storing configuration/state in Kubernetes resources |
| 2 | + |
| 3 | +Written by: |
| 4 | +- Jonathan West (@jgwest) |
| 5 | + |
| 6 | + |
| 7 | +**Using a relational database does not prevent users from managing the GitOps managed service with Kubernetes CRs** |
| 8 | + |
| 9 | +Using a relational database to track the internal managed entities of the GitOps managed service does NOT prevent us from allowing users to interact with the service via Kubernetes CRs. |
| 10 | + |
| 11 | +For example, you can imagine a case where the user creates something like this: |
| 12 | + |
| 13 | +```yaml |
| 14 | +apiVersion: redhat.com/managed-gitops/v1alpha1 |
| 15 | +kind: ManagedEnvironmentCredentials |
| 16 | +metadata: |
| 17 | + name: my-cluster-credentials |
| 18 | +spec: |
| 19 | + credentials: |
| 20 | + // (…) |
| 21 | +``` |
| 22 | +
|
| 23 | +
|
| 24 | +E.g. they have a cluster they want to use Argo CD to deploy to, and so they provide us credentials using a custom CR.t |
| 25 | +
|
| 26 | +While support for this is not part of first managed-gitops iteration, it would be straightforward to add support for this: |
| 27 | +
|
| 28 | +- The gitops-backend service would watch user namespaces for the ManagedEnvironmentCredentials CR |
| 29 | +- On creation, the gitops-backend sees this new CR |
| 30 | +- It then updates the corresponding Managed Cluster Environment in the RDBMS database (and informs the cluster agent of the change). |
| 31 | +
|
| 32 | +Thus, using something like [kcp](https://github.com/kcp-dev/kcp), it is straightforward to extend the architecture to add a controller to watch K8S APIs on a kcp control plane, and to respond to these with events in the relational database and on the cluster. (In fact, this would just be a replacement for the REST API of the backend service, replacing it with a controller watching a KCP cluster) |
| 33 | +
|
| 34 | +
|
| 35 | +
|
| 36 | +**Suitability for providing fast responses to UI requests** |
| 37 | +
|
| 38 | +In our case, the persistence store (database) needs to be read by a number of different components, including the backend, and cluster operator. |
| 39 | +
|
| 40 | +The backend, specifically, will be responsible for serving UI requests, and (as with any UI) we need the page load times for those UI requests to be as fast as possible. This means we need to store the content with a form of persistence which is lightweight, performant, and scalable. |
| 41 | +
|
| 42 | +For instance, one case we have is we might need to retrieve the sync/health status of hundreds of user Argo CD Applications, at once, as quickly as possible. My expectation is that an RDBMS like Postgres (or Redis) would work better for this purpose than the Kubernetes API server. |
| 43 | +
|
| 44 | +**Transactions**: |
| 45 | +
|
| 46 | +There are a number of resources that we will be required to manage, and these resources may necessarily contain references to one another that need to be kept in sync. |
| 47 | +
|
| 48 | +The easiest way to ensure a set of resources are consistently updated together is with RDBMS transactions. In contrast, Kubernetes does not have the ability to bulk update a set of resources, which means you have to update them one at a time (perhaps over a number of threads). This ‘one at a time’ approach potentially leads to incomplete reads from consumers, where some of the resources have been updated and some have not (an inconsistent state). |
| 49 | +
|
| 50 | +**SQL (Efficient use of network bandwidth, efficient joins)**: |
| 51 | +
|
| 52 | +With an RDBMS, you may use SQL to exactly shape the specific SELECT query parameters you are looking for. For example, if you are only looking for the ‘username’ and ‘cluster id’ fields of a table, you may request these in a SELECT query, and discard the others. |
| 53 | +
|
| 54 | +In contrast, with Kubernetes you must acquire the entire resource contents (spec, status, etc) in order to see the contents of any individual fields (though you do have a limited ability to filter which resources are returned). |
| 55 | +
|
| 56 | +Likewise, with SQL JOINs, you can combine the results of multiple tables together; in contrast, with the Kubernetes API, you would need to retrieve each individual entry and join it yourself, which increases the CPU usage and network I/O. |
| 57 | +
|
| 58 | +- This is especially true, when defining secondary indexes on fields of a database, for speeding up queries that are not based (strictly) on the primary key. |
| 59 | +
|
| 60 | +**Backup / Disaster Recovery**: |
| 61 | +
|
| 62 | +There are many tools, documents, processes, and cloud services available for backing up and restoring a PostgreSQL database. While it is reasonably straightforward to backup and restore the YAML of a Kubernetes server, it requires a more hands on approach vs PostgreSQL (potentially introducing errors into the process). |
| 63 | +
|
| 64 | +**Foreign Keys**: |
| 65 | +
|
| 66 | +It’s beneficial to be able to rely on the consistency of relationships between entities in the database, as reinforced through foreign keys. |
| 67 | +
|
| 68 | +**Strong typing:** |
| 69 | +
|
| 70 | +I’m personally a fan of strongly typed schemas: using tables to define a strict set of data types, rather than unstructured, schema-less JSON, traditionally used in a NoSQLDB. |
| 71 | +
|
| 72 | +**RDBMS databases are the traditional tool for this job** |
| 73 | +
|
| 74 | +In all software engineering projects, we should aim to use ‘the right tool for the job’. We should ask: what technology is specialized for this purpose? And, which technology has the industry had success using to implement similar projects? |
| 75 | +
|
| 76 | +RDBMS databases are the traditional tool for creating, querying, and updating a list |
| 77 | +
|
| 78 | +of interrelated entities, in a durable, consistent, reasonably performant manner. |
| 79 | +
|
| 80 | +Imagine I am a software developer working for a bank, which is creating a backend bank app. That app needs to store a list of customers, and a list of credit ratings. In all likelihood I’m going to do it using an RDBMS. I’m not going to use a Kubernetes namespace with CreditRating and Customer CRs. This use case is no different. |
| 81 | +
|
| 82 | +We as Kubernetes/OpenShift developers are very familiar with the ins and outs of Kubernetes resources, and the Kubernetes control plane, but as the saying goes, “When all you have is a hammer, every problem starts to look like a nail”. |
| 83 | +
|
| 84 | +
|
| 85 | +**“Easier” to move from RDBMS, to other databases (NoSQL/Redis), than the vice versa** |
| 86 | +
|
| 87 | +Since RDBMS are strongly typed, with foreign key constraints, it is “easier” to move from an RDBMS, to another database technology (Redis, NoSQL), than to go in the opposite direction. |
| 88 | +
|
| 89 | +For example, if we wanted the benefits of better scaling offered by a key-value store database (such as Redis or etcd) it is straightforward to migrate the appropriate database tables with a simple translation script. |
| 90 | +
|
| 91 | +Likewise, moving to NoSQL (Mongo) would be a matter of a simple translation script that iterated through the tables, to produce the corresponding JSON objects. |
| 92 | +
|
| 93 | +(I say ‘easier’, because databases also have difference semantics, for example ACID vs CAP and eventual consistency, which may break the product code in non-obvious ways) |
0 commit comments