-
julian.baeume authored
Changelog: - use PROPAGATE_UPGRADES env variable to defer update events to core-ui-mw pods - set to `false` to only warm up cache - re-deploy with set to `true` to actually roll out the upgrade
julian.baeume authoredChangelog: - use PROPAGATE_UPGRADES env variable to defer update events to core-ui-mw pods - set to `false` to only warm up cache - re-deploy with set to `true` to actually roll out the upgrade
UI Middleware
Provides the collected manifest.json of services in a cluster as well as a list of dependencies for each source file. This information can be used to dynamically compile all ui components at runtime in the browser. Besides serving the information about all resources, this service can also function to provide snapshots of all the resources available at a specific point in time.
Architecture
The UI Middleware is, as the name already suggests, a middleware component to support the App Suite UI. As an installation of App Suite UI can consist of multiple projects, like the Core App Suite UI, Documents UI, OX Guard UI, and other custom plugins, those components need to be combined at some point to be served to the browser. This is handled by the ui-middleware component, by putting it between the Ingress Controller and the containers serving the individual components that make up the App Suite UI.
If you prefer a picture, this is, how such an installation can look like.
Prerequisites
- A Redis server is required (standalone, sentinel and cluster mode supported)
Deployments
Kubernetes
cd helm
helm upgrade -i -f ui-middleware/values.yaml -f values/develop.yaml ui-middleware ./ui-middleware
It is possible to horizontally scale the UI Middleware, as more clients are fetching resources from this service. Each instance maintains a cache of all files locally (in memory), allowing to scale easily.
Configuration
Helm Variable (values) | Environment Variable | Description | Default |
---|---|---|---|
port |
PORT |
Exposed port | "8080" |
cacheTTL |
CACHE_TTL |
Vite manifest caching time | 30000 |
logLevel |
LOG_LEVEL |
Pino log level | "info" |
' updater.propagateUpgrades ' |
PROPAGATE_UPGRADES |
Propagate upgrades to pods, use to orchestrate updates | true |
redis.mode |
REDIS_MODE |
Redis mode (standalone, sentinel or cluster) | "standalone" |
redis.sentinelMasterId |
REDIS_SENTINEL_MASTER_ID |
Name of the sentinel masterSet |
"mymaster" |
redis.hosts |
N/A (see below) | Redis hosts as list | ["localhost:6379"] |
N/A (see above) | REDIS_HOSTS |
Redis hosts as string | "localhost:6379" |
redis.db |
REDIS_DB |
Redis DB, e.g. "1"
|
null |
redis.prefix |
REDIS_PREFIX |
Redis prefix | "ui-middleware" |
redis.auth.enabled |
N/A | Generate redis auth secret | false |
redis.username |
REDIS_USERNAME |
Redis username | "" |
redis.password |
REDIS_PASSWORD |
Redis password | "" |
redis.sidecar.image |
N/A | Redis sidecar image | "redis:latest" |
redis.tls.enabled |
REDIS_TLS_ENABLED |
Enable TLS for Redis | false |
redis.tls.ca |
REDIS_TLS_CA |
PEM version of redis server CA certificate | "" |
compressFileSize |
COMPRESS_FILE_SIZE |
Larger files will be gzipped | 600 |
compressFileTypes |
COMPRESS_FILE_TYPES |
Set of compression mime types | application/javascript application/json application/x-javascript application/xml application/xml+rss text/css text/html text/javascript text/plain text/xml image/svg+xml |
slowRequestThreshold |
SLOW_REQUEST_THRESHOLD |
Slow request threshold in ms | 4000 |
Config Map
# List of urls where to find ui containers
baseUrls:
- http://service.namespace.svc.cluster.local/
# optional, when this number is changed, the changes on clients will be invalidated and will reload the ui-sources
salt: '1234'
Ingress
When using Ingress to make the service available to the public, the service is intended to be configured as a "fallback" or "default" service to answer all requests that are not specified to be served by one of the other services (e.g like everything under /api). This should simplify the Ingress configuration. One example can be found in the ui-middleware repository.
Some notes on code loading
It is mandatory to load the UI over https with a valid certificate and there are multiple reasons for that. These notes give an overview over the features that are required for code loading and will substantiate the need for https.
- First of all, securing your site with https is not hard anymore. We have e.g. Let's Encrypt and several other ways to achieve this. But which case applies to a use-case is not part of these notes.
- The UI needs a service worker to function. The service worker is used for file-caching and version-mismatch detection (the "reload"-banner). Service workers are only loaded over https with valid certificate to prevent man-in-the-middle attacks.
- The UI consists of many small files, because it cannot be bundled as before. To improve file transfer time, HTTP/2 is required which requires https.
- The service worker also increases boot time by loading zipped chunks of files and puts them in the browser-cache on initial boot. Without https no service worker, without service worker no zipped loading.
- To reduce the transferred file-size, the UI-middleware uses brotli encoding for every transferred file (except the index.html). The UI-middleware will not check for accept-encoding headers due to performance reasons. Modern browsers will not load brotli over insecure connections and the UI won't load without it.
It is noteworthy that localhost is an exception to the above rules and the UI including service workers and preloading (but not http2) will work on localhost for development environments.
Redis default setup
For setups where scaling of the UI middleware is not necessary you can use the default values of the helm chart, which will deploy a redis server as a sidecar container.
Orchestrate Updates
In some situations it is necessary to orchestrate updates of the UI service. For instance if there are multiple independent installations that all should be able to ship the same version of the UI at any given time. Think of active/active setups where users could be routed between different sites.
This scenario can be covered by setting the updater.propagateUpgrades
to false
and deploy new versions of services with UI sources during an upgrade.
The UI service will then warm up the cache but still use the old version of the UI sources as the current one.
Once all installations are prepared and the update should be rolled out to the user, just re-deploy with
the setting updater.propagateUpgrades
set to true
. The updater of the UI service will then propagate the new version.
This can be done at any point in time for each installation, as all installations will be able to ship all possible versions of the UI.
Orchestrate using kubectl patch
It's also possible to manually patch the updater deployment, once propagation of the upgrade is desired.
# Get current environment variables, we are looking for PROPAGATE_UPGRADES
kubectl -n $KUBE_NAMESPACE get deployments.apps my-core-ui-middleware-updater -o yaml | yq '.spec.template.spec.containers.0.env'
# Dry run the patch operation on the deployment to set PROPAGATE_UPGRADES to "true", verify the output
kubectl -n $KUBE_NAMESPACE patch deployments.apps my-core-ui-middleware-updater --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env/0/value", "value":"true"}]' --dry-run=client -o yaml | yq '.spec.template.spec.containers.0.env'
# Apply the patch operation to set PROPAGATE_UPGRADES to "true"
kubectl -n $KUBE_NAMESPACE patch deployments.apps my-core-ui-middleware-updater --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env/0/value", "value":"true"}]'