Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added design/img/Keycloak-operator-components.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added design/img/Keycloak-operator-layers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added design/img/Keycloak-operator-loop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 154 additions & 0 deletions design/operator-architecture.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Keycloak Operator High Level Architecture

* **Status**: Draft #1
* **JIRA**: https://issues.redhat.com/browse/KEYCLOAK-10319[KEYCLOAK-10319]
* **Source of the images**: https://docs.google.com/presentation/d/13N5ClXXXcxKjXgor72rUdtMBN07_GneUkhTTwBsy_tE/edit?usp=sharing[Google Slides]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this link as it is not public


This document shows a big picture how all the components and layers of Keycloak Operator work together. The blueprint presented here shall be used a guideline for designing features as well as integrating community Pull Requests.

## Manifesto

We envision Keycloak Operator as:

* an opinionated way of installing and maintaining Keycloak installation on both Kubernetes and OpenShift.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should upgrades be called out or is that covered under maintaining?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm I see it is covered

* a way to deliver a high level usability rather than low-level toggles
* an example of a recommended way of installing, configuring, managing custom extension and themes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this line.

* the thinnest possible layer, with the least possible logic, on top of Keycloak Container and Keycloak Server.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand what you mean, but this could also be interpreted as just dumping the exported config into map[string]string.

Do you envision being able to set up and configure keycloak just by reading the documentation/CRD and creating a CRD, instead doing the setup in the UI and exporting the configuration (including the "defaults" that are included in the export)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our CRDs reflect Keycloak resources. A good example is RealmRepresentation JSON payload and KeycloakRealm CRD. They are very similar and, in the long-term, should be the same.

So it is perfectly doable to configure Keycloak purely through CRDs (without touching the UI).


## Use cases

Our vision for Keycloak Operator is driven by use cases. The use cases mention the following personas:

* Cluster Admin - a Kubernetes/OpenShift administrator
* Service Infrastructure Admin - an administrator who provisions Keycloak Operator as well as Keycloak Cluster
* Realm Admin - a Keycloak administrator, who can provision realms (both using Custom Resources and Admin UI)
* Secured Application - either configured by an engineering team (using self registration feature) or by Keycloak Admin (or Keycloak Realm Admin).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fully convinced about these personas. Looking at the doc I sent you there are:

  • Cluster Admin
  • Service Infrastructure Admin
  • Service Admin
  • Service Consumer
  • Service End User

I think those are better personas then those you've created. From my perspective the description in context of the Keycloak Operator would be something like:

  • Cluster Admin - Provisions cluster resources. Basically, installs the Keycloak Operator.
  • Service Infrastructure Admin - Provisions the managed services. Basically, creates the Keycloak CR.
  • Service Admin - Manages the Realm. Basically, create the KeycloakRealm CR, but can also manage it through the Keycloak admin console
  • Service Consumer - Application developers. Basically, creates the KeycloakClient CR, but can also create clients through other means (admin console, client registration cli, etc.)
  • Service End User - Application users. Not sure there are any use-cases for these in this context, but worth mentioning anyways


The use cases we defined are based on Operatr Capability Model:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator, not Operatr


image:https://raw.githubusercontent.com/operator-framework/operator-sdk/master/doc/images/operator-capability-level.png[Operator Capability Model]

.Keycloak Operator use cases
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to agree on personas before going through the use-cases in more details.

[caption=]
[options="header",cols="^.^,^.^,^.^,^.^"]
|====
|Persona |Capability |Use case| Addressed?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed perhaps change this to Supported or Implemented? Not sure if that's what you mean by Addressed.


|Cluster Admin |Basic Install | Define Pod Disruption Budget to upgrade Kubernetes cluster| Yes
|Cluster Admin |Basic Install | Use Keycloak as a default IdP in Kubernetes| Yes
|Cluster Admin |Basic Install | Use Keycloak as a default IdP in OpenShift| No
|Cluster Admin |Full Lifecycle| Configure Storage Class for Local Backups| Yes

|Service Infrastructure Admin |Basic Install | Install Keycloak Operator | Yes
|Service Infrastructure Admin |Basic Install | Install and manage Keycloak installation | Yes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Install and manage Keycloak installation seems a bit broad. What does it cover?

|Service Infrastructure Admin |Basic Install | Install Keycloak in offline mode | No
|Service Infrastructure Admin |Basic Install | Apply custom theme for the organization | Yes
|Service Infrastructure Admin |Basic Install | Install extensions | Yes
|Service Infrastructure Admin |Seamless Upgrades | Upgrade installed Keycloak (automatically and on demand) with no downtime | Partially
|Service Infrastructure Admin |Seamless Upgrades | Create automatic backup before migrating to the new version| No
|Service Infrastructure Admin |Full Lifecycle | Create database backup| Yes
|Service Infrastructure Admin |Full Lifecycle | Recover Keycloak from a database backup| No
|Service Infrastructure Admin |Deep Insights | Monitor Keycloak health| Yes
|Service Infrastructure Admin |Deep Insights | Monitor Keycloak performance| Yes

|Realm Admin |Basic Install | Provision new Realm, Client and User| Yes
|Realm Admin |Basic Install | Secure an application| Yes
|Realm Admin |Basic Install | Access Admin Console| Yes
|Realm Admin |Basic Install | Provide transparent secure proxy (Gatekeeper) for third party applications| Yes
|Realm Admin |Seamless Upgrades | Zero downtime upgrades| No
|Realm Admin |Full Lifecycle | Backup created resources periodically| Yes
|Realm Admin |Deep Insights | Analyze usage and performance metrics| Partially
|Realm Admin |Autopilot | Scale Keycloak automatically depending on traffic| No

|Secured Application |Basic Install | Accessing Keycloak using a secured connection| Yes
|Secured Application |Seamless upgrades | Accessing Keycloak during the upgrade process| No
|Secured Application |Full Lifecycle | Keep session secured even when facing a disaster| Partially
|Secured Application |Autopilot | Provide stable response time despite the load| No
|====

## History
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just remove the History section - this is irrelevant information.


Keycloak Operator was re-designed from the scratch by a small team withing RHMI and RHSSO groups. The implementation plan was described in the link:operator.md[Initial Operator Design Document]. At the end of a 3 sprint cycle, the team delivered a Level 4 Operator, that was pushed into the https://operatorhub.io/operator/keycloak-operator[Operatorhub]. Once the initial implementation was done, the Operator was handed over to the Keycloak/RHSSO Team.

After maintaining the operator for several months, it became necessary to create a High Level Design (and a long-term vision) how all Keycloak components work together.

## Layered architecture

Keycloak ecosystem consists of 3 layers:

* https://github.com/keycloak/keycloak-operator[Keycloak Operator]
* https://github.com/keycloak/keycloak-containers/tree/master/server[Keycloak Container Image]
* https://github.com/keycloak/keycloak[Keycloak Server]

image::img/Keycloak-operator-layers.png[Keycloak ecosystem layers]

Keycloak Operator uses Keycloak Container image, which in turn uses Keycloak Server bits. The goal is to implement features in the lowest possible layer, so that they get inherited by the all upper layers.

.Feature implementation example
****
Let's consider a simple feature - adding JSON formatted logs based on an environmental variable.

This feature requires touching all three layers:

1. Keycloak Server needs to provide a JSON log based formatter.
2. Keycloak Container needs to expose an environment variable, that could be switching on to use JSON formatting
3. Keycloak Operator needs to expose proper configuration on Custom Resource level enabling JSON log formatting.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the Operator actually be opinionated here and just use JSON logging since that is the recommended on OpenShift/Kubernetes to have a common/parsable logging format?


The basic rule here is to push individual features as low in the stack as possible. In this example, providing JSON log extension only in the Operator would be a mistake.
****

## Components created by the Operator

The diagram below presents the most important connections between components:

image::img/Keycloak-operator-components.png[Keycloak Operator components]

When Keycloak Operator spins up Keycloak installation, it creates:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should make it a bit more clear in the list what types the things below are. For example keycloak-postgres is listed twice, once it doesn't say what type it is, then in the second it says in the description it's a service. Then for keycloak-discovery it more clearly says it's a Service. I would use the format: name type - description for everything


* `keycloak-db-secret` - used to store database username, password and other properties, such as external address (if used).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do find it a bit weird that the external address for the DB is stored within a secret ;)

* `credentials-<<CR Name>>` - Admin user credentials to log into Keycloak installation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on this one? I don't understand what this is used for.

* `keycloak` StatefulSet with HA support
* `keycloak-postgresql` - Responsible for spinning up Postgresql deployment
* `keycloak-discovery` Service - used for `JDBC_PING` discovery
* `keycloak` Service - Used for connecting Keycloak using HTTPS (HTTP is not allowed)
* `keycloak-postgresql` - A service for connecting both internal and external (if used) database instance
* `keycloak` Route (OpenShift) or Ingress (Kubernetes) - Used for accessing Keycloak

As you probably noticed, `MyKeycloak` name (used for `Keycloak` Custom Resource) appears only once - in `credential-<<CR Name>>`. Since the Operator allows to install only a single Keycloak cluster in a namespace, we always call it `keycloak`.

NOTE: Hardcoding names may change in the future, but at the time of writing this design, we have no plans to do it.

## Managed Custom Resources

Keycloak Operator uses the following Custom Resources:

* `Keycloak` - Responsible for spinning up Keycloak installation
* `KeycloakRealm` - Managing Keycloak Realms
* `KeycloakClient` - Managing Keycloak Clients
* `KeycloakUser` - Managing Keycloak Users

Some of the Custom Resources, such as `KeycloakClient` or `KeycloakUser` require a reference to a `KeycloakRealm`. This mapping has been implemented by using `LabelSelector` (the same object used by Kubernetes Services for example). The selector needs to be configured in such a way, that is points one or many realms.

## Internal loop

One of the key design decisions we made is to separate Kubernetes client code from our Operator logic. This allows us to test most of the Keycloak Operator interactions in unit tests. Every Controller uses the following pattern:

image::img/Keycloak-operator-loop.png[Keycloak Operator loop]

Every loop (triggered by the Operator SDK) starts with collecting the `Current State`. This object contains a list of Kubernetes resources used by the Operator (such as Keycloak `StatefulSet`, Keycloak `Service` etc). Then, this list is passed to a `Reconciler` object, that produces a list of `Actions`. `Actions` are a description of a piece of work in the Kubernetes cluster (e.g. create a Keycloak Service). All those objects are aggregated into a single list and called `Desired State`. Finally, we pass this list into an `Action Runner` that interacts with Kubernetes cluster and creates or updates all objects.

This approach has a lot of benefits, including:

* We can test `Reconciler` logic using unit tests as we don't need to communicate with Kubernetes.
* All Custom Resource loops look the same - we collect the `Current State`, reconcile it into a `Desired State` and finally, we pass it to an `Action Runner`.

## Implementation guideline

* **Supporting both OpenShift and Kubernetes is a must** - every feature must work in both usecases correctly.
* **We prefer convention over configuration** - we hardcode secret names, so that they can be easily figured out by automated scripts.
* **Do not use mocks** - we just hate them... and with our approach to the architecture, they are completely unnecessary.
* **We prefer unit tests over e2e tests** - although we do need both, we prefer testing features on the lowest possible layer.

## Further reading

* https://github.com/operator-framework/community-operators/blob/master/docs/best-practices.md[Operator Best Practices]
2 changes: 1 addition & 1 deletion design/operator.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Observerability
# Initial Operator Design

* **Status**: Notes
* **JIRA**: [KEYCLOAK-10036](https://issues.jboss.org/browse/KEYCLOAK-10036)
Expand Down