Configuración de despliegue de Discourse con opiniones de MKJ

Michael,

Ahora que lo he usado, estoy de acuerdo en que restic es “el mejor” para las copias de seguridad, pero me pregunto si, dada la reciente controversia con MinIO, has utilizado una opción compatible como Garage en su lugar. ¿O es la respuesta “no importa, todos usan los mismos comandos, así que puedes usar el que prefieras”? Son herramientas que apenas estoy empezando a aprender, así que no conozco las diferencias detalladas, solo que la recomendación actual es usar Garage en lugar de MinIO.

1 me gusta

Oh, sí, estoy seguro de que hoy empezaría en un garaje. Simplemente aún no he migrado.

1 me gusta

También gracias a @raykholo por señalarme fuera de banda que mis instrucciones para desactivar las páginas enormes transparentes estaban erróneas.

Si alguien está siguiendo esto, utilicen esto en su lugar:

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

Estas no son configurables mediante sysctl, y si previamente creaste /etc/sysctl.d/10-huge-pages.conf siguiendo mis instrucciones anteriores, puedes eliminarlo.

No validé correctamente esto cuando lo escribí, y he fallado en notar desde entonces que no funcionaba correctamente. Buen hallazgo, ¡y gracias!

3 Me gusta

Mmm. Tengo dos sistemas, los he revisado y podría hipótesis que para Ubuntu 22 tienes razón y tu corrección ha sido útil, pero para Ubuntu 24 no es necesaria. Al escribir esto, me doy cuenta de que tu corrección también podría ser efectiva en Ubuntu 24, aunque no sea necesaria. ¿Con qué versión del sistema operativo estás trabajando?

Se trata de la configuración predeterminada. Es seguro desactivarla si ya está desactivada (no tiene efecto) y el método descrito ahora es ampliamente compatible.

Estoy utilizando AlmaLinux, como se indica al principio. Una vez que Docker por fin ofreció compatibilidad con espacios de nombres v2, aproveché la primera oportunidad para migrar a un sistema operativo derivado de Red Hat.

1 me gusta

Tal vez respondiendo a mi propia pregunta, aquí hay algunas notas de intentar usar Garage con tu guía de implementación:


1. Garage no admite “etiquetas de permisos” en archivos (esta es la verdadera limitación de Garage)

Amazon te permite etiquetar cada archivo como “público” o “privado” de forma individual. Garage no implementa eso.

Discourse intenta usarlo por defecto. La parte engañosa: cuando Discourse decía “guarda este archivo, márcalo como público”, Garage aceptaba la solicitud e ignoraba silenciosamente la etiqueta. Así que las cargas básicas parecían funcionar bien, y solo se habría roto mucho después, la primera vez que alguien hiciera una carga privada.

Solución: decirle a Discourse que deje de usar etiquetas (un solo ajuste). El R2 de Cloudflare tiene la misma limitación y recibe la misma solución. Garage maneja los permisos a su manera, a nivel de cubo (bucket), que es todo lo que necesitamos.


2. Discourse insiste en nombrar los cubos de una manera específica (no es un defecto de Garage)

Discourse no dirá “el servidor en garage, cubo uploads”. Insiste en uploads.garage — el nombre del cubo pegado al frente, como un subdominio. Y no hay opción para desactivar eso.

Nada en nuestra red conocía ese nombre, así que Discourse no podía conectarse en absoluto — la instalación murió a mitad de camino.

Solución: di a Garage ese estilo de nomenclatura y registré los nombres. Dos líneas de configuración.

Este es un quirk conocido de Discourse, no de Garage — es por eso que el almacenamiento de Oracle está en la lista oficial de “no funcionará” de Discourse.


3. Las imágenes necesitaban una dirección web pública (nada que ver con Garage)

Discourse escribe la dirección de cada imagen en la publicación permanentemente. Sin decirle la dirección pública de antemano, guardó direcciones internas que solo nuestro servidor puede alcanzar — así que cada imagen estaría rota para los visitantes, para siempre, a menos que reconstruyéramos cada publicación.

Solución: establecer la dirección del CDN antes de la primera carga. Habría sido idéntico en Amazon, R2, cualquier cosa.


Nota: cada uno de estos fue silencioso. Nada dijo “no compatible”. Uno aceptó la solicitud e la ignoró, otro pareció un error de red, otro pareció funcionar bien.

Así que, solo algunas cosas iniciales a tener en cuenta para un nuevo usuario que intenta esto por primera vez, y/o alguien que intenta seguir la guía de Michael con Garage.

Aquí están las notas completas de IA de lo que experimentamos ahora que el foro ha sido desplegado:


Ejecutando Discourse en Garage autoalojado (S3) detrás de un túnel de Cloudflare — notas

Garage no está en la tabla de compatibilidad “Configurar un proveedor de almacenamiento de objetos compatible con S3”, así que aquí hay un punto de datos. Configuración: Garage v2.3.0, Discourse de dos contenedores, Debian 13,
nodo único, túnel de Cloudflare en lugar de un nginx externo. Advertencia de antemano: esta es una
pequeña implementación sin tráfico de producción aún, así que trátala como “funciona” y no como “probada
a escala de Maker Forums”.

  1. Funciona. Verificado contra las propias rutas de código de Discourse, no una CLI: UploadCreator,
    OptimizedImage, ListObjectsV2, HeadObject/GetObject con viaje de ida y vuelta byte-exacto,
    multipart, delete, remove_upload, más BackupRestore::Backuper escribiendo en el cubo de copias de seguridad
    y BackupStore#files / #download_file leyéndolo de vuelta. La configuración de ciclo de vida
    funciona, así que s3_configure_tombstone_policy realmente tiene efecto en lugar de ser
    ignorada silenciosamente. PutBucketCors funciona, así que el CORS manual está bien.

  2. Debes configurar el direccionamiento estilo host virtual, y romperá db:migrate si no lo haces.
    Discourse convierte el endpoint http://garage:3900 + cubo “uploads” en el host
    uploads.garage:3900, y no hay opción de estilo de ruta en ningún lugar de s3_helper.rb o
    site_settings.yml. Se requieren dos mitades: root_domain bajo [s3_api] en garage.toml,
    Y un nombre DNS resoluble por cubo (un alias de red Docker por cubo, ya que el DNS de Docker
    no tiene comodines). El síntoma si lo omites es un error Aws::Waiters sobre
    getaddrinfo durante SiteIconManager.ensure_optimized! — parece un fallo de red, no
    uno de almacenamiento. Cada nuevo cubo necesita un nuevo nombre.

  3. Garage no implementa PutObjectAcl / GetObjectAcl, así que establece s3_use_acls en falso —
    igual que R2. Dos trampas aquí. Primero, PutObject con --acl public-read es silenciosamente
    ACEPTADO y el encabezado ignorado, así que las pruebas de carga básicas pasan y solo se rompe después
    en una transición de carga segura. Segundo, DISCOURSE_S3_USE_ACLS no es una variable global oculta —
    ponerla en app.yml no hace nada. Apareció como verdadero en mi primer arranque a pesar de estar en
    el entorno. Tiene que ser una configuración del sitio, y necesita ser revisada después de cada reconstrucción
    porque nada te advierte.

  4. Si pones un CDN al frente, apúntalo al endpoint WEB de Garage (:3902), no al API S3
    (:3900). Las lecturas anónimas solo funcionan en el endpoint web; el API S3 correctamente devuelve 403
    a solicitudes no autenticadas, así que un CDN apuntando a :3900 falla en cada imagen mientras tus
    credenciales son perfectamente válidas. También necesitas garage bucket website --allow en el
    cubo de cargas, y un root_domain bajo [s3_web]. Actívalo solo en cargas — el
    cubo de copias de seguridad nunca debe ser legible anónimamente.

  5. ListObjectVersions devuelve NotImplemented en Garage. No importa: buscar
    s3_helper.rb y file_store/s3_store.rb para list_object_versions / object_versions
    devuelve cero coincidencias. Discourse no usa versionado de objetos S3; la expiración de marcadores de posición es
    reglas de ciclo de vida más un prefijo.

  6. Probar con aws-cli o mc NO prueba compatibilidad. Ambos usan por defecto
    direccionamiento estilo ruta contra un endpoint personalizado; Discourse solo hace estilo host virtual. Mi paso de CLI
    volvió 15/16 y pareció una luz verde, luego la instalación real falló
    inmediatamente en la diferencia de direccionamiento. Mismo almacén, mismas credenciales, mismas
    operaciones. Si estás evaluando un backend S3 no probado, ejecuta Discourse real — una
    instancia de contenedor único desechable es suficiente, y hacerlo antes de que exista cualquier contenido
    es todo el punto, ya que S3 → local es una puerta de un solo sentido.

  7. En un túnel de Cloudflare específicamente: la sección external-nginx-for-SSL no aplica
    (TLS termina en el borde, no hay certbot, no hay 80/443 entrante), pero la configuración de salida real-IP
    sigue siendo esencial y argumentablemente más aún. Usa real_ip_header CF-Connecting-IP
    en lugar de X-Forwarded-For — cuando el túnel es la única ruta de entrada es un único
    valor de conjunto de borde inequívoco. Verifícalo publicando desde una IP pública conocida y revisando
    lo que registró nginx; sin esto Discourse registra la dirección Docker del conector para
    cada solicitud y limita la tasa de toda internet como un solo cliente. Lo que pierdes versus
    el enfoque external-nginx es la página de mantenimiento durante las reconstrucciones — cloudflared
    no puede servir una.

  8. Corrección menor a algo ampliamente (mal)declarado, incluido por mí: DISCOURSE_S3_CDN_URL
    no está horneado irreversible en URLs almacenadas. upload.url sí almacena la URL S3 cruda con
    el host interno, pero Discourse sustituye el host CDN en tiempo de renderizado vía
    Discourse.store.cdn_url / UrlHelper.cook_url. Establecerlo tarde es recuperable — el
    costo es rake posts:rebake, porque posts.cooked almacena en caché el HTML desde el tiempo de horneado. Aún así
    establécelo antes de la primera carga; solo no entres en pánico si no lo hiciste.

  9. Dos notas operativas no relacionadas con Garage. ./launcher eco la línea completa docker run
    incluyendo DISCOURSE_S3_SECRET_ACCESS_KEY en texto plano, así que los registros de bootstrap son
    portadores de secretos — vale la pena rotar claves después de una instalación ruidosa. Y un Garage de nodo único
    aún requiere layout assign + layout apply antes de que almacene algo, con
    replication_factor = 1 significando ninguna replicación en absoluto, así que tu trabajo de copia de seguridad es la única
    durabilidad que tienes.

No utilizo un almacén de objetos para el acceso público en Discourse, en parte porque la migración es de vía única y también porque no lo necesito; en mi caso, serviría esos datos desde el mismo almacenamiento, solo con otra VM para gestionarlo. Utilizo un almacén de objetos únicamente como destino de respaldo de restic, realizado fuera de Discourse.

Las copias de seguridad de Discourse con miniaturas pero sin imágenes, combinadas con copias de seguridad a nivel de host de las cargas, respaldadas con restic en el host, significa que esas limitaciones son, por lo que sé, irrelevantes.