MKJs Konfiguration für die Deployment-Strategie von Discourse

Michael,

Nachdem ich es jetzt ausprobiert habe, stimme ich zu, dass restic für Backups „das Beste“ ist. Aber ich frage mich, ob du angesichts der jüngsten Minio-Kontroverse eine kompatible Alternative wie Garage stattdessen genutzt hast? Oder lautet die Antwort: „Es ist egal, sie alle verwenden dieselben Befehle, also kannst du einfach das nehmen, was dir gefällt“? Das sind Werkzeuge, die ich gerade erst lernen werde, also kenne ich die detaillierten Unterschiede nicht, nur dass die aktuelle Empfehlung lautet, Garage statt Minio zu verwenden.

1 „Gefällt mir“

Oh ja, ich bin zuversichtlich, dass ich heute mit dem Garage-Ansatz beginnen würde. Ich habe es nur noch nicht migriert.

1 „Gefällt mir“

Außerdem danke ich @raykholo, der mich außerhalb des Threads darauf hingewiesen hat, dass meine Anweisungen zum Deaktivieren von transparenten Huge Pages stillschweigend falsch waren.

Wer dies verfolgt, sollte stattdessen Folgendes verwenden:

echo 'w /sys/kernel/mm/transparent_hugepage/enabled - - - - never
w /sys/kernel/mm/transparent_hugepage/defrag  - - - - never' > /etc/tmpfiles.d/thp.conf
systemd-tmpfiles --create

Diese Einstellungen können nicht über sysctl konfiguriert werden. Wenn du zuvor /etc/sysctl.d/10-huge-pages.conf erstellt hast, um meinen früheren Anweisungen zu folgen, kannst du diese Datei löschen.

Ich habe das bei der Erstellung nicht korrekt überprüft und bin seitdem nicht darauf aufmerksam geworden, dass es nicht richtig funktioniert. Gut aufgepasst, danke!

3 „Gefällt mir“

Hmm. Ich habe zwei Systeme, und ich habe sie gerade angesehen. Ich vermute, dass du für Ubuntu 22 recht hast und deine Korrektur geholfen hat, aber für Ubuntu 24 ist sie nicht nötig. Während ich das schreibe, fällt mir ein, dass deine Korrektur auch unter Ubuntu 24 wirksam sein könnte, auch wenn sie nicht erforderlich ist. Mit welcher Betriebssystemversion arbeitest du?

Es geht um die Standardkonfiguration. Es ist sicher, sie zu deaktivieren, wenn sie bereits deaktiviert ist (no-op), und die hier beschriebene Methode wird weitgehend unterstützt.

Ich verwende AlmaLinux, wie oben beschrieben. Sobald Docker endlich Unterstützung für v2-Namespaces bot, nutzte ich die erste Gelegenheit, zu einem Red-Hat-Derivat zu wechseln.

1 „Gefällt mir“

Vielleicht beantworte ich meine eigene Frage: Hier sind einige Notizen aus dem Versuch, Garage mit deinem Runbook zu verwenden:


1. Garage unterstützt keine „Berechtigungstags“ für Dateien (das ist die eigentliche Lücke bei Garage)

Amazon erlaubt es, jede Datei einzeln mit „öffentlich“ oder „privat“ zu versehen. Garage implementiert das nicht.

Discourse versucht standardmäßig, dies zu nutzen. Der heimtückische Teil: Als Discourse sagte „speichere diese Datei, markiere sie als öffentlich“, akzeptierte Garage die Anfrage und ignorierte den Tag leise. Grundlegende Uploads sahen also gut aus, und es wäre erst viel später kaputtgegangen, als jemand zum ersten Mal einen Upload privat machte.

Lösung: Sag Discourse, dass es mit den Tags aufhören soll (eine Einstellung). Cloudflares R2 hat die gleiche Lücke und wird mit der gleichen Lösung behoben. Garage handhabt Berechtigungen auf seine eigene Weise, auf Bucket-Ebene, was alles ist, was wir brauchen.


2. Discourse besteht darauf, Buckets auf eine bestimmte Weise zu benennen (kein Fehler von Garage)

Discourse wird nicht sagen „der Server bei garage, Bucket uploads“. Es besteht auf uploads.garage — der Bucket-Name vorne angeklebt, wie eine Subdomain. Und es gibt keine Option, das auszuschalten.

Nichts in unserem Netzwerk kannte diesen Namen, also konnte Discourse überhaupt keine Verbindung herstellen — die Installation brach中途 ab.

Lösung: Gab Garage diesen Namensstil und registrierte die Namen. Zwei Zeilen Konfiguration.

Dies ist eine bekannte Discourse-Sonderheit, keine von Garage — deshalb ist Oracles Speicher auf der offiziellen „funktioniert nicht“-Liste von Discourse.


3. Bilder brauchten eine öffentliche Webadresse (hat nichts mit Garage zu tun)

Discourse schreibt die Adresse jedes Bildes dauerhaft in den Beitrag. Ohne ihm die öffentliche Adresse im Voraus zu nennen, speicherte es nur interne Adressen, die nur unser Server erreichen kann — also wäre jedes Bild für Besucher für immer kaputt, es sei denn, wir würden jeden Beitrag neu aufbauen.

Lösung: Setze die CDN-Adresse vor dem ersten Upload. Wäre auf Amazon, R2, was auch immer, identisch gewesen.


Hinweis: Jeder dieser Punkte war still. Nichts sagte „nicht unterstützt“. Ein akzeptierte die Anfrage und ignorierte sie, einer sah wie ein Netzwerkfehler aus, einer sah so aus, als würde er gut funktionieren.

Also, nur einige erste Dinge, die ein neuer Benutzer im Hinterkopf behalten sollte, der dies zum ersten Mal versucht, und/oder jemand, der versucht, Michaels Runbook mit Garage zu befolgen.

Hier sind die vollständigen KI-Notizen aus dem, was wir erlebt haben, jetzt da das Forum bereitgestellt wurde:


Discourse auf selbst gehostetem Garage (S3) hinter einem Cloudflare-Tunnel ausführen — Notizen

Garage ist nicht in der Kompatibilitäts-Tabelle „Konfigurieren eines S3-kompatiblen Object-Speicher-Anbieters“ aufgeführt, also hier ein Datenpunkt. Einrichtung: Garage v2.3.0, zwei-Container-Discourse, Debian 13,
einzelner Knoten, Cloudflare-Tunnel statt eines externen nginx. Vorab-Hinweis: Dies ist eine
kleine Bereitstellung ohne Produktivverkehr, also behandle es als „funktioniert“ und nicht als
„kämpferisch getestet im Maßstab der Maker Foren“.

  1. Es funktioniert. Verifiziert gegen Discourses eigene Code-Pfade, nicht ein CLI: UploadCreator,
    OptimizedImage, ListObjectsV2, HeadObject/GetObject mit byte-exaktem Round-Trip,
    multipart, delete, remove_upload, plus BackupRestore::Backuper, das in den backups-
    Bucket schreibt, und BackupStore#files / #download_file, das es wieder liest. Lifecycle-Konfiguration
    funktioniert, also s3_configure_tombstone_policy wirkt tatsächlich, anstatt
    leise ignoriert zu werden. PutBucketCors funktioniert, also ist manuelles CORS in Ordnung.

  2. Du musst virtual-host-style-Adressierung konfigurieren, und es wird db:migrate kaputtgehen, wenn du
    das nicht tust. Discourse verwandelt Endpunkt http://garage:3900 + Bucket „uploads“ in den Host
    uploads.garage:3900, und es gibt keine path-style-Option irgendwo in s3_helper.rb oder
    site_settings.yml. Zwei Hälften sind erforderlich: root_domain unter [s3_api] in garage.toml,
    UND ein auflösbarer DNS-Name pro Bucket (ein Docker-Netzwerk-Alias pro Bucket, da Docker
    DNS keine Wildcards hat). Symptom, wenn du es verpasst, ist ein Aws::Waiters-Fehler bezüglich
    getaddrinfo während SiteIconManager.ensure_optimized! — liest sich wie ein Netzwerkfehler, nicht
    wie ein Speicherfehler. Jeder neue Bucket braucht einen neuen Namen.

  3. Garage implementiert PutObjectAcl / GetObjectAcl nicht, also setze s3_use_acls auf false —
    gleich wie R2. Zwei Fallen hier. Erstens, PutObject mit --acl public-read wird leise
    AKZEPTIERT und der Header ignoriert, also bestehen grundlegende Upload-Tests und es geht erst später bei
    einem secure-upload-Übergang kaputt. Zweitens, DISCOURSE_S3_USE_ACLS ist keine verschattete globale Variable —
    es in app.yml zu setzen, tut nichts. Es kam bei meinem ersten Boot als true hoch, obwohl es in
    der Umgebungsvariable war. Es muss eine site-setting sein, und es muss nach jedem Rebuild
    neu überprüft werden, weil nichts warnt.

  4. Wenn du ein CDN voranstellst, zeige es auf Garages WEB-Endpunkt (:3902), nicht den S3-API-
    Endpunkt (:3900). Anonyme Lesezugriffe funktionieren nur am Web-Endpunkt; die S3-API gibt korrekt 403 für
    nicht authentifizierte Anfragen zurück, also schlägt ein CDN, das auf :3900 zeigt, bei jedem Bild fehl, während deine
    Anmeldeinformationen perfekt gültig sind. Du brauchst auch garage bucket website --allow auf dem
    uploads-Bucket, und eine root_domain unter [s3_web]. Aktiviere es nur auf uploads — der
    backups-Bucket darf niemals anonym lesbar sein.

  5. ListObjectVersions gibt NotImplemented auf Garage zurück. Es ist egal: grep nach
    list_object_versions / object_versions in s3_helper.rb und file_store/s3_store.rb
    ergibt null Treffer. Discourse verwendet keine S3-Objektversionierung; Tombstone-Ablauf ist
    Lifecycle-Regeln plus ein Präfix.

  6. Testen mit aws-cli oder mc beweist NICHT Kompatibilität. Beide standardisieren auf path-style
    Adressierung gegen einen benutzerdefinierten Endpunkt; Discourse macht nur virtual-host-style. Mein CLI-
    Durchgang kam mit 15/16 zurück und sah wie ein grünes Licht aus, dann schlug die echte Installation
    sofort aufgrund des Adressierungsunterschiedes fehl. Gleicher Speicher, gleiche Anmeldeinformationen, gleiche
    Operationen. Wenn du einen unbewiesenen S3-Backend bewertest, treibe echtes Discourse — ein
    Wegwerf-Einzel-Container-Instanz reicht aus, und es vor jeglichem Inhalt zu tun, ist der ganze Punkt, da S3 → lokal eine Einbahnstraße ist.

  7. Bei einem Cloudflare-Tunnel speziell: Der external-nginx-for-SSL-Abschnitt gilt nicht
    (TLS terminiert am Edge, kein certbot, kein eingehender 80/443), aber die real-IP-Ausgangs-
    Konfiguration ist dennoch essentiell und arguably sogar mehr. Verwende real_ip_header CF-Connecting-IP
    statt X-Forwarded-For — wenn der Tunnel der einzige Ingress-Pfad ist, ist es ein einzelner
    unmissverständlicher Edge-Set-Wert. Verifiziere es, indem du von einer bekannten öffentlichen IP postest und prüfst,
    was nginx protokolliert hat; ohne es protokolliert Discourse die Docker-Adresse des Connectors für
    jede Anfrage und rate-limits das gesamte Internet als einen Client. Was du im Vergleich zum
    external-nginx-Ansatz verlierst, ist die Wartungsseite während Rebuilds — cloudflared
    kann keine bedienen.

  8. Kleine Korrektur zu etwas, das weit verbreitet (falsch) angegeben wird, einschließlich von mir: DISCOURSE_S3_CDN_URL
    ist nicht irreversibel in gespeicherte URLs eingearbeitet. upload.url speichert zwar die rohe S3-URL mit
    dem internen Host, aber Discourse ersetzt den CDN-Host zur Renderzeit via
    Discourse.store.cdn_url / UrlHelper.cook_url. Spät setzen ist wiederherstellbar — die
    Kosten sind rake posts:rebake, weil posts.cooked das HTML aus der cook-Zeit cached. Setze es trotzdem
    vor dem ersten Upload; panikere nur nicht, wenn du es nicht getan hast.

  9. Zwei operative Notizen, die nichts mit Garage zu tun haben. ./launcher echo-t die vollständige docker run-Zeile
    einschließlich DISCOURSE_S3_SECRET_ACCESS_KEY im Klartext, also sind Bootstrap-Logs
    geheimnisbehaftet — es lohnt sich, Schlüssel nach einer lauten Installation zu rotieren. Und ein einzelner-Knoten-Garage
    erfordert immer noch layout assign + layout apply, bevor es irgendetwas speichern wird, mit
    replication_factor = 1, was keine Replikation bedeutet, also ist dein Backup-Job die einzige
    Dauerhaftigkeit, die du hast.

Ich verwende in Discourse keinen Objektspeicher für den öffentlichen Zugriff, zum einen, weil die Migration ein Weg ohne Rückkehr ist, und auch, weil ich ihn nicht benötige; in meinem Fall würden diese Daten aus demselben Speicher bereitgestellt, nur mit einer weiteren VM zur Verwaltung. Ich verwende einen Objektspeicher lediglich als Restic-Backup-Ziel, das außerhalb von Discourse durchgeführt wird.

Discourse-Backups mit Thumbnails, aber ohne Bilder, kombiniert mit Host-Backups der Uploads, die mit Restic auf dem Host gesichert werden, bedeuten, dass diese Einschränkungen, soweit ich weiß, irrelevant sind.