mirror of
https://github.com/suitenumerique/docs.git
synced 2026-09-24 02:25:08 +02:00
✨(helm) deploy new infra using helm
The new infra we have must be configured in the helm chart. This commit all the missing templates to deploy yhub, it also automate the creation of the private keys needed by all services.
This commit is contained in:
@@ -1,12 +1,65 @@
|
||||
# Collaboration
|
||||
|
||||
By default with Docs, collaboration is enabled. To allow the collaboration between users, a connection to a websocket server is made (the y-provider service), you only have to configure the Django backend URL and the allowed origin in your y-provider service:
|
||||
By default with Docs, collaboration is enabled. To allow the collaboration between users, a connection to a websocket server is made (the yhub service), you only have to configure the Django backend URL and the allowed origin in your yhub service:
|
||||
|
||||
```yaml
|
||||
COLLABORATION_BACKEND_BASE_URL: https://{yourdocsdomain.tld}
|
||||
COLLABORATION_SERVER_ORIGIN: https://{yourdocsdomain.tld}
|
||||
```
|
||||
|
||||
The collaboration server keeps the live state of a document in Redis and persists it to a PostgreSQL database of its own, so it needs both:
|
||||
|
||||
```yaml
|
||||
REDIS: redis://{redis-host}:6379/0
|
||||
POSTGRES: postgres://{user}:{password}@{postgres-host}:5432/yhub
|
||||
```
|
||||
|
||||
Nothing creates that schema at startup: the server never runs DDL. Run the script yhub ships (`npm run init-db`, which the helm chart runs as a job) once before starting it, and again after every upgrade that adds a table. It creates the database when it is missing, it is idempotent, and until it has run every document read fails with `relation "..." does not exist`.
|
||||
|
||||
The Django backend reads and writes document content there too, so point it at the service:
|
||||
|
||||
```yaml
|
||||
YHUB_API_BASE_URL: http://{yhub-service}:443
|
||||
```
|
||||
|
||||
Prefer the internal service url: the routes the backend calls are not meant to be reachable from the outside. Route `/collaboration/ws/` to the service publicly — that is the one the browsers open — plus the document routes (`/collaboration/ydoc/`, `rollback`, `prune`, `changeset`, `activity`) and `/collaboration/jwks/`, which carries public keys and nothing else. Keep `reset-connections`, `migrate`, `restore-ydoc`, `reset-ydoc` and `create-ydoc` in-cluster.
|
||||
|
||||
Both directions are authenticated with short-lived RS256 JWTs rather than a shared secret, and each side verifies the other against the JWKS it publishes — so both need a signing key of their own, and neither needs a copy of the other's:
|
||||
|
||||
```yaml
|
||||
# Django
|
||||
JWT_PRIVATE_KEY_FILE: /path/to/backend-private.pem
|
||||
# yhub
|
||||
YHUB_JWT_PRIVATE_KEY_FILE: /path/to/yhub-private.pem
|
||||
```
|
||||
|
||||
They are ordinary PKCS#8 RSA keys (`openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048`), and rolling one needs no change on the other side. Without them the documents still open and edit, but the backend cannot create, delete or restore a document's content, and yhub cannot tell it that a document changed — its `updated_at` stops following the edits.
|
||||
|
||||
### Generating them on the cluster
|
||||
|
||||
The helm chart generates both for you, so that no key has to be created by hand, put in a values file or in a secret:
|
||||
|
||||
```yaml
|
||||
jwtKeys:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
A job then creates the two keys, once, in a secret every service mounts read-only, and points the backend and yhub at them. It generates them with `openssl` in a pod-local volume and hands them to `kubectl create secret`, so they never touch a disk, a manifest or a values file. The secret is left alone when it is already there, so the job is safe to re-run — it runs on every sync — and rolling the keys is deleting the secret and letting the next run create it again. Both sides follow: they pick the verification key by its `kid` and fetch the set again when they meet one they do not know.
|
||||
|
||||
The job is the only thing allowed near that secret: the chart gives it a service account whose role can `create` a secret and read whether that one exists, nothing more. The services never call the kubernetes API — they read a mounted file. The secret is not part of the release either, so uninstalling keeps the same identities; delete the secret to start over.
|
||||
|
||||
Deployments already holding their keys in a secret of their own point the chart at it instead, and the job and its rights are not created at all:
|
||||
|
||||
```yaml
|
||||
jwtKeys:
|
||||
enabled: true
|
||||
existingSecret: my-jwt-keys # holding private.pem and yhub-private.pem
|
||||
```
|
||||
|
||||
Setting `JWT_PRIVATE_KEY_FILE` or `YHUB_JWT_PRIVATE_KEY_FILE` yourself keeps priority over what the job provides, so a deployment holding its keys in a secret of its own can leave `jwtKeys` disabled and mount them where it wants.
|
||||
|
||||
Several replicas can serve the same document: they exchange updates through Redis, so no sticky routing is needed on the websocket ingress.
|
||||
|
||||
## What happens when connection to the websocket is not allowed?
|
||||
|
||||
When multiple users access a Docs and the connection to the websocket is not allowed, then they will be in a situation where they can lose data.
|
||||
|
||||
@@ -67,7 +67,9 @@ backend:
|
||||
AWS_STORAGE_BUCKET_NAME: docs-media-storage
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
USER_RECONCILIATION_FORM_URL: https://docs.127.0.0.1.nip.io
|
||||
Y_PROVIDER_API_BASE_URL: http://impress-y-provider:443/api/
|
||||
# the collaboration server, reached in-cluster
|
||||
YHUB_API_BASE_URL: http://impress-docs-yhub:443
|
||||
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider:443/api/
|
||||
Y_PROVIDER_API_KEY: my-secret
|
||||
CACHES_KEY_PREFIX: "{{ now | unixEpoch }}"
|
||||
migrate:
|
||||
@@ -135,6 +137,26 @@ yProvider:
|
||||
COLLABORATION_LOGGING: true
|
||||
COLLABORATION_SERVER_ORIGIN: https://docs.127.0.0.1.nip.io
|
||||
|
||||
# The collaboration server: it serves everything under /collaboration/, the
|
||||
# websocket included. It keeps the live state of a document in redis and
|
||||
# persists it to a PostgreSQL database of its own, created by the init-db job
|
||||
# the chart ships — give the user in POSTGRES the right to create it, or create
|
||||
# the database yourself beforehand.
|
||||
yhub:
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: lasuite/impress-yhub
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
envVars:
|
||||
POSTGRES: postgres://dinum:pass@postgresql-dev-backend-postgres:5432/yhub
|
||||
REDIS: redis://user:pass@redis-dev-backend-redis:6379/2
|
||||
REDIS_PREFIX: yhub
|
||||
COLLABORATION_BACKEND_BASE_URL: https://docs.127.0.0.1.nip.io
|
||||
COLLABORATION_SERVER_ORIGIN: https://docs.127.0.0.1.nip.io
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: docs.127.0.0.1.nip.io
|
||||
|
||||
@@ -26,33 +26,17 @@ COLLABORATION_BACKEND_BASE_URL: http://{django-service}:8000
|
||||
|
||||
The JWKS url defaults to `{COLLABORATION_BACKEND_BASE_URL}/api/v1.0/jwks`; override it with `JWKS_URL` if Django is not reachable at that base url from the y-provider service.
|
||||
|
||||
### Splitting conversion service
|
||||
### One service, not two anymore
|
||||
|
||||
The conversion service is present in the `y-provider` server. The same server used to manage websockets. You can split in one side the websocket server and in an other side the converter service.
|
||||
This feature is only available in our helm chart, if you are deploying an other way you can take example of what is made to implement it.
|
||||
The idea is to deploy twice the `y-provider` server, one dedicated for websockets and one dedicated to the conversion.
|
||||
The `y-provider` server used to serve the websockets as well, which is why it could be deployed twice — one release for the collaboration, one for the conversion (`yProvider.converter`). The collaboration is served by [yhub](collaboration.md) now, so the conversion is all that is left: the `y-provider` service **is** the converter, and the `yProvider.converter` values are gone.
|
||||
|
||||
In the helm chart, you can use this value that will do the job for you:
|
||||
|
||||
```yaml
|
||||
yProvider:
|
||||
converter:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
Every parameter in the `yProvider` key can be overridden in the `yProvider.converter` key.
|
||||
|
||||
Once enabled, you have to enable the `Y_PROVIDER_API_BASE_URL` with the url of the newly created service, it is the same as before with `-converter` at the end.
|
||||
If before it was
|
||||
|
||||
```yaml
|
||||
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider:443/api/
|
||||
```
|
||||
|
||||
now it is
|
||||
A deployment coming from a chart older than this one has one thing to change, the url the backend calls, which loses its suffix:
|
||||
|
||||
```yaml
|
||||
# before
|
||||
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider-converter:443/api/
|
||||
# now
|
||||
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider:443/api/
|
||||
```
|
||||
|
||||
## Docspec configuration
|
||||
|
||||
@@ -226,6 +226,7 @@ impress-docs-backend-8494fb797d-8k8wt 1/1 Running 0 6m45s
|
||||
impress-docs-celery-worker-764b5dd98f-9qd6v 1/1 Running 0 6m45s
|
||||
impress-docs-frontend-5b69b65cc4-s8pps 1/1 Running 0 6m45s
|
||||
impress-docs-y-provider-5fc7ccd8cc-6ttrf 1/1 Running 0 6m45s
|
||||
impress-docs-yhub-6d84f9b7c5-2xqzp 1/1 Running 0 6m45s
|
||||
keycloak-dev-backend-keycloak-0 1/1 Running 0 24m
|
||||
keycloak-dev-backend-keycloak-pg-0 1/1 Running 0 24m
|
||||
minio-dev-backend-minio-0 1/1 Running 0 8m24s
|
||||
|
||||
Reference in New Issue
Block a user