Aller au contenu principal

Grocy sur Google Cloud Run

Grocy sur Google Cloud Run

Grocy est un ERP domestique et de gestion des courses auto-hébergé : suivi des stocks avec lecture de codes-barres, gestion des corvées et des tâches, listes de courses et planification des repas. Il se distingue du module Mealie de ce catalogue, qui couvre uniquement les recettes et la planification des repas — Grocy est l'outil d'ERP domestique plus large. Ce module déploie Grocy sur Cloud Run v2 en s'appuyant sur le socle App_CloudRun, qui provisionne et gère l'infrastructure Google Cloud partagée ; Grocy_CloudRun est une fine surcouche qui fournit la configuration propre à Grocy (image, port, sondes, câblage du stockage) et transmet tout le reste tel quel.

Ce guide se concentre sur les services cloud utilisés par Grocy et sur la manière de les explorer et de les exploiter depuis la console Google Cloud et la ligne de commande. Pour les mécanismes communs à toutes les applications Cloud Run — identité du service, entrée et équilibrage de charge, mise à l'échelle et concurrence, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC Service Controls, sauvegardes et cycle de vie du déploiement — reportez-vous au guide du socle App_CloudRun plutôt que de les répéter ici.


1. Vue d'ensemble​

Grocy s'exécute sous forme d'un conteneur nginx + php-fpm (l'image grocy amont de LinuxServer.io, non modifiée) sur Cloud Run v2. Le déploiement assemble un ensemble restreint de services Google Cloud :

FonctionnalitéService Google CloudRemarques
CalculCloud Run v21 vCPU / 1 GiB par défaut, min=max=1 (pas d'autoscaling — SQLite à écrivain unique)
Base de donnéesAucuneGrocy utilise une base de données SQLite embarquée — aucune instance Cloud SQL n'est créée
État persistantCloud Filestore (NFS)/config (base de données SQLite, configuration, téléversements, sauvegardes) est monté via NFS, pas via GCS FUSE — voir §4
Stockage d'objetsCloud StorageUn bucket storage est provisionné mais inutilisé par défaut (voir §4)
SecretsSecret ManagerAucun n'est généré pour Grocy — il n'existe pas d'identifiant administrateur injectable
EntréeURL Cloud Run / Cloud Load BalancingURL run.app par défaut ; équilibreur de charge HTTPS externe et domaine personnalisé en option

Valeurs par défaut judicieuses à connaître d'emblée :

  • SQLite est la seule base de données prise en charge par Grocy. Confirmé par la lecture du code source amont de Grocy (services/DatabaseService.php) — il n'existe aucun embranchement vers un pilote MySQL/Postgres. database_type est fixé à NONE.
  • /config est persisté via NFS, pas via GCS FUSE — un correctif délibéré, et non la valeur par défaut habituelle du catalogue. Grocy écrit dans data/grocy.db-journal toutes les 1 à 2 secondes ; la couche de traduction vers le stockage d'objets de GCS FUSE ne peut pas soutenir ce schéma d'écriture. Un /config reposant sur GCS FUSE a tourné en boucle de plantages en production (confirmé en conditions réelles sur 12 cycles de démarrage / plus de 20 minutes — BufferedWriteHandler.OutOfOrderError répétés, limitation de débit HTTP 429, erreurs de descripteur de fichier obsolète). Grocy_CloudRun définit à la place enable_nfs = true, nfs_mount_path = "/config". Voir §4 pour l'histoire complète.
  • Il s'agit d'une classe de bug réellement différente de l'incident SQLite-sur-NFS d'UptimeKuma. Le problème d'UptimeKuma était une incompatibilité des verrous en mode WAL. Grocy n'active jamais le mode WAL (aucun PRAGMA journal_mode nulle part dans son code source) — son problème tient à la fréquence d'écriture, pas à la sémantique des verrous. Ensemble, ces deux cas montrent que « gcsfuse casse SQLite » est une leçon plus large que le seul verrouillage WAL.
  • Instance unique uniquement. min_instance_count = 1, max_instance_count = 1. La base de données SQLite de Grocy est à écrivain unique, sans prise en charge du clustering — exécuter plusieurs réplicas sur le même volume n'est pas sûr.
  • Aucun identifiant administrateur injectable. L'image amont est livrée avec les identifiants par défaut admin / admin, modifiés via l'interface web à la première connexion. Aucun secret Secret Manager n'est créé pour Grocy.
  • Les sondes de santé ciblent /, pas /health. Grocy n'a pas de point de terminaison de santé dédié ; la page de connexion (200, sans authentification) sert à la fois pour la sonde de démarrage et pour la sonde de vivacité (liveness).

2. Services Google Cloud et comment les explorer​

Toutes les commandes supposent que PROJECT et REGION sont définis. Les noms de services et de ressources sont indiqués dans les Sorties du déploiement.

A. Cloud Run — le service Grocy​

Grocy s'exécute sous forme d'un service Cloud Run v2 à instance unique. Chaque déploiement crée une révision immuable ; le trafic peut être réparti entre les révisions pour des déploiements progressifs sûrs.

  • Console : Cloud Run → sélectionnez le service pour les révisions, le trafic, les journaux et les métriques.
  • CLI :
    gcloud run services list --project "$PROJECT" --region "$REGION"
    gcloud run services describe <service-name> --project "$PROJECT" --region "$REGION"
    gcloud run revisions list --service <service-name> --project "$PROJECT" --region "$REGION"

Consultez App_CloudRun pour la mise à l'échelle, la concurrence, l'environnement d'exécution et la répartition du trafic.

B. Cloud Filestore (NFS) — le volume /config​

Tout l'état de Grocy — la base de données SQLite embarquée (grocy.db), config.php, les images et pièces jointes téléversées et les sauvegardes — se trouve sous /config, monté sur une instance Cloud Filestore (NFS) plutôt que via GCS FUSE. C'est la décision de stockage déterminante de ce module (voir §4) ; perdre ou mal configurer ce montage fait perdre toutes les données de Grocy.

  • Console : Filestore → Instances.
  • CLI :
    gcloud filestore instances list --project "$PROJECT" --zone "$REGION-a"
    gcloud filestore instances describe <instance-name> --project "$PROJECT" --zone "$REGION-a"

Consultez App_CloudRun pour la découverte automatique et le provisionnement NFS.

C. Cloud Storage​

Un bucket Cloud Storage storage dédié est provisionné automatiquement, mais par défaut il n'est pas utilisé pour adosser /config — ce montage passe par NFS à la place (voir §4). Il reste disponible pour tout gcs_volumes personnalisé qu'un opérateur ajoute.

  • Console : Cloud Storage → Buckets.
  • CLI :
    gcloud storage buckets list --project "$PROJECT"

Consultez App_CloudRun pour les options GCS Fuse et CMEK.

D. Réseau et entrée​

Le service est joignable par défaut à son URL run.app. Un équilibreur de charge HTTPS externe avec domaine personnalisé, Cloud CDN et Cloud Armor peut y être ajouté ; les paramètres d'entrée et la sortie VPC contrôlent la connectivité.

  • Console : Cloud Run (URL du service) ; Network services → Load balancing.
  • CLI :
    gcloud run services describe <service-name> --region "$REGION" --format='value(status.url)'
    gcloud compute addresses list --project "$PROJECT"

Consultez App_CloudRun.

E. Cloud Logging et Monitoring​

Les journaux des conteneurs sont envoyés à Cloud Logging ; les métriques Cloud Run à Cloud Monitoring, avec en option des tests de disponibilité et des règles d'alerte.

  • Console : Logging → Logs Explorer ; Monitoring → Dashboards / Alerting.
  • CLI :
    gcloud run services logs read <service-name> --project "$PROJECT" --region "$REGION" --limit 50

3. Comportement de l'application Grocy​

  • Aucune initialisation de base de données au premier déploiement. Grocy n'a ni base de données externe ni job db-init — il crée et migre son propre schéma SQLite embarqué sous /config au premier démarrage.
  • La durabilité de /config dépend du montage NFS, pas d'une base de données gérée. Comme tout l'état de Grocy (base de données, configuration, téléversements, sauvegardes) réside dans des fichiers sur /config, la fiabilité du montage NFS est la fiabilité du déploiement. Vérifiez que le montage est sain avant de vous fier aux données qui y sont écrites.
  • Aucun identifiant administrateur n'est généré ni injectable. L'image amont est livrée avec les identifiants par défaut admin / admin. Connectez-vous avec ceux-ci au premier accès et changez immédiatement le mot de passe via Users → admin → Edit dans l'interface de Grocy — aucune variable d'environnement ni valeur Secret Manager ne le définit à votre place.
  • Chemin de santé. Les sondes de démarrage et de disponibilité envoient toutes deux une requête HTTP GET /, qui renvoie la page de connexion de Grocy (200) sans authentification. Ce n'est pas un point de terminaison de santé dédié — Grocy n'en a pas — mais cela indique de façon fiable que la pile nginx + php-fpm répond.
  • La contrainte d'écrivain unique est architecturale, pas un réglage de mise à l'échelle. Comme la base de données SQLite de Grocy n'a pas d'équivalent MySQL/Postgres ni de prise en charge du clustering, max_instance_count doit rester à 1. Aucune configuration ne permet d'activer sans risque la mise à l'échelle horizontale pour ce module.
  • Vérifié en conditions réelles. https://grocycr31ffe08b-kj6qcu2rxa-uc.a.run.app — 16 échantillons curl sur environ 5 minutes contre une seule révision stable (grocycr31ffe08b-00003-h4w), servant systématiquement <title>Login | Grocy</title>, sans aucune entrée de journal présentant une signature de corruption (OutOfOrderError / 429 / stale file / database is locked / disk I/O error tous absents) après le correctif NFS décrit au §4.

4. Pourquoi /config utilise NFS plutôt que GCS FUSE — l'histoire réelle​

Le câblage du stockage persistant de ce module n'est pas la valeur par défaut habituelle de ce catalogue, et la raison mérite d'être comprise avant de le modifier.

Grocy écrit dans data/grocy.db-journal à chaque transaction de base de données — confirmé en conditions réelles, environ toutes les 1 à 2 secondes en usage léger. La couche de traduction vers le stockage d'objets de GCS FUSE repose sur une cohérence à terme et une sémantique d'objet entier ; elle ne soutient pas ce type d'écritures petites et très fréquentes. Grocy_CloudRun montait à l'origine /config sur un bucket adossé à GCS FUSE — le modèle habituel de ce catalogue pour la configuration persistante des applications — et il a tourné en boucle de plantages en production. Confirmé en conditions réelles sur 12 cycles de démarrage complets en plus de 20 minutes, sans que les vrais contrôles HTTP ne renvoient une seule fois une page fonctionnelle :

  • BufferedWriteHandler.OutOfOrderError répétés
  • Réponses HTTP 429 de limitation de débit de la part de GCS
  • Erreurs de descripteur de fichier obsolète
  • Une boucle permanente de plantage et redémarrage

Le correctif : faire passer /config de GCS FUSE à la prise en charge native des volumes NFS du socle — enable_nfs = true, nfs_mount_path = "/config" — avec enable_gcs_storage_volume de Grocy_Common défini à false pour éviter un double montage au même chemin. NFS implémente une vraie sémantique de fichiers POSIX (rename/fsync/verrous consultatifs) que la couche de traduction de GCS FUSE n'offre pas, et il soutient sans difficulté le schéma d'écriture de Grocy.

Pourquoi il s'agit d'un bug réellement différent de l'autre incident SQLite-sur-stockage-partagé du catalogue (UptimeKuma) : le problème d'UptimeKuma était une incompatibilité des verrous en mode WAL avec NFS. Grocy n'active jamais le mode WAL — il n'existe aucun PRAGMA journal_mode dans son code source (vérifié par rapport à DatabaseService.php en amont) ; il utilise le mode de journal par défaut de SQLite, DELETE/rollback-journal. L'échec de Grocy sur GCS FUSE n'avait donc rien à voir avec le verrouillage — c'était purement un problème de fréquence d'écriture que la sémantique de stockage d'objets de GCS FUSE ne peut pas absorber. Lus ensemble, ces cas élargissent la leçon générale : gcsfuse peut casser une base de données SQLite embarquée pour plus d'une raison — non seulement le verrouillage en mode WAL, mais aussi les schémas d'écriture très fréquents en général, indépendamment du mode de journal.

Un second bug, sans rapport, a été corrigé au passage lors du build de ce module : une surcharge parasite container_image_source = "prebuilt" dans deploy.tfvars contournait silencieusement l'intégralité du build du Dockerfile personnalisé — la même classe de piège déjà documentée dans ce catalogue pour UptimeKuma.

La variante GKE adopte une approche différente du même problème de fond. Grocy_GKE est échafaudé mais n'a pas encore été déployé ni vérifié. Son correctif prévu est un PVC en mode bloc de StatefulSet sur /config — un véritable stockage en mode bloc, et non un système de fichiers réseau — conformément au modèle général de ce catalogue pour les variantes GKE d'applications fortement dépendantes de SQLite (p. ex. CalibreWeb_GKE). Ce guide sera mis à jour une fois cette variante déployée et vérifiée.


5. Variables de configuration​

Les variables sont regroupées exactement comme elles apparaissent sur la plateforme de déploiement. Seuls les paramètres propres à Grocy ou notables pour lui sont listés ; toutes les autres entrées sont héritées d'App_CloudRun avec leur comportement standard.

Groupe 3 — Identité de l'application​

VariableValeur par défautDescription
application_namegrocyNom de base des ressources. Ne pas modifier après le premier déploiement.
application_display_nameGrocyNom lisible affiché dans la console.
application_versionlatestTag de l'image LinuxServer. "latest" se résout en v4.6.0-ls333 épinglé (l'ARG de build GROCY_VERSION, et non l'APP_VERSION générique).

Groupe 4 — Exécution et mise à l'échelle​

VariableValeur par défautDescription
cpu_limit / memory_limit1000m / 1GiGrocy est léger ; la mémoire par défaut laisse de la marge pour les téléversements d'images et de pièces jointes.
min_instance_count1Maintient l'instance active — évite les démarrages à froid.
max_instance_count1Doit rester à 1. La base de données SQLite de Grocy est à écrivain unique, sans prise en charge du clustering.
container_port80Port HTTP par défaut de Grocy.
container_protocolhttp1Grocy sert du HTTP/1.1 simple ; h2c n'est pas nécessaire.
execution_environmentgen2Requis pour le montage NFS.
enable_cloudsql_volumefalseGrocy n'a pas de Cloud SQL — conservez false.

Groupe 6 — Variables d'environnement et secrets​

VariableValeur par défautDescription
environment_variables{}Fusionnée avec les valeurs par défaut de Grocy_Common : PUID=1000, PGID=1000, TZ=Etc/UTC.
secret_environment_variables{}Aucun secret par défaut n'existe pour Grocy — cette map est entièrement fournie par l'opérateur.

Groupe 11 — Cloud Storage et système de fichiers​

VariableValeur par défautDescription
enable_nfstrueVaut true par défaut pour ce module — requis pour que /config puisse soutenir le schéma d'écriture SQLite de Grocy (voir §4). Ne le désactivez pas, sauf pour le remplacer par un montage tout aussi durable et conforme à POSIX.
nfs_mount_path/configGrocy conserve tout son état (base de données, configuration, téléversements, sauvegardes) ici. Ne le modifiez pas, sauf si le chemin de données de l'image amont change.
create_cloud_storagetrueLe bucket storage est tout de même créé (inutilisé pour /config par défaut ; disponible pour des gcs_volumes personnalisés).

Groupe 12 — Backend de base de données​

VariableValeur par défautDescription
database_typeNONEFixe — Grocy n'a aucune base de données SQL.

Groupe 13 — Jobs et tâches planifiées​

VariableValeur par défautDescription
initialization_jobs[]Aucun job d'initialisation par défaut — Grocy initialise son propre schéma SQLite au premier démarrage.

Groupe 14 — Observabilité et santé​

VariableValeur par défautDescription
startup_probeHTTP / , délai de 15 s, 10 tentativesPage de connexion de Grocy — il n'existe pas de point de terminaison de santé dédié.
liveness_probeHTTP / , délai de 30 s, 3 tentativesMême point de terminaison que la sonde de démarrage.

Toutes les autres entrées sont héritées d'App_CloudRun avec leur comportement standard.


6. Sorties​

SortieDescription
service_nameNom du service Cloud Run.
grocy_urlURL de l'interface de Grocy (port 80). Joignable uniquement depuis le VPC lorsque ingress_settings = "internal".
service_locationRégion dans laquelle s'exécute le service.
stage_servicesDétails des services par étape (Cloud Deploy).
load_balancer_ip / load_balancer_urlIP / URL de l'équilibreur de charge HTTPS externe (si activé).
storage_bucketsBuckets Cloud Storage créés (le bucket storage, inutilisé pour /config par défaut).
network_name / network_exists / regionsRéseau VPC, présence, régions.
container_image / container_registryImage déployée et dépôt Artifact Registry.
monitoring_enabled / monitoring_notification_channels / uptime_check_namesÉtat de la surveillance, canaux, tests de disponibilité.
deployment_id / tenant_id / resource_prefixIdentifiants de nommage.
project_id / project_numberIdentifiants du projet.
initialization_jobsNoms des jobs d'initialisation créés (vide par défaut).
cicd_enabled / github_repository_url / github_repository_owner / github_repository_name / cicd_configurationÉtat et détails du CI/CD.
artifact_registry_repository / cloudbuild_trigger_name / cloudbuild_trigger_idRegistre et déclencheur de build.
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_modeÉtat de VPC-SC.
audit_logging_enabled / artifact_registry_cmek_enabledÉtat des journaux d'audit et de CMEK.

7. Pièges de configuration et valeurs par défaut judicieuses​

Risque : Critique (perte de données / panne / sécurité) — Élevé (service dégradé) — Moyen (coût ou dégradation partielle) — Faible (mineur).

Validation au moment du plan héritée. Ce module fait passer sa configuration par le moteur du socle App_CloudRun, qui valide les valeurs et leurs combinaisons au moment du plan. La plupart des entrées hors limites ou contradictoires sont détectées avant la création de toute ressource.

ParamètreValeur judicieuseRisqueConséquence en cas d'erreur
enable_nfstrueCritiqueLe désactiver sans montage POSIX tout aussi durable ramène /config sur GCS FUSE (ou sur un chemin éphémère dans le conteneur), ce qui reproduit la boucle de plantage et redémarrage confirmée (BufferedWriteHandler.OutOfOrderError, des 429, erreurs de descripteur de fichier obsolète) — ou fait perdre silencieusement tout l'état à chaque redémarrage.
nfs_mount_path/configCritiqueGrocy code en dur son chemin de données sur /config. Modifier le chemin de montage sans modification correspondante de l'image fait perdre l'accès à la base de données, à la configuration et aux téléversements.
max_instance_count1CritiqueLa base de données SQLite de Grocy est à écrivain unique, sans prise en charge du clustering. Toute valeur supérieure à 1 expose à une corruption de la base par des écrivains concurrents.
container_image_sourcecustom (la valeur par défaut du module)ÉlevéUne surcharge parasite "prebuilt" contourne silencieusement le build du Dockerfile personnalisé (déjà rencontré une fois lors du build de ce module, et documenté pour UptimeKuma) — Cloud Run pointe alors vers un chemin Artifact Registry jamais construit.
database_typeNONEMoyenGrocy l'ignore totalement (aucun chemin de code ne le lit), mais toute autre valeur provisionne une instance Cloud SQL inutilisée et facturée.
Mot de passe administrateurÀ changer à la première connexionÉlevéLes identifiants par défaut admin / admin de l'image amont sont documentés publiquement ; les laisser inchangés sur un déploiement public ingress_settings = "all" constitue une réelle exposition.
min_instance_count1FaibleLe définir à 0 réduit les coûts mais réintroduit des démarrages à froid sur la pile nginx + php-fpm de Grocy.

Pour le comportement du socle mentionné tout au long de ce guide — identité du service, mise à l'échelle et concurrence, entrée et équilibrage de charge, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC-SC, sauvegardes et mise en miroir des images — consultez App_CloudRun. La configuration applicative propre à Grocy est décrite dans Grocy_Common.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.