OpenProject Common — Configuration applicative partagée
OpenProject_Common est la couche applicative partagée d'OpenProject. Elle n'est
pas déployée seule ; elle fournit la configuration propre à OpenProject sur laquelle
reposent à la fois OpenProject_GKE et
OpenProject_CloudRun, afin que les deux variantes de
plateforme se comportent de manière identique là où cela compte. Les utilisateurs finaux
ne configurent jamais cette couche directement — elle n'a aucune entrée propre dans
l'interface de déploiement — mais comprendre ce qu'elle fournit explique les valeurs par
défaut que vous voyez dans la documentation des plateformes.
Pour l'infrastructure qui provisionne et exécute réellement OpenProject, consultez les guides de plateforme (OpenProject_GKE, OpenProject_CloudRun) et les guides du socle (App_GKE, App_CloudRun, App_Common).
1. Ce que fournit cette couche
| Domaine | Fourni par OpenProject_Common | Où cela apparaît |
|---|---|---|
| Secret cryptographique | Génère SECRET_KEY_BASE (64 octets aléatoires → 128 caractères hexadécimaux) et le stocke dans Secret Manager | Injecté automatiquement ; à récupérer via Secret Manager (voir ci-dessous) |
| Image de conteneur | Enveloppe l'image tout-en-un officielle openproject/openproject avec un cloud-entrypoint.sh personnalisé ; build via Cloud Build | Sortie container_image du déploiement de la plateforme |
| Moteur de base de données | Impose Cloud SQL for PostgreSQL 15 comme seul moteur pris en charge | §Base de données dans les guides de plateforme |
| Amorçage de la base de données | Définit les jobs du premier déploiement (db-init → db-migrate) qui créent le rôle/la base de données puis exécutent rake db:migrate db:seed | Sortie initialization_jobs |
| Paramètres de base | Définit l'environnement de base d'OpenProject : RAILS_ENV, mode HTTPS, journalisation sur STDOUT, exécution asynchrone de good_job, dimensionnement du pool de connexions à la base de données | Comportement de l'application dans les guides de plateforme |
| Contrôles de santé | Définit des sondes de démarrage/vivacité HTTP par défaut (/health_checks/default) — OpenProject_CloudRun/OpenProject_GKE les remplacent par des sondes TCP pour contourner le Host Authorization de Rails ; assemble également un objet readiness_probe sans effet (ce n'est pas un champ d'entrée reconnu par App_CloudRun/App_GKE) | §Observabilité dans les guides de plateforme |
2. Le secret SECRET_KEY_BASE dans Secret Manager
Un unique secret est généré automatiquement et stocké dans Secret Manager — il n'est jamais défini en clair et ne doit jamais être modifié après le premier déploiement :
SECRET_KEY_BASE— 64 octets aléatoires rendus sous la forme d'une chaîne hexadécimale de 128 caractères. Rails l'utilise pour signer les sessions et les cookies et pour dériver la clé des colonnes chiffrées de la base de données. Il doit rester stable d'un redémarrage et d'un redéploiement à l'autre : sa rotation après le premier démarrage rend illisibles toutes les sessions existantes et toutes les données chiffrées. Le module le génère une seule fois (random_id) et le conserve dans Secret Manager, de sorte qu'il survit à la recréation des conteneurs, au dimensionnement et aux redéploiements.
Récupérez le secret après le déploiement :
# List secrets for this deployment (names include the resource prefix):
gcloud secrets list --project "$PROJECT" --filter="name~secret-key-base"
# Read a secret version:
gcloud secrets versions access latest --secret=<secret-name> --project "$PROJECT"
Le mot de passe de la base de données est généré et géré séparément par le socle ; le
nom de son secret est indiqué dans les sorties du déploiement de la plateforme
(database_password_secret). Consultez App_Common pour le modèle partagé
de secrets et de Workload Identity.
3. Moteur de base de données et amorçage
OpenProject nécessite PostgreSQL 15 ; le moteur est imposé et MySQL ou les autres moteurs ne sont pas pris en charge. Lors du premier déploiement, deux jobs ponctuels s'exécutent dans l'ordre :
db-init(avecpostgres:15-alpine) crée de manière idempotente le rôle et la base de données de l'application (propriétaire = l'utilisateur de l'application), en accordant les privilèges dont OpenProject a besoin.db-migrateexécute l'image personnalisée de l'application et lancerake db:migrate db:seed— ce qui crée le schéma complet d'OpenProject et le compte administrateur par défaut. Il s'exécute au moment de l'apply avec un délai d'expiration généreux (30 minutes) et la totalité du CPU, de sorte que le conteneur web démarre ensuite rapidement sur un schéma déjà migré.
Pourquoi un job de migration dédié (plutôt qu'une migration au démarrage) : le service
s'exécute en mode web uniquement (./docker/prod/web) pour éviter le conflit de ports
Apache+Puma de l'image tout-en-un, et le mode web uniquement ignore le seeder supervisé que
le point d'entrée tout-en-un exécuterait autrement. Rails (en production) refuse de
démarrer Puma tant que des migrations sont en attente (« You have N pending migrations » →
exit 1 → échec de la sonde de démarrage) ; les migrations doivent donc être terminées
avant le démarrage du service.
Le job est autovérifié et autoréparateur :
- Il exécute explicitement
rake db:migrate(et nondb:schema:load), car la purge du schéma effectuée parschema:loadeffacerait l'extensionpg_trgmet casserait les index trigrammes GIN ;db:migratecréepg_trgmviaenable_extensionavant les index. - Avant de migrer, il exécute
DROP OWNED BY CURRENT_USER CASCADEpour supprimer les éventuelles tables partielles laissées par une tentative précédente interrompue (lesAggregatedMigrationsconsolidées d'OpenProject ne sont pas transactionnelles), de sorte que chaque tentative démarre sur une base propre. - Si le seeder échoue, le schéma reste non migré et la création du service échoue de manière visible sur la protection contre les migrations en attente — aucune mise en production silencieuse avec une base de données vide.
Le job de migration définit OPENPROJECT_RAILS__CACHE__STORE = file_store (OpenProject
utilise memcached par défaut pour son cache, qui n'est pas présent dans le job ;
OpenProject valide ce paramètre par rapport à une liste fixe
{file_store, memcache, redis}).
Inspectez directement la base de données avec :
gcloud sql connect <instance-name> --user=<db-user> --database=<db-name> --project "$PROJECT"
Les noms de l'instance, de la base de données et de l'utilisateur figurent dans les sorties du déploiement de la plateforme.
4. Image de conteneur et point d'entrée
L'image personnalisée est une fine enveloppe FROM openproject/openproject:<version>. Le
seul ajout est cloud-entrypoint.sh, qui s'exécute avant le démarrage standard :
- Compose
DATABASE_URLà partir des variablesDB_*du socle. OpenProject (Rails) lit un unique DSN sous forme d'URL. Le point d'entrée choisit une branche selon la forme de l'hôte résolu et encode le mot de passe pour l'URL :- Socket Unix Cloud SQL (
/cloudsql/...) → forme socket de libpqpostgres://user:pass@/db?host=/cloudsql/<inst>(les deux-points du chemin du socket casseraient la partie autorité de l'URL ; il est donc placé dans?host=). - Loopback (
127.0.0.1— le sidecar Auth Proxy de GKE) → TCP simple, sans SSL. - Adresse IP privée (par défaut sur Cloud Run) →
sslmode=require(Cloud SQL rejette les connexions TCP non chiffrées par adresse IP privée). Cette URL est composée inconditionnellement — l'image amont intègre uneDATABASE_URLpar défaut pointant vers127.0.0.1; une protection du type « ne pas écraser si elle est déjà définie » serait donc fatale sur Cloud Run, où il n'existe pas de proxy en loopback.
- Socket Unix Cloud SQL (
- Définit
OPENPROJECT_HOST__NAMEetOPENPROJECT_HTTPSafin qu'OpenProject construise des URL absolues correctes derrière le front-end HTTPS de la plateforme. - Exécute le point d'entrée tout-en-un standard + le
CMD— sur cette plateforme, leCMDest./docker/prod/web(Puma uniquement) ;good_jobexécute donc son worker et son cron dans le processus viaGOOD_JOB_EXECUTION_MODE = async.
L'image utilise un ARG de build propre à l'application, OPENPROJECT_VERSION (et non le
APP_VERSION générique que le socle injecte et qui l'écraserait). OpenProject ne
publie que des tags de version majeure numériques (16, 15, …) et aucun tag latest ;
la valeur par défaut de la campagne "latest" est donc épinglée sur la version majeure
stable 16.
5. Paramètres principaux de l'application
OpenProject_Common établit l'environnement de base afin que l'application démarre
correctement dès le premier lancement :
RAILS_ENV = production.OPENPROJECT_HTTPS = tostring(var.https_enabled)— combiné àOPENPROJECT_HOST__NAME(défini par le point d'entrée), il produit des URL absolues correctes, maishttps_enabledn'est pas fixé àtrue:OpenProject_CloudRuntransmet toujourstrue(une URL Cloud Run*.run.appest toujours en HTTPS), tandis queOpenProject_GKEtransmetvar.enable_custom_domain— le mode HTTPS ne s'active qu'une fois un domaine personnalisé et un certificat géré configurés. Forcertruesur GKE sans domaine personnalisé redirigerait de force chaque requête vers une adressehttps://qui ne répond jamais (une panne totale et silencieuse) ; c'est pourquoi la variante transmet cette valeur de manière conditionnelle au lieu de l'imposer en dur.RAILS_LOG_TO_STDOUT = true— émet les journaux pour Cloud Logging / GKE.GOOD_JOB_EXECUTION_MODE = async— les jobs en arrière-plan (e-mails, notifications, travaux planifiés) s'exécutent dans le processus, à l'intérieur du conteneur web. Il n'y a pas de Redis :good_jobgère sa file d'attente dans PostgreSQL ; le module transmet doncenable_redis = false.RAILS_MAX_THREADS = 5— le pool de connexions Rails à la base de données, maintenu au-dessus de la concurrence pour éviter les dépassements de délai du pool.
Le câblage de la base de données propre à chaque plateforme (socket, loopback ou adresse
IP privée) est géré par le point d'entrée (voir §4), en fonction de la valeur de
enable_cloudsql_volume sur la variante.
6. Comportement des sondes de santé
OpenProject exécute Rails 8, qui applique le Host Authorization par rapport à
OPENPROJECT_HOST__NAME et renvoie
400 Bad Request: Invalid host_name configuration à toute requête dont l'en-tête Host
ne correspond pas — y compris les sondes de santé HTTP de la plateforme, qui se connectent
avec l'adresse IP du pod comme Host. Une sonde HTTP ne réussit donc jamais, même
lorsque Puma est sain et que le trafic réel des navigateurs (Host = le domaine du service)
fonctionne correctement. Les sondes sont donc configurées comme suit :
- Sonde de démarrage : TCP. Vérifie uniquement que Puma écoute sur le port, en
contournant le Host Authorization. Comme les migrations s'exécutent dans le job
db-migrate(et non au démarrage), le conteneur devient rapidement prêt sur son port. Notez que ce comportement TCP provient des valeurs par défaut de la variablestartup_probepropre àOpenProject_CloudRun/OpenProject_GKE— la valeur par défaut destartup_probedansOpenProject_Commonest une sonde HTTP sur/health_checks/default; les variantes de plateforme la remplacent. - Sonde de vivacité : TCP sur GKE (GKE prend en charge une sonde de vivacité TCP,
si bien qu'un Puma sain reste actif) ; désactivée sur Cloud Run (Cloud Run ne prend
en charge que des sondes de vivacité HTTP/gRPC, et une sonde HTTP ferait redémarrer en
boucle un conteneur sain). Comme pour la sonde de démarrage, ce remplacement a lieu dans
la variante de plateforme, et non dans
Common. - Sonde de disponibilité (readiness) : non évaluée en pratique. Le
main.tfd'OpenProject_Commonconstruit un objetreadiness_probepointant versGET /health_checks/default, maisreadiness_proben'est pas un champ d'entrée reconnu parApp_CloudRunni parApp_GKE(seulsstartup_probe,liveness_probe,health_check_configetuptime_check_configle sont) — c'est une configuration morte, sans effet. Les variableshealth_check_config/uptime_check_configréellement utilisées par les variantes CloudRun/GKE ont unpathpar défaut égal à/, et non/health_checks/default.
7. Stockage des pièces jointes
Par défaut, OpenProject stocke les pièces jointes des lots de travaux sur le système de
fichiers local, qui est éphémère sur Cloud Run. Pour des pièces jointes durables, les
deux variantes utilisent par défaut enable_nfs = true et montent Cloud Filestore sur
/opt/openproject/storage (OPENPROJECT_ATTACHMENTS__STORAGE__PATH). Vous pouvez aussi
configurer fog/S3 vers un point de terminaison compatible GCS via les variables
d'environnement OPENPROJECT_FOG_*. Cette couche ne crée aucun bucket de données dédié.
Pour la configuration d'OpenProject propre à l'utilisateur (variables par groupe, sorties et manière d'explorer chaque service depuis la console et la CLI), consultez les guides de plateforme : OpenProject_GKE et OpenProject_CloudRun.
Guides associés
- OpenProject sur Google Cloud Run — cette configuration déployée sur Cloud Run.
- OpenProject sur GKE Autopilot — cette configuration déployée sur GKE.
Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.